# 费时分析

const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin');
const smw = new SpeedMeasureWebpackPlugin();
module.exports =smw.wrap({
});

# 缩小范围

# extensions

指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配

resolve: {
  extensions: [".js",".jsx",".json",".css"]
},

# alias

配置别名可以加快webpack查找模块的速度

每当引入bootstrap模块的时候,它会直接引入bootstrap,而不需要从node_modules文件夹中按模块的查找规则查找

const bootstrap = path.resolve(__dirname,'node_modules/_bootstrap@3.3.7@bootstrap/dist/css/bootstrap.css');
resolve: {
+    alias:{
+        "bootstrap":bootstrap
+    }
},

# modules

对于直接声明依赖名的模块(如 react ),webpack 会类似 Node.js 一样进行路径搜索,搜索node_modules目录

这个目录就是使用resolve.modules字段进行配置的 默认配置

resolve: {
modules: ['node_modules'],
}

如果可以确定项目内所有的第三方依赖模块都是在项目根目录下的 node_modules 中的话

resolve: {
modules: [path.resolve(__dirname, 'node_modules')],
}

# mainFields

默认情况下package.json 文件则按照文件中 main 字段的文件名来查找文件

resolve: {
  // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
  mainFields: ['browser', 'module', 'main'],
  // target 的值为其他时,mainFields 默认值为:
  mainFields: ["module", "main"],
}

# mainFiles

当目录下没有 package.json 文件时,我们说会默认使用目录下的 index.js 这个文件,其实这个也是可以配置的

resolve: {
  mainFiles: ['index'], // 你可以添加其他默认使用的文件名
}

# resolveLoader

resolve.resolveLoader用于配置解析 loader 时的 resolve 配置,默认的配置

module.exports = {
  resolveLoader: {
    modules: [ 'node_modules' ],
    extensions: [ '.js', '.json' ],
    mainFields: [ 'loader', 'main' ]
  }
}

# noParse

module.noParse 字段,可以用于配置哪些模块文件的内容不需要进行解析

不需要解析依赖(即无依赖) 的第三方大型类库等,可以通过这个字段来配置,以提高整体的构建速度

module.exports = {
// ...
module: {
  noParse: /jquery|lodash/, // 正则表达式
  // 或者使用函数
  noParse(content) {
    return /jquery|lodash/.test(content)
  },
}
}...

使用 noParse 进行忽略的模块文件中不能使用 import、require、define 等导入机制

# IgnorePlugin

IgnorePlugin用于忽略某些特定的模块,让 webpack 不把这些指定的模块打包进去

import moment from  'moment';
console.log(moment);

第一个是匹配引入模块路径的正则表达式;第二个是匹配模块的对应上下文,即所在目录名

new webpack.IgnorePlugin(/^\.\/locale/,/moment$/)

# 日志优化

日志太多太少都不美观,可以修改stats

预设 替代 描述
errors-only none 只在错误时输出
minimal none 发生错误和新的编译时输出
none false 没有输出
normal true 标准输出
verbose none 全部输出

# friendly-errors-webpack-plugin

friendly-errors-webpack-plugin

  • success 构建成功的日志提示
  • warning 构建警告的日志提示
  • error 构建报错的日志提示
cnpm i friendly-errors-webpack-plugin
+ stats:'verbose',
  plugins:[
+   new FriendlyErrorsWebpackPlugin()
  ]

编译完成后可以通过echo $?获取错误码,0为成功,非0为失败

# DLL

  • .dll为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据
  • 把基础模块独立出来打包到单独的动态连接库里
  • 当需要导入的模块在动态连接库里的时候,模块不能再次被打包,而是去动态连接库里获取

# 第一步 定义Dll

  • DllPlugin插件: 用于打包出一个个动态连接库
  • DllReferencePlugin: 在配置文件中引入DllPlugin插件打包好的动态连接库 webpack.dll.config.js
const path = require("path");

const DllPlugin = require("webpack/lib/DllPlugin");
module.exports = {
  mode: "development",
  entry: {
    react: ["react", "react-dom"],
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
    path: path.resolve(__dirname, "dist"),
    filename: "[name].dll.js", //react.dll.js
    // library必须和后面dllplugin中的name一致 后面会说明
    library: "_dll_[name]",
  },
  plugins: [
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      name: "_dll_[name]",
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(__dirname, "dist", "[name].manifest.json"), //react.manifest.json
    }),
  ],
};
webpack --config webpack.dll.config.js --mode=development

react.dll文件里是保存的模块,key就作为id;

var _dll_react = (function(modules) {/*省略若干代码*/})({/*省略若干代码*/})

react.manifest.json文件里,是用来描述对应的dll文件里保存的模块

