从零开始的 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,
    },

    };

相关文章
相关标签/搜索