從零開始的 webpack4

從零開始的 webpack4

前提條件

安裝 node.js
當前 node.js 版本 :v12.13.1
當前 npm 版本 : 6.12.1javascript

完整源碼css

喜歡就加個星吧html

1、 簡易打包

1.初始化項目 & 建立項目

mkdir webpack4-react && cd webpack4-react
npm init -y

2.安裝webpack & webpack的cli

當前 webpack 版本:4.41.5
當前 webpack-cli 版本:3.3.10java

npm install --save-dev webpack webpack-cli

node

yarn add webpack webpack-cli --dev
調整 package.json 文件,以便確保咱們安裝包是私有的(private),而且移除 main 入口。這能夠防止意外發布你的代碼。

package.jsonreact

{
  ...
  "description": "webpack4-react",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}

3.下載lodash依賴

npm install --save lodash

webpack

yarn add lodash

4.建立如下目錄結構、文件和內容:

webpack4-react
|- package.json
|- /dist
  |- index.html
|- /src
  |- index.js

package.jsongit

{
  "name": "webpack4-react",
  "version": "1.0.0",
  "description": "webpack4-react",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.15"
  },
  "devDependencies": {
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10"
  }
}

dist/index.htmles6

<!DOCTYPE html>
<html>
  <head>
    <title>webpack4-react</title>
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>

src/index.jsgithub

import _ from 'lodash';
function component() {
  var element = document.createElement('div');
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}

document.body.appendChild(component());

5. 執行打包命令

npx webpack
將看到如下輸出:

Hash: 17a14a12467064d9d4dd
Version: webpack 4.41.5
Time: 1239ms
Built at: 2020-01-04 10:56:16
  Asset      Size  Chunks             Chunk Names
main.js  72.1 KiB       0  [emitted]  main
Entrypoint main = main.js
[1] ./src/index.js 210 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
    + 1 hidden module

此時在 dist 文件夾下已經生成一個 main.js 文件
在瀏覽器中打開 dist下的index.html,若是一切訪問都正常,你應該能看到如下文本:'Hello webpack'。

簡易打包已經完成

2、使用配置文件打包

1. 添加 webpack.config.js 配置文件

webpack4-react
|- package.json
|- webpack.config.js
|- /dist
  |- index.html
|- /src
  |- index.js

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

dist/index.html

...
<body>
  <script src="bundle.js"></script>
</body>
...

2. 添加NPM 腳本

package.json

{
  "name": "webpack4-react",
  "version": "1.0.0",
  "description": "webpack4-react",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.15"
  },
  "devDependencies": {
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10"
  }
}

3.執行腳本命令打包

npm run build

終端將輸出:

Hash: 9cbb2fac6cc224bfe661
Version: webpack 4.41.5
Time: 1272ms
Built at: 2020-01-04 11:39:26
  Asset      Size  Chunks             Chunk Names
main.js  72.1 KiB       0  [emitted]  main
Entrypoint main = main.js
[1] ./src/index.js 213 bytes {0} [built]
[2] (webpack)/buildin/global.js 472 bytes {0} [built]
[3] (webpack)/buildin/module.js 497 bytes {0} [built]
    + 1 hidden module

一樣的, dist 文件夾下生成bundle.js文件

這樣就實現了基本的webpack構建了

3、集成 React

1. 下載 react 和 react-dom

npm install --save-dev react react-dom

2. 下載babel(編譯js、jsx,es6等)

npm install --save-dev @babel/cli @babel/core @babel/preset-react @babel/preset-env @babel/plugin-transform-runtime babel-loader

3. 修改 入口文件

src/index.js

import React from 'react';
import ReactDom from 'react-dom';

const hello = 'Hello React'
ReactDom.render(
    <div>
        <div>{hello}</div>
    </div>,
    document.getElementById('app'),
);

4. 添加 react 根元素

dist/index.html

...
<body>
  <div id="app"></div>
  <script src="bundle.js"></script>
</body>
...

5. 添加 babel 相關配置

爲了使用babel解析 jsx
    1. webpack配置文件中

    webpack.config.js

    ...
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
              options: {
                presets: [
                  [
                    '@babel/preset-env',
                    {
                      useBuiltIns: 'usage',
                      corejs: 3,
                      targets: {
                        chrome: '58',
                        ie: '8',
                      },
                    },
                  ],
                  '@babel/preset-react',
                ],
              },
            },
          ],
        },
      ],
    }
    ...
    1. 添加babel配置文件
    在根目錄下新建 .babelrc 文件

    .babelrc

    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": 3,
            "targets": {
              "chrome": "58",
              "ie": "8"
            }
          }
        ],
        "@babel/preset-react"
      ],
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "absoluteRuntime": false,
            "corejs": false,
            "helpers": true,
            "regenerator": true,
            "useESModules": false
          }
        ]
      ]
    }

    當前文件目錄結構:

    webpack4-react
    |- /dist
      |- index.html
    |- /src
      |- index.js
    |- .babelrc
    |- package.json
    |- webpack.config.js

6. 打包

