Skip to content

webpack 优化构建速度

用于生产环境

注,打包配置一般是指 webpack.prod.js 文件

1、优化 babel-loader,开启缓存和排除范围

js
{
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: ['babel-loader?cacheDirectory'], // 开启缓存
                include: path.resolve(__dirname,'scr'),
                // 排除范围,include 和 exclude 二选一
                //exclude: path.resolve(__dirname,'node_modules')
            },
        ]
    },
}

2、用 IngorePlugin 忽略无用文件,比如

js
import moment from 'moment';

默认会引入所有语言 JS 代码,代码过大,如何只引入中文?

先忽略语言包文件夹

js
{
  plugins: [
    // 忽略 moment 下的 /locale 目录
    new webpack.IgnorePlugin(/\.\/locale/, /moment/),
  ];
}

然后手动引入

js
import moment from 'moment';
import 'moment/locale/zh-cn'; // 手动引入语言包
moment.locale('zh-cn'); // 设置中文
console.log('locale', moment.locale());
console.log('date', moment().format('ll')); // 2020年xx月xx日
// locale zh-cn
// date 2020年12月28日

3、noParse 避免重复打包

js
{
    module: {
        // 独立完整的 react.min.js 文件没有采用模块化
        // 忽略对 react.min.js 文件的递归解析处理
        noParse:[/react\.min\.js$/],
    }
}

IngorePlugin 和 noParse 区别

  • IngorePlugin 直接不引入,代码中没有
  • noParse 引入,但不打包

4、happyPack 多进程打包

  • JS 单线程,开启多进程打包
  • 提高构建速度(特别是多核 CPU)

比如开启 babel 开启多进程,先引入

js
const HappyPack = require('happypack');

然后配置

js
{
    module: {
        rules: [
            // js
            {
                test: /\.js$/,
                // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
                use: ['happypack/loader?id=babel'],
                include: srcPath,
                // exclude: /node_modules/
            },
        ]
    },
    plugins: [
        // happyPack 开启多进程打包
        new HappyPack({
            // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
            id: 'babel',
            // 如何处理 .js 文件,用法和 Loader 配置中一样
            loaders: ['babel-loader?cacheDirectory']
        }),
    ]
}

5、使用 ParallelUglifyPlugin 多进程压缩 JS

  • webpack 内置 Uglify 工具压缩 JS (单进程)
  • JS 单线程,开启多进程压缩更快
  • 和 happyPack 同原理

先引入

js
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

然后配置

js
{
    plugins: [
        // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
        new ParallelUglifyPlugin({
            // 传递给 UglifyJS 的参数
            // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
            uglifyJS: {
                output: {
                    beautify: false, // 最紧凑的输出
                    comments: false, // 删除所有的注释
                },
                compress: {
                    // 删除所有的 `console` 语句,可以兼容ie浏览器
                    drop_console: true,
                    // 内嵌定义了但是只用到一次的变量
                    collapse_vars: true,
                    // 提取出出现多次但是没有定义成变量去引用的静态值
                    reduce_vars: true,
                }
            }
        })
    ],
}

关于开启多进程

  • 项目较大,打包较慢,开启多进程能提高速度
  • 项目较小,打包很快,开启多进程会减低速度(进程开销)

用于开发环境

注,以下配置 webpack.dev.js 文件

6、开启浏览器自动刷新

一般用不上,本地开发 devServer 默认开启监听文件变化,自动刷新浏览器

js
{
    devServer: {
    },
    watch: true, // 开启监听,默认为 false
    watchOptions: {
        ignored: /node_modules/, // 忽略哪些
        // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
        // 默认为 300ms
        aggregateTimeout: 300,
        // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
        // 默认每隔1000毫秒询问一次
        poll: 1000
    }
}

7、开启热更新

  • 自动刷新:整个页面全部刷新,速度较慢
  • 自动刷新:整个页面全部刷新,状态会丢失

先引入

js
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');

然后配置

js
{
    entry: {
        // index: path.join(srcPath, 'index.js'),
        index: [
            'webpack-dev-server/client?http://localhost:8080/',
            'webpack/hot/dev-server',
            path.join(srcPath, 'index.js')
        ],
    },
    plugins: [
        new HotModuleReplacementPlugin()
    ],
    devServer: {
        hot: true,
    }
}

这样修改 css 可以实现热更新了,但 JS 需要监听范围,如

js
import { sum } from './math';
// 增加,开启热更新之后的代码逻辑
if (module.hot) {
  module.hot.accept(['./math'], () => {
    const sumRes = sum(10, 10);
    console.log('sumRes in hot', sumRes);
  });
}

以上修改 math.js 才会触发热更新回调函数

8、DllPlugin 动态链接库插件

  • 前端框架如 vue React,体积大,构建慢
  • 较稳定,不常升级版本
  • 同一个版本只构建一次即可,不用每次都重新构建
  • webpback 已内置 DllPlugin 支持
  • DllPlugin - 打包出 dll 文件
  • DllReferencePlugin - 使用 dll 文件

配置 webpack.dll.js 文件

js
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
const { srcPath, distPath } = require('./paths');
module.exports = {
  mode: 'development',
  // JS 执行入口文件
  entry: {
    // 把 React 相关模块的放到一个单独的动态链接库
    react: ['react', 'react-dom'],
  },
  output: {
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    // 也就是 entry 中配置的 react 和 polyfill
    filename: '[name].dll.js',
    // 输出的文件都放到 dist 目录下
    path: distPath,
    // 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
    // 之所以在前面加上 _dll_ 是为了防止全局变量冲突
    library: '_dll_[name]',
  },
  plugins: [
    // 接入 DllPlugin
    new DllPlugin({
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      // 例如 react.manifest.json 中就有 "name": "_dll_react"
      name: '_dll_[name]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(distPath, '[name].manifest.json'),
    }),
  ],
};

在 package.json 配置执行的脚步,并执行 npm run dll 命令

js
{
  "scripts": {
    "dll": "webpack --config build/webpack.dll.js"
  },
}

使用打包出来的 dll 文件,配置 webpack.dev.js 文件

js
// 第一,引入 DllReferencePlugin
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = smart(webpackCommonConf, {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: ['babel-loader'],
        include: srcPath,
        exclude: /node_modules/, // 第二,不要再转换 node_modules 的代码
      },
    ],
  },
  plugins: [
    // 第三,告诉 Webpack 使用了哪些动态链接库
    new DllReferencePlugin({
      // 描述 react 动态链接库的文件内容
      manifest: require(path.join(distPath, 'react.manifest.json')),
    }),
  ],
});

最后在 index.html 引用

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

基于 MIT 许可发布