{
    "name": "_dll_react",
    "content": {
        "./node_modules/_object-assign@4.1.1@object-assign/index.js": {
            "id": "./node_modules/_object-assign@4.1.1@object-assign/index.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_prop-types@15.7.2@prop-types/checkPropTypes.js": {
            "id": "./node_modules/_prop-types@15.7.2@prop-types/checkPropTypes.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_prop-types@15.7.2@prop-types/lib/ReactPropTypesSecret.js": {
            "id": "./node_modules/_prop-types@15.7.2@prop-types/lib/ReactPropTypesSecret.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_react-dom@16.13.1@react-dom/cjs/react-dom.development.js": {
            "id": "./node_modules/_react-dom@16.13.1@react-dom/cjs/react-dom.development.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_react-dom@16.13.1@react-dom/index.js": {
            "id": "./node_modules/_react-dom@16.13.1@react-dom/index.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_react@16.13.1@react/cjs/react.development.js": {
            "id": "./node_modules/_react@16.13.1@react/cjs/react.development.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_react@16.13.1@react/index.js": {
            "id": "./node_modules/_react@16.13.1@react/index.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_scheduler@0.19.1@scheduler/cjs/scheduler-tracing.development.js": {
            "id": "./node_modules/_scheduler@0.19.1@scheduler/cjs/scheduler-tracing.development.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_scheduler@0.19.1@scheduler/cjs/scheduler.development.js": {
            "id": "./node_modules/_scheduler@0.19.1@scheduler/cjs/scheduler.development.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_scheduler@0.19.1@scheduler/index.js": {
            "id": "./node_modules/_scheduler@0.19.1@scheduler/index.js",
            "buildMeta": {
                "providedExports": true
            }
        },
        "./node_modules/_scheduler@0.19.1@scheduler/tracing.js": {
            "id": "./node_modules/_scheduler@0.19.1@scheduler/tracing.js",
            "buildMeta": {
                "providedExports": true
            }
        }
    }
}

# 第二步 使用动态链接库文件

在webpack.config中使用dll要用到DllReferencePlugin,这个插件通过引用 dll 的 manifest 文件来把依赖的名称映射到模块的 id 上,之后再在需要的时候通过内置的 webpack_require 函数来 require 他们.

const path = require("path");
const glob = require("glob");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const DllReferencePlugin = require("webpack/lib/DllReferencePlugin.js");
const PATHS = {
  src: path.join(__dirname, 'src')
}
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /\.js/,
        include: path.resolve(__dirname, "src"),
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-env", "@babel/preset-react"],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        include: path.resolve(__dirname, "src"),
        exclude: /node_modules/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          "css-loader",
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
    }),
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`,  { nodir: true }),
    }),
    // 第一步产出的manifest文件就用在这里,给主构建流程作为查找dll的依据:DllReferencePlugin去 manifest.json 文件读取 name 字段的值,把值的内容作为在从全局变量中获取动态链接库中内容时的全局变量名,因此:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 参数必须和 output.library 中保持一致。
+    new DllReferencePlugin({
+      manifest: require("./dist/react.manifest.json"),
+    }),
  ],
};
webpack --config webpack.config.js --mode development

# 第三步 html中使用

<script src="react.dll.js"></script>

# 利用缓存

webpack中利用缓存一般有以下几种思路:

  • babel-loader开启缓存
  • 使用cache-loader
  • 使用hard-source-webpack-plugin

# babel-loader

Babel在转义js文件过程中消耗性能较高,将babel-loader执行的结果缓存起来,当重新打包构建时会尝试读取缓存,从而提高打包构建速度、降低消耗

{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [{
    loader: "babel-loader",
    options: {
      cacheDirectory: true
    }
  }]
}

# cache-loader

  • 在一些性能开销较大的 loader 之前添加此 loader,以将结果缓存到磁盘里
  • 存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader
cnpm i  cache-loader -D
const loaders = ['babel-loader'];
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          'cache-loader',
          ...loaders
        ],
        include: path.resolve('src')
      }
    ]
  }
}

# hard-source-webpack-plugin

  • HardSourceWebpackPlugin为模块提供了中间缓存,缓存默认的存放路径是 node_modules/.cache/hard-source。`
  • 配置 hard-source-webpack-plugin后,首次构建时间并不会有太大的变化,但是从第二次开始,构建时间大约可以减少 80%左右
  • webpack5中会内置hard-source-webpack-plugin
cnpm i  hard-source-webpack-plugin -D 
var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

module.exports = {
  entry: // ...
  output: // ...
  plugins: [
    new HardSourceWebpackPlugin()
  ]
}

# oneOf

每个文件对于rules中的所有规则都会遍历一遍,如果使用oneOf就可以解决该问题,只要能匹配一个即可退出。(注意:在oneOf中不能两个配置处理同一种类型文件)

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        //优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下 loader 只会匹配一个
        oneOf: [
          ...,
          {},
          {}
        ]
      }
    ]
  }
}

# 多进程处理

# thread-loader

把这个 loader 放置在其他 loader 之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行

cnpm  i thread-loader- D
const path = require("path");
const glob = require("glob");
const PurgecssPlugin = require("purgecss-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const DllReferencePlugin = require("webpack/lib/DllReferencePlugin.js");
const PATHS = {
  src: path.join(__dirname, 'src')
}
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  module: {
    rules: [
      {
        test: /\.js/,
        include: path.resolve(__dirname, "src"),
        use: [
+          {
+            loader:'thread-loader',
+            options:{
+              workers:3
+            }
+          },
          {
            loader: "babel-loader",
            options: {
              presets: ["@babel/preset-env", "@babel/preset-react"],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        include: path.resolve(__dirname, "src"),
        exclude: /node_modules/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          "css-loader",
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
    }),
    new PurgecssPlugin({
      paths: glob.sync(`${PATHS.src}/**/*`,  { nodir: true }),
    }),
    new DllReferencePlugin({
      manifest: require("./dist/react.manifest.json"),
    }),
  ],
};

# parallel

terser-webpack-plugin 开启 parallel 参数

const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
            }),
        ],
    },
};