執行 npm run build
終端輸出:
```
$ webpack
Hash: f4d46dd4732764195f93
Version: webpack 4.41.5
Time: 446ms
Built at: 2020-01-04 13:28:48
    Asset     Size  Chunks             Chunk Names
bundle.js  128 KiB       0  [emitted]  main
Entrypoint main = bundle.js
[3] ./src/index.js 211 bytes {0} [built]
    + 7 hidden modules
```

在瀏覽器中打開 dist下的index.html,若是一切訪問都正常,你應該能看到如下文本:'Hello React'。

4、創建開發環境

1. webpack-dev-server

    1. 下載依賴
    npm install --save-dev webpack-dev-server
    1. webpack 配置文件中配置 webpack-dev-server

    webpack.config.js

    ...
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    devServer: {
      contentBase: '/src',
      hot: true,
    },
    ...

2. html-webpack-plugin

    1. 下載依賴
    npm install --save-dev html-webpack-plugin
    1. webpack 配置文件中配置 webpack-dev-server

    webpack.config.js

    ...
    devServer: {
      contentBase: '/src',
      hot: true,
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './src/index.html',
        filename: './index.html',
        chunks: ['index'],
        inject: 'body',
      }),
    ],
    ...

3. 添加 NPM 腳本

package.json

{
  "name": "webpack4-react",
  "version": "1.0.0",
  "description": "webpack4-react",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --open --config webpack.config.js",
    "build": "webpack --config webpack.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.15",
    "react": "^16.12.0",
    "react-dom": "^16.12.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.7.7",
    "@babel/core": "^7.7.7",
    "@babel/plugin-transform-runtime": "^7.7.6",
    "@babel/preset-env": "^7.7.7",
    "@babel/preset-react": "^7.7.4",
    "babel-loader": "^8.0.6",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.1"
  }
}

4.執行腳本命令 npm run dev

執行 npm run dev 後會自動打開瀏覽器,此時修改 index.js 文件中內容,瀏覽器會實時更新

刪除dist文件夾

執行 npm run build 打包依舊會在dist下生成打包文件

5、資源管理

1. 加載 CSS

爲了從 JavaScript 模塊中 import 一個 CSS 文件,你須要在 module 配置中 安裝並添加 style-loader 和 css-loader:
    1. 下載 style-loader css-loader
    npm install --save-dev style-loader css-loader
    1. webpack.config.js 中配置 css 的 loader
    module: {
          rules: [
            {
              test: /\.css$/,
              use: [
              'style-loader',
              'css-loader',
              ]
            },
      ...
      }
    1. src/index.js 中引入css
    import './style/reset.css';

    執行 npm run dev ,會看到 reset.css 中的樣式已經生效

2. CSS 預處理器 & 模塊化 & 兼容性處理

    1. 下載依賴

      npm install --save-dev autoprefixer postcss-loader
      npm install --save-dev less-loader node-sass sass sass-loader
      這個過程當中安裝 node-sass 可能會很慢, 耐心等待
    1. 建立React組件

      建立以下目錄文件及內容:

      webpack4-react
      |- src
        |- components
          |- Date
            |- index.jsx
            |- style.scss
        |- style
          |- reset.scss
        |- index.js
      |- .babelrc
      |- package.json
      |- webpack.config.js

      src/components/Date/index.jsx

      import React from 'react';
      import style from './style.scss';
      
      class DateComponent extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            date: new Date(),
          };
        }
      
        componentDidMount() {
          this.timerID = setInterval(() => this.tick(), 1000);
        }
      
        componentWillUnmount() {
          clearInterval(this.timerID);
        }
      
        tick() {
          this.setState({
            date: new Date(),
          });
        }
      
        render() {
          const { date } = this.state;
          return (
           <div>
              <div className={style.title}>時間</div>
              <div className={style.title}>
              {date.toLocaleTimeString()}
              </div>
            </div>
          );
        }
      }
      export default DateComponent;

      src/components/Date/style.scss

      .title {
        height: 50px;
        font: bold 20px '微軟雅黑';
        text-align: center;
        color: #000;
      }
    1. 引入組件

      src/index.js

      import React from 'react';
      import ReactDom from 'react-dom';
      import DateComponents from './components/Date/index.jsx';
      import './style/reset.scss';
      
      const hello = 'Hello React';
      ReactDom.render(
        <div>
          <div>{hello}</div>
          <DateComponents />
        </div>,
        document.getElementById('app')
      );
    1. 新增 CSS 相關的 webpack 配置

      根目錄下新建 tools/utils.js

      // css 配置
      const styleLoader = {
        loader: 'style-loader',
      };
      const cssLoader = {
        loader: 'css-loader',
        options: {
          modules: true, // webpack3 爲 module
          sourceMap: true,
          importLoaders: 2,
        },
      };
      const postCssLoader = {
        loader: 'postcss-loader',
      };
      const sassLoader = {
        loader: 'sass-loader',
        options: {
          sourceMap: true,
        },
      };
      const lessLoader = {
        loader: 'less-loader',
      };
      exports.loadersConfig = {
        styleLoader,
        cssLoader,
        postCssLoader,
        sassLoader,
        lessLoader,
      };
      // css 配置

      webpack.config.js

      ...
      const utils = require('./tools/utils.js');
      const {
        postCssLoader,
        styleLoader,
        sassLoader,
        cssLoader,
      } = utils.loadersConfig;
      ...
      module.exports = {
        ...
        module: {
          rules: [
            {
              test: /\.css$/,
              exclude: /node_modules/,
              use: [styleLoader, cssLoader, postCssLoader],
            },
            {
              test: /\.scss$/,
              include: [/pages/, /components/, /style/],
              use: [
                styleLoader,
                cssLoader,
                postCssLoader,
                sassLoader,
              ],
            },
          }
        ...
      };
    1. 新增 postcss-loader 配置文件

      根目錄下新增 postcss.config.js

      const AUTOPREFIXER_BROWSERS = [
        'Android 2.3',
        'Android >= 4',
        'Chrome >= 35',
        'Firefox >= 31',
        'Explorer >= 8',
        'iOS >= 7',
        'Opera >= 12',
        'Safari >= 7.1',
      ];
      module.exports = {
        plugins: [require('autoprefixer')({overrideBrowserslist: ['> 0.15% in CN']})],
      };
這時候執行 npm run dev 命令會報錯,由於缺乏一些babel依賴,下載一下就行了
npm install --save @babel/runtime core-js

執行 npm run dev ,自動打開瀏覽器,css 相關的配置構建完成

3. 圖片處理

    1. 下載依賴
    npm install --save-dev file-loader url-loader
    1. webpack配置中添加規則
    ...
    module: {
      rules: [
          ...
          {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  name: '[path][name].[ext]',
                  limit: 1024 * 15,
                  fallback: 'file-loader',
                },
              },
            ],
          },
          ...
      ]
    }
    ...
    1. React 組件中 引入圖片

    src/components/Date/index.jsx

    ...
    import reactLogo from './../../images/React.svg';
    import webpackLogo from './../../images/webpack.svg';
    ...
    render() {
      const { date } = this.state;
      return (
        <div>
          <div className={style.title}>時間</div>
          <div>
          <img className={style.logo} src={reactLogo} alt="" />
          <img className={style.logo} src={webpackLogo} alt="" />
          </div>
          <div className={style.title}>
          {date.toLocaleTimeString()}
          </div>
        </div>
      );
    }
    ...

