LeanCloud Web hosting & Webpack & PostCSS & SUIT CSS

813 阅读1分钟
  • 修改 package.json

    "dependencies": {
        "babel-loader": "^5.0.0",
        "css-loader": "^0.12.0",
        "postcss-loader": "^0.4.2",
        "react": "^0.13.2",
        "style-loader": "^0.12.1",
        "cssnext": "^1.4.0",
        "suitcss": "latest",
        "suitcss-base": "latest",
        "suitcss-components-arrange": "latest",
        "suitcss-components-button": "latest",
        "suitcss-components-flex-embed": "latest",
        "suitcss-components-grid": "latest",
        "suitcss-utils-align": "latest",
        "suitcss-utils-display": "latest",
        "suitcss-utils-layout": "latest",
        "suitcss-utils-link": "latest",
        "suitcss-utils-offset": "latest",
        "suitcss-utils-position": "latest",
        "suitcss-utils-size": "latest",
        "suitcss-utils-text": "latest"
    }

    安装新添加的 loaders

    $ npm install
    
  • 修改 webpack.config.js 来使用 postcss

    const cssnext = require('cssnext');
    
    module.exports = {
      entry: './src/main.js',
      output: {
        path: './leancloud/public',
        filename: 'bundle.js'
      },
      resolve: {
        extensions: ['', '.jsx', '.js', '.css'],
        modulesDirectories: ["src", "node_modules"]
      },
      module: {
        loaders: [
          { test: /\.jsx?$/, loader: 'babel', exclude: /node_modules/ },
          { test: /\.css$/, loader: 'style!css!postcss' }
        ]
      },
      postcss: [
        cssnext({
          import: {
            path: ['node_modules', 'src/css']
          }
        })
      ]
    };
  • 添加 src/css/base.css

    @import "suitcss-base";
    @import "variables";
  • 添加 src/css/components/footer.css

    .tl-Footer {
      font-style: italic;
    }
  • 添加 src/css/components/container.css

    .tl-Container {
      background: var(--color-red);
    }
    
    @import "variables";
  • 添加 src/css/variables.css

    /**
     * Breakpoints
     */
    
    @custom-media --sm-viewport (min-width:320px);
    @custom-media --md-viewport (min-width:640px);
    @custom-media --lg-viewport (min-width:960px);
    
    /**
     * Colors
     */
    
    :root {
      --color-twitter-blue: #55acee;
      --color-white: #fff;
    
      /* Primary grays */
      --color-charcoal: #292f33;
      --color-dark-gray: #66757f;
      --color-medium-gray: #8899a6;
      --color-gray: #ccd6dd;
      --color-border-gray: #e1e8ed;
      --color-faint-gray: #f5f8fa;
    
      /* Primary blues */
      --color-dark-blue: #226699;
      --color-deep-blue: #3b88c3;
      --color-light-blue: #88c9f9;
    
      /* Secondary colors */
      --color-orange: #ffac33;
      --color-green: #77b255;
      --color-purple: #9266cc;
      --color-red: #dd2e44;
      --color-yellow: #ffcc4d;
    
      /* Secondary color variants */
      --color-dark-orange: #f4900c;
      --color-dark-green: #3e721d;
      --color-dark-purple: #553788;
      --color-dark-red: #a0041e;
      --color-deep-green: #5c913b;
      --color-deep-purple: #744eaa;
      --color-deep-red: #be1931;
      --color-light-yellow: #ffd983;
      --color-light-green: #a6d388;
      --color-light-purple: #aa8dd8;
      --color-light-red: #ea596e;
      --color-faded-yellow: #ffe8b6;
      --color-faded-green: #c6e5b3;
      --color-faded-blue: #bbddf5;
      --color-faded-purple: #cbb7ea;
      --color-faded-red: #f4abba;
    }
    
    /**
     * Fonts
     */
    
    :root {
      --font-size: 16px;
      --font-family: sans-serif;
      --line-height: 1.4;
    }
    
    /**
     * Spacing
     */
    
    :root {
      --space-small-px: 10px;
      --space-medium-px: 15px;
      --space-large-px: 20px;
    }
    

    这里都是用的 CSS4 的语法,主要用来改 SUIT CSS 的一些参数,和我们会用到的各个组件的参数

  • src/components 改为 src/js/components

  • 新建 src/js/components/Container.js

    const React = require("react");
    const Header = require("js/components/Header");
    const Footer = require("js/components/Footer");
    
    require("css/components/container");
    require("suitcss-utils-layout");
    
    const Container = React.createClass({
      render () {
        return (
          <div className="tl-Container u-cf">
            <Header />
            <Footer />
          </div>
        );
      }
    });
    
    module.exports = Container;
    

    现在和 commonJS 一样,每个 Component 都需要 require 对应的 css 文件。比如这里用了 tl-Containeru-cf 这两个 class,就需要分别 require 他们对应的文件。

  • 新建 src/js/components/Header.js

    const React = require("react");
    
    require("css/components/header");
    
    const Header = React.createClass({
      render () {
        return (
          <div>
            <div className="tl-Header">
              Header
            </div>
          </div>
        );
      }
    });
    
    module.exports = Header;
  • 新建 src/js/components/Footer.js

    const React = require("react");
    
    require("css/components/footer");
    
    const Footer = React.createClass({
      render () {
        return (
          <div className="footer">
            Footer
          </div>
        );
      }
    });
    
    module.exports = Footer;
  • 修改 src/main.js

    const React = require("react");
    const Container = require("js/components/Container");
    
    require("css/base");
    
    React.render(<Container />, document.getElementById("main"));
    
  • 再运行 webpack 编译后,在 localhost 里面就能看到新的带 css 的页面了

    screenshot

    如果你注意,你会发现 css 会自动加载到 header 里面,而不是单独的 css 文件。这就是 webpack 的一个独特的地方,他只会加载当前页面需要的 css 文件,如果你的 App 有很多页面,比如 admin 的话,在页面 A 就不会加载页面 B 的 style。

    当然这么做也会有一些弊端,比如如果 css 文件很大的话,那么在页面加载过程中会出现几秒没有美化的页面,直到整个 bundle.js 加载成功才会出现美化。

  • 单一 css 文件

    要解决这个问题,可以使用一个 webpack 的 plugin

    $ npm install extract-text-webpack-plugin --save
    

    修改 webpack.config.js

    const webpack = require('webpack');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    const cssnext = require('cssnext');
    
    const definePlugin = new webpack.DefinePlugin({
      __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'false'))
    });
    
    module.exports = {
      entry: './src/main.js',
      output: {
        path: './leancloud/public',
        filename: 'bundle.js'
      },
      resolve: {
        extensions: ['', '.jsx', '.js', '.css'],
        modulesDirectories: ["src", "node_modules"]
      },
      module: {
        loaders: [
          { test: /\.jsx?$/, loader: 'babel', exclude: /node_modules/ },
          { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css!postcss') }
        ]
      },
      postcss: [
        cssnext({
          import: {
            path: ['node_modules', 'src/css']
          }
        })
      ],
      plugins: [
        definePlugin,
        new ExtractTextPlugin("bundle.css")
      ]
    };

    再运行 webpack,你会发现 leancloud/public 里面,除了 bundle.js 之外,还多出了一个 bundle.css

    注意这里我还加了段 definePlugin,有什么用呢?如果在你的源码里面加入

    if (__DEV__) {
      console.log('Dev');
    }

    的话,那么这一段只会在本地运行,在 webpack -p 的时候这段代码会被自动移除掉

    现在在 index.ejs 里面的 header 里加上

    <link rel="stylesheet" href="bundle.css" />

    刷新浏览器,你会发现一样的结果,只不过现在的 style 都在 bundle.css 里面了

  • 部署到 production

    $ webpack -p
    $ cd leancloud
    $ avoscloud deploy
    $ avoscloud publish
    

    现在访问 timeline.avosapps.com,能看到刚才的修改都已经上线了。

  • 把常用的命令放到 package.json 里面

    修改 package.json

    "scripts": {
        "start": "cd leancloud && avoscloud -P 4567 && cd $OLDPWD",
    "watch": "BUILD_DEV=1 webpack --watch",
    "clean": "rm -rf leancloud/public/*",
    "build": "npm run clean && NODE_ENV=production webpack -p",
    "deploy": "cd leancloud && avoscloud deploy && cd $OLDPWD",
    "publish": "cd leancloud && avoscloud publish && cd $OLDPWD"
    },

    现在用 npm run XX 就可以跑一些常用的命令,你也可以把其中一些打包起来