4. 其餘

字體、數據等參考 webpack 官網 資源管理

6、 resolve 配置

在代碼引入組件或圖片時,咱們來配置一些便捷的方式

1. 配置

webpack.config.js

// 引入 node 的 path 模塊
const path = require('path');
...
module.exports = {
  ...
  resolve: {
    // 設置模塊導入規則,import/require時會直接在這些目錄找文件
    modules: ['node_modules'],
    // import導入時省略後綴
    extensions: ['.js', '.jsx', '.scss', '.less', '.css', '.json'],
    // import導入時別名
    alias: {
      '@components': path.resolve('./src/components'),
      '@images': path.resolve('./src/images'),
      '@style': path.resolve('./src/style'),
    },
  },
  ...
}

2. 使用

舉個🌰

src/index.js 中

import React from 'react';
import ReactDom from 'react-dom';
import DateComponents from '@components/Date/index.jsx';
import '@style/reset.scss';

const hello = 'Hello React';
ReactDom.render(
  <div>
    <div>{hello}</div>
    <DateComponents />
  </div>,
  document.getElementById('app')
);

此時 執行 npm run dev 查看

7、 環境配置構建

開發環境(development)和生產環境(production)的構建目標差別很大。在開發環境中,咱們須要具備強大的、具備實時從新加載(live reloading)或熱模塊替換(hot module replacement)能力的 source map 和 localhost server。而在生產環境中,咱們的目標則轉向於關注更小的 bundle,更輕量的 source map,以及更優化的資源,以改善加載時間。因爲要遵循邏輯分離,咱們一般建議爲每一個環境編寫彼此獨立的 webpack 配置。

1. 使用 webpack-merge 配置

    1. 下載依賴
    npm install --save-dev webpack-merge clean-webpack-plugin uglifyjs-webpack-plugin
    1. 拆分 webpack 配置

    根目錄下建立 webpack 文件夾

    |- webpack
      |- webpack.common.js
      |- webpack.dev.js
      |- webpack.production.js

    webpack.common.js

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    const utils = require('./../tools/utils');
    const { postCssLoader, styleLoader, sassLoader, cssLoader } = utils.loadersConfig;
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, '../dist'),
      },
      devServer: {
        contentBase: '/src',
        hot: true,
      },
      resolve: {
        // 設置模塊導入規則,import/require時會直接在這些目錄找文件
        modules: ['node_modules'],
        // import導入時省略後綴
        extensions: ['.js', '.jsx', '.scss', '.less', '.css', '.json'],
        // import導入時別名
        alias: {
        '@assets': path.resolve('./src/assets'),
        '@common': path.resolve('./src/common'),
        '@components': path.resolve('./src/components'),
        '@images': path.resolve('./src/images'),
        '@pages': path.resolve('./src/pages'),
        '@style': path.resolve('./src/style'),
        },
      },
      plugins: [
        new HtmlWebpackPlugin({
        template: './src/index.html',
        filename: './index.html',
        chunks: ['index'],
        inject: 'body',
        }),
      ],
      module: {
        rules: [
          {
            test: /\.css$/,
            exclude: /node_modules/,
            use: [styleLoader, cssLoader, postCssLoader],
          },
          {
            test: /\.scss$/,
            include: [/pages/, /components/, /style/],
            use: [styleLoader, cssLoader, postCssLoader, sassLoader],
          },
          {
            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  name: '[path][name].[ext]',
                  limit: 1024 * 15,
                  fallback: 'file-loader',
                },
              },
            ],
          },
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: [
              {
                loader: 'babel-loader',
                options: {
                  presets: [
                    [
                      '@babel/preset-env',
                      {
                          useBuiltIns: 'usage',
                          corejs: 3,
                            targets: {
                            chrome: '58',
                            ie: '8',
                          },
                      },
                    ],
                    '@babel/preset-react',
                  ],
                },
              },
            ],
          },
        ],
      },
    };

    webpack.dev.js

    const merge = require('webpack-merge');
    const common = require('./webpack.common.js');
    module.exports = merge(common, {
      mode: 'development',
      devtool: 'inline-source-map',
      devServer: {
        contentBase: '/src',
        hot: true,
      }
    });
    webpack.production.js
    const merge = require('webpack-merge');
      const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); // 用來縮小(壓縮優化)js文件
      const { CleanWebpackPlugin } = require('clean-webpack-plugin');
      const common = require('./webpack.common.js');
      module.exports = merge(common, {
        mode: 'production',
        devtool: 'source-map',
        plugins: [
          new UglifyJSPlugin({
            sourceMap: true,
          }),
          new CleanWebpackPlugin(),
        ],
      });
    1. npm 腳本命令更改

    package.json

    "dev": "webpack-dev-server --open --config webpack/webpack.dev.js",
    "build": "webpack --config webpack/webpack.production.js"
    1. 設置環境變量

    下載依賴

    npm install --save-dev cross-env

    npm 腳本命令更改

    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --config webpack/webpack.dev.js",
    "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.production.js"

8、 其餘優化

1. 配置中獲取環境變量

在 npm 腳本執行的時候設置的環境變量經過 process.env.NODE_ENV 來獲取,process.env.NODE_ENV 的值 在當前腳本下有兩種: development , production , 藉此能夠根據不一樣環境設置不一樣的配置。

2. 添加 html-loader 及 html 優化

    1. 下載依賴
    npm install --save-dev html-loader
    1. 配置

    webpack.config.common.js

    ...
    entry: {
      //配置頁面入口
      index: ['./src/index.js'],
    },
    output: {
      //配置輸出選項
      path: path.resolve(__dirname, '../dist'), //輸出路徑爲,當前路徑下
      filename: '[name].[hash:5].js', //輸出後的文件名稱
    },
    ...
    plugins: [
      new HtmlWebpackPlugin({
        title: 'webpack & react',
        template: './src/index.html', //本地模板文件的位置,支持加載器(如handlebars、ejs、undersore、html等),如好比 handlebars!src/index.hbs;
        filename: './index.html', //輸出文件的文件名稱,默認爲index.html,不配置就是該文件名;此外,還能夠爲輸出文件指定目錄位置(例如'html/index.html')
        chunks: ['index'], // chunks主要用於多入口文件,當你有多個入口文件,那就回編譯後生成多個打包後的文件,那麼chunks 就能選擇你要使用那些js文件
        inject: 'body', //一、true或者body:全部JavaScript資源插入到body元素的底部二、head: 全部JavaScript資源插入到head元素中三、false: 全部靜態資源css和JavaScript都不會注入到模板文件中
        showErrors: true, //是否將錯誤信息輸出到html頁面中
        hash: false, //是否爲全部注入的靜態資源添加webpack每次編譯產生的惟一hash值
        favicon: 'react.ico', //添加特定的 favicon 路徑到輸出的 HTML 文件中。
        minify: {
          //是否對大小寫敏感,默認false
          caseSensitive: true,
          //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫爲disabled  默認false
          collapseBooleanAttributes: true,
          //是否去除空格,默認false
          collapseWhitespace: true,
          //是否壓縮html裏的css(使用clean-css進行的壓縮) 默認值false;
          minifyCSS: true,
          //是否壓縮html裏的js(使用uglify-js進行的壓縮)
          minifyJS: true,
          //Prevents the escaping of the values of attributes
          preventAttributesEscaping: true,
          //是否移除屬性的引號 默認false
          removeAttributeQuotes: true,
          //是否移除註釋 默認false
          removeComments: true,
          //從腳本和樣式刪除的註釋 默認false
          removeCommentsFromCDATA: true,
          //是否刪除空屬性,默認false
          removeEmptyAttributes: true,
          //  若開啓此項,生成的html中沒有 body 和 head,html也未閉合
          removeOptionalTags: false,
          //刪除多餘的屬性
          removeRedundantAttributes: true,
          //刪除script的類型屬性,在h5下面script的type默認值:text/javascript 默認值false
          removeScriptTypeAttributes: true,
          //刪除style的類型屬性, type="text/css" 同上
          removeStyleLinkTypeAttributes: true,
          //使用短的文檔類型,默認false
          useShortDoctype: true,
        },
    }),
    ]
    ...
    module: {
      rules: [
        test: /\.html$/,
        use: 'html-loader',
      ]
    }
    npm install --save-dev html-loader

3. 壓縮 提取 CSS

    1. 下載依賴
    npm install --save-dev mini-css-extract-plugin
    1. 在生產環境中壓縮CSS, 開發環境中不壓縮

    webpack.config.common.js

    ...
    const devMode = process.env.NODE_ENV !== 'production';
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    ...
    module.exports = {
      ...
      plugins: [
        ...
        new MiniCssExtractPlugin({
          filename: devMode ? '[name].css' : '[name]_[hash:5].css',
          chunkFilename: devMode ? '[id].css' : '[id]_[hash:5].css',
          disable: false, //是否禁用此插件
          allChunks: true,
        }),
        ...
      ]
      ...
      module: {
        ...
        {
          test: /\.scss$/,
          include: [/pages/, /components/, /style/],
          use: [
            devMode ? styleLoader : MiniCssExtractPlugin.loader,
            cssLoader,
            postCssLoader,
            sassLoader,
          ],
        },
        ...
      }
    }

4. 生產環境壓縮 JS, 打包時清除dist文件夾

    1. 下載依賴
    npm install --save-dev uglifyjs-webpack-plugin clean-webpack-plugin
    1. webpack 配置

    webpack.production.js

    ...
    const merge = require('webpack-merge');
    const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    const common = require('./webpack.config.common.js');
    module.exports = merge(common, {
      mode: 'production',
      devtool: 'source-map',
      plugins: [
        new UglifyJSPlugin({
          sourceMap: true,
        }),
        new CleanWebpackPlugin(),
      ],
    });
    ...

5. happypack 加快打包速度

    1. 下載依賴
    npm install --save-dev happypack
    1. 配置

    webpack.config.common.js

    ...
    const os = require('os');
    const HappyPack = require('happypack');
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
    ...
    plugins: [
      ...
      new HappyPack({
        id: 'babel', //用id來標識 happypack處理那裏類文件
        threadPool: happyThreadPool, //共享進程池
        loaders: [
          {
            loader: 'babel-loader',
          },
        ],
      }),
      ...
    ],
    ...
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          use: ['happypack/loader?id=babel'],
          exclude: /node_modules/, //設置node_modules裏的js文件不用解析
        },
      ]
    }
    ...

5. polyfill 編譯es6的新語法

    1. 下載依賴
    npm install --save-dev @babel/polyfill @babel/plugin-transform-arrow-functions @babel/preset-es2017
    1. 配置

    webpack.common.js

    entry: {
      //配置頁面入口
      index: ['@babel/polyfill', './src/index.js'],
    },

9、 代碼規範- eslint & stylelint

1. 添加編輯器配置文件以及插件

vs code 格式化插件 使用的是 Prettier - Code formatter 以及 ESLint

|- .vscode
    |- setting.json

setting.json

{
  "editor.tabSize": 4,
  "prettier.singleQuote": true,
  "editor.detectIndentation": false,
  "editor.renderControlCharacters": true,
  "editor.renderWhitespace": "all",
  "emmet.includeLanguages": {
    "javascript": "javascriptreact"
  },
  "prettier.trailingComma": "es5",
  "emmet.triggerExpansionOnTab": true,
  "javascript.implicitProjectConfig.experimentalDecorators": true,
  "workbench.colorTheme": "Solarized Light",
  "window.zoomLevel": 0,
  "prettier.useTabs": true,
  "editor.foldingStrategy": "indentation",
  "explorer.confirmDelete": false,
  "javascript.updateImportsOnFileMove.enabled": "never",
  "eslint.validate": [
    {
      "language": "javascript",
      "autoFix": true
    },
    {
      "language": "javascriptreact",
      "autoFix": true
    }
  ],
  "eslint.autoFixOnSave": true
}

2. eslint

    1. 下載依賴
    npm insatll --save-dev babel-eslint eslint eslint-config-airbnb eslint-config-react-app eslint-friendly-formatter eslint-loader eslint-plugin-flowtype eslint-plugin-html eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react autoprefixer pre-commit
    1. webpack 的 eslint 配置

    根目錄下新建 .eslintrc.js 文件

    module.exports = {
      root: true,
      env: {
        browser: true,
        commonjs: true,
        es6: true,
      },
      extends: [
        'airbnb',
      ],
      globals: {
        $: true,
        process: true,
        __dirname: true,
      },
      parser: 'babel-eslint',
      parserOptions: {
        //es6的module模式
        sourceType: 'module',
        ecmaFeatures: {
          experimentalObjectRestSpread: true,
          jsx: true,
        },
        ecmaVersion: 9,
      },
      settings: {
        'import/ignore': ['node_modules', '.s?css', '@w*'],
      },
      // "excludedFiles": "*.test.js",
      plugins: ['react', 'import', 'jsx-a11y'],
      rules: {
        'import/no-unresolved': 0,
        'import/extensions': 0,
        'import/prefer-default-export': 0,
    
        'react/prop-types': 0,
        'react/jsx-filename-extension': 0,
        'react/prefer-stateless-function': 0,
        'react/jsx-indent': [2, 'tab'],
        'react/jsx-indent-props': [2, 'tab'],
        'react/require-default-props': 0,
        // // @off 同構應用須要在 didMount 裏寫 setState
        'react/no-did-mount-set-state': 0,
    
        'jsx-a11y/anchor-is-valid': 0,
        'jsx-a11y/click-events-have-key-events': 0,
        'jsx-a11y/mouse-events-have-key-events': 0,
        'jsx-a11y/no-noninteractive-element-interactions': 0,
        'jsx-a11y/no-static-element-interactions': 0,
    
        'no-return-assign': 0,
        'no-console': 0,
        // 0、一、2分別表示不開啓檢查、警告、錯誤
        indent: [2, 'tab', { SwitchCase: 1 }], // tab縮進
        // 圈複雜度
        complexity: [2, 9],
        'max-params': [2, 7],
        'max-depth': [2, 4],
        'max-len': [
          'error',
          {
            code: 150,
            tabWidth: 4,
            ignoreComments: true,
            ignoreUrls: true,
            ignoreStrings: true,
            ignoreTemplateLiterals: true,
            ignoreRegExpLiterals: true,
          },
        ],
        'no-tabs': 0,
        'object-curly-newline': [
          0,
          {
            ObjectExpression: 'always',
            ObjectPattern: { multiline: true },
            ImportDeclaration: 'never',
            ExportDeclaration: {
              multiline: true,
            },
          },
        ],
        'object-curly-spacing': 0,
    
        'arrow-parens': [2, 'as-needed'],
        // 最大回調層數
        'max-nested-callbacks': [2, 3],
        'no-unused-vars': [
          2,
          {
            argsIgnorePattern: '^React',
            varsIgnorePattern: '[Rr]eact|[Ss]tyle',
          },
        ],
        'no-extra-boolean-cast': 0,
        'array-callback-return': 0,
        'no-param-reassign': 0,
        'jsx-quotes': [0, 'prefer-double'], //強制在JSX屬性(jsx-quotes)中一導致用雙引號
        'no-underscore-dangle': 0,
        'quote-props': 0,
        // "no-native-reassign": 2,//不能重寫native對象
        // // if while function 後面的{必須與if在同一行,java風格。
        // "brace-style": [2, "1tbs", { "allowSingleLine": true }],
        // // 雙峯駝命名格式
        // "camelcase": 2,
        // // 以方括號取對象屬性時,[ 後面和 ] 前面是否須要空格, 可選參數 never, always
        // "computed-property-spacing": [2,"never"],
        // //容許箭頭函數能夠省略小括號
        // 'arrow-parens': 0,
        // 'no-extra-semi': 2, // 不容許多餘的分號
        // //容許使用async-await函數
        // 'generator-star-spacing': 0,
        // //在開發環境開啓debugger功能,生產環境禁止使用debugger
        // 'no-debugger': process.env.NODE_ENV === 'development' ? 0 : 2,
        // "quotes": [2, "single"], //單引號
        // "no-var": 2, //對var警告
        // "semi": ["error", "always"], //不強制使用分號
        // "no-irregular-whitespace": 0, //不規則的空白不容許
        // "no-alert": 2, //禁止使用alert confirm prompt
        // "no-lone-blocks": 0, //禁止沒必要要的嵌套塊
        // "no-class-assign": 2, //禁止給類賦值
        // "no-cond-assign": 2, //禁止在條件表達式中使用賦值語句
        // "no-const-assign": 2, //禁止修改const聲明的變量
        // "no-delete-var": 2, //不能對var聲明的變量使用delete操做符
        // "no-dupe-keys": 2, //在建立對象字面量時不容許鍵重複
        // "no-duplicate-case": 2, //switch中的case標籤不能重複
        // "no-dupe-args": 2, //函數參數不能重複
        // "no-empty": 2, //塊語句中的內容不能爲空
        // "no-func-assign": 2, //禁止重複的函數聲明
        // "no-invalid-this": 0, //禁止無效的this,只能用在構造器,類,對象字面量
        // "no-redeclare": 2, //禁止重複聲明變量
        // "no-spaced-func": 2, //函數調用時 函數名與()之間不能有空格
        // "no-this-before-super": 0, //在調用super()以前不能使用this或super
        // "no-undef": 2, //不能有未定義的變量
        // "no-use-before-define": 2, //未定義前不能使用
        // // "camelcase": 0, //強制駝峯法命名
        // "no-mixed-spaces-and-tabs": 0, //禁止混用tab和空格
        // "prefer-arrow-callback": 0, //比較喜歡箭頭回調
        // "arrow-spacing": 0, //=>的前/後括號
        //
        // // 禁止在 componentDidMount 裏面使用 setState
    
        // // 禁止在 componentDidUpdate 裏面使用 setState
        // 'react/no-did-update-set-state': 2,
        // // 禁止拼寫錯誤
    
        // 'react/no-typos': 2,
        // // 禁止使用字符串 ref
        // 'react/no-string-refs': 2,
        // // @fixable 禁止出現 HTML 中的屬性,如 class
        // 'react/no-unknown-property': 2,
        // // 禁止出現未使用的 propTypes
        // // @off 不強制要求寫 propTypes
        // 'react/no-unused-prop-types': 2,
        // // 出現 jsx 的地方必須 import React
        // // @off 已經在 no-undef 中限制了
        // 'react/react-in-jsx-scope': 0,
        // // 非 required 的 prop 必須有 defaultProps
        // // @off 不強制要求寫 propTypes
        // 'react/require-default-props': 0,
        // // render 方法中必須有返回值
        // 'react/require-render-return': 2,
        // // @fixable 組件內沒有 children 時,必須使用自閉和寫法
        // // @off 不必限制
        // 'react/self-closing-comp': 0,
        // // style 屬性的取值必須是 object
        // 'react/style-prop-object': 2,
        // // HTML 中的自閉和標籤禁止有 children
        // 'react/void-dom-elements-no-children': 2,
        // // 數組中的 jsx 必須有 key
        // 'react/jsx-key': 2,
        // // 禁止在 jsx 中使用像註釋的字符串
        // 'react/jsx-no-comment-textnodes': 2,
        // // 禁止出現重複的 props
        // 'react/jsx-no-duplicate-props': 2,
        // // 禁止使用未定義的 jsx elemet
        // 'react/jsx-no-undef': 2,
        // // jsx 文件必須 import React
        // 'react/jsx-uses-react': 2,
        // // 定義了的 jsx element 必須使用
        // 'react/jsx-uses-vars': 2,
        // // @fixable 多行的 jsx 必須有括號包起來
        // // @off 不必限制
        // 'react/jsx-wrap-multilines': 2,
        // "react/no-array-index-key": 2, // 遍歷出來的節點必須加key
        // "react/no-children-prop": 2, // 禁止使用children做爲prop
        // "react/no-direct-mutation-state": 2, // 禁止直接this.state = 方式修改state 必須使用setState
      },
    };

    webpack.common.js

    ...
    module: {
      rules: [
        ...
        {
          test: /\.(js|jsx)$/,
          loader: 'eslint-loader',
          enforce: 'pre',
          include: [path.resolve(__dirname, 'src')], // 指定檢查的目錄
          options: {
            // 這裏的配置項參數將會被傳遞到 eslint 的 CLIEngine
            formatter: require('eslint-friendly-formatter'), // 指定錯誤報告的格式規範
          },
        },
      ]
    }
    ...

    根目錄下新建 .eslintignore 文件 用來制定忽略某些文件的 eslint 校驗

    webpack

3. stylelint

    1. 下載依賴

      npm insatll --save-dev stylelint stylelint-config-recommended stylelint-config-standard stylelint-order stylelint-webpack-plugin
    1. stylelint 配置

      webpack.dev.js

    const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); // 用來縮小(壓縮優化)js文件
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    const common = require('./webpack.common.js');
    module.exports = merge(common, {
    mode: 'production',
    devtool: 'source-map',
    plugins: [

    new UglifyJSPlugin({
      sourceMap: true,
    }),
    new CleanWebpackPlugin(),

    ],
    });

    package.json

    ...
    "pre-commit": [

    "dev",
    "build"

    ],
    ...

    根目錄下新建 .stylelintrc.js 文件

    module.exports = {

    extends: ['stylelint-config-standard', 'stylelint-config-recommended'],
    plugins: ['stylelint-order'],
    rules: {
      'order/order': [
        // "at-rules",
        // "declarations",
        'custom-properties',
        'dollar-variables',
        'rules',
      ],
      'order/properties-order': [
        'position',
        'z-index',
        'top',
        'bottom',
        'left',
        'right',
        'float',
        'clear',
        'columns',
        'columns-width',
        'columns-count',
        'column-rule',
        'column-rule-width',
        'column-rule-style',
        'column-rule-color',
        'column-fill',
        'column-span',
        'column-gap',
        'display',
        'grid',
        'grid-template-rows',
        'grid-template-columns',
        'grid-template-areas',
        'grid-auto-rows',
        'grid-auto-columns',
        'grid-auto-flow',
        'grid-column-gap',
        'grid-row-gap',
        'grid-template',
        'grid-template-rows',
        'grid-template-columns',
        'grid-template-areas',
        'grid-gap',
        'grid-row-gap',
        'grid-column-gap',
        'grid-area',
        'grid-row-start',
        'grid-row-end',
        'grid-column-start',
        'grid-column-end',
        'grid-column',
        'grid-column-start',
        'grid-column-end',
        'grid-row',
        'grid-row-start',
        'grid-row-end',
        'flex',
        'flex-grow',
        'flex-shrink',
        'flex-basis',
        'flex-flow',
        'flex-direction',
        'flex-wrap',
        'justify-content',
        'align-content',
        'align-items',
        'align-self',
        'order',
        'table-layout',
        'empty-cells',
        'caption-side',
        'border-collapse',
        'border-spacing',
        'list-style',
        'list-style-type',
        'list-style-position',
        'list-style-image',
        'ruby-align',
        'ruby-merge',
        'ruby-position',
        'box-sizing',
        'width',
        'min-width',
        'max-width',
        'height',
        'min-height',
        'max-height',
        'padding',
        'padding-top',
        'padding-right',
        'padding-bottom',
        'padding-left',
        'margin',
        'margin-top',
        'margin-right',
        'margin-bottom',
        'margin-left',
        'border',
        'border-width',
        'border-top-width',
        'border-right-width',
        'border-bottom-width',
        'border-left-width',
        'border-style',
        'border-top-style',
        'border-right-style',
        'border-bottom-style',
        'border-left-style',
        'border-color',
        'border-top-color',
        'border-right-color',
        'border-bottom-color',
        'border-left-color',
        'border-image',
        'border-image-source',
        'border-image-slice',
        'border-image-width',
        'border-image-outset',
        'border-image-repeat',
        'border-top',
        'border-top-width',
        'border-top-style',
        'border-top-color',
        'border-top',
        'border-right-width',
        'border-right-style',
        'border-right-color',
        'border-bottom',
        'border-bottom-width',
        'border-bottom-style',
        'border-bottom-color',
        'border-left',
        'border-left-width',
        'border-left-style',
        'border-left-color',
        'border-radius',
        'border-top-right-radius',
        'border-bottom-right-radius',
        'border-bottom-left-radius',
        'border-top-left-radius',
        'outline',
        'outline-width',
        'outline-color',
        'outline-style',
        'outline-offset',
        'overflow',
        'overflow-x',
        'overflow-y',
        'resize',
        'visibility',
        'font',
        'font-style',
        'font-variant',
        'font-weight',
        'font-stretch',
        'font-size',
        'font-family',
        'font-synthesis',
        'font-size-adjust',
        'font-kerning',
        'line-height',
        'text-align',
        'text-align-last',
        'vertical-align',
        'text-overflow',
        'text-justify',
        'text-transform',
        'text-indent',
        'text-emphasis',
        'text-emphasis-style',
        'text-emphasis-color',
        'text-emphasis-position',
        'text-decoration',
        'text-decoration-color',
        'text-decoration-style',
        'text-decoration-line',
        'text-underline-position',
        'text-shadow',
        'white-space',
        'overflow-wrap',
        'word-wrap',
        'word-break',
        'line-break',
        'hyphens',
        'letter-spacing',
        'word-spacing',
        'quotes',
        'tab-size',
        'orphans',
        'writing-mode',
        'text-combine-upright',
        'unicode-bidi',
        'text-orientation',
        'direction',
        'text-rendering',
        'font-feature-settings',
        'font-language-override',
        'image-rendering',
        'image-orientation',
        'image-resolution',
        'shape-image-threshold',
        'shape-outside',
        'shape-margin',
        'color',
        'background',
        'background-image',
        'background-position',
        'background-size',
        'background-repeat',
        'background-origin',
        'background-clip',
        'background-attachment',
        'background-color',
        'background-blend-mode',
        'isolation',
        'clip-path',
        'mask',
        'mask-image',
        'mask-mode',
        'mask-position',
        'mask-size',
        'mask-repeat',
        'mask-origin',
        'mask-clip',
        'mask-composite',
        'mask-type',
        'filter',
        'box-shadow',
        'opacity',
        'transform-style',
        'transform',
        'transform-box',
        'transform-origin',
        'perspective',
        'perspective-origin',
        'backface-visibility',
        'transition',
        'transition-property',
        'transition-duration',
        'transition-timing-function',
        'transition-delay',
        'animation',
        'animation-name',
        'animation-duration',
        'animation-timing-function',
        'animation-delay',
        'animation-iteration-count',
        'animation-direction',
        'animation-fill-mode',
        'animation-play-state',
        'scroll-behavior',
        'scroll-snap-type',
        'scroll-snap-destination',
        'scroll-snap-coordinate',
        'cursor',
        'touch-action',
        'caret-color',
        'ime-mode',
        'object-fit',
        'object-position',
        'content',
        'counter-reset',
        'counter-increment',
        'will-change',
        'pointer-events',
        'all',
        'page-break-before',
        'page-break-after',
        'page-break-inside',
        'widows',
      ],
      indentation: 'tab',
      'color-no-invalid-hex': true,
      'font-family-no-missing-generic-family-keyword': null,
      'font-family-name-quotes': null,
      'function-url-quotes': 'always',
      'at-rule-no-unknown': null,
      'no-eol-whitespace': null,
      'selector-attribute-quotes': 'always',
      'string-quotes': 'single',
      'selector-pseudo-element-colon-notation': null,
      'at-rule-no-vendor-prefix': true,
      'media-feature-name-no-vendor-prefix': null,
      'media-feature-name-no-unknown': null,
      'property-no-vendor-prefix': null,
      'selector-no-vendor-prefix': true,
      'value-no-vendor-prefix': true,
      'selector-pseudo-class-no-unknown': null,
      'shorthand-property-no-redundant-values': null,
      'at-rule-empty-line-before': null,
      'at-rule-name-space-after': null,
      'comment-empty-line-before': null,
      'declaration-bang-space-before': null,
      'declaration-empty-line-before': null,
      'function-comma-newline-after': null,
      'function-name-case': null,
      'function-parentheses-newline-inside': null,
      'function-max-empty-lines': null,
      'function-whitespace-after': null,
      'number-leading-zero': null,
      'number-no-trailing-zeros': null,
      'rule-empty-line-before': null,
      'selector-combinator-space-after': null,
      'selector-list-comma-newline-after': null,
      // "selector-pseudo-element-colon-notation": null,
      'unit-no-unknown': null,
      'no-descending-specificity': null,
      'value-list-max-empty-lines': null,
    },

    };

相關文章
相關標籤/搜索