webpack 之 性能优化篇

472 阅读5分钟

「本文正在参与技术专题征文Node.js进阶之路,点击查看详情

提升webpack打包速度

1、 升级打包工具的版本,跟上技术的迭代

  • 升级webpack、node、npm,yarn版本;

2、 在尽可能少的模块上使用loader; 降低loader作用范围,可以提升性能。

  • 添加exclude:/node_modules/,避免node_modules下面的js模块使用loader
  • 或者 include

image.png

3、 尽量少的使用plugin,且使用靠谱的plugin

使用官方推荐的,社区验证过的的好用的插件。

比如在开发环境dev环境

  • 不必使用MiniCssExtractPlugin插件提取css文件;
  • 不必使用OptimizeCssAssetsPlugin来压缩css文件;

4、 resolve 参数合理配置

webpack.js.org/configurati…

resolve参数合理配置,配置过多会造成性能问题

resolve参理配置示例

 resolve: { 
     // extensions 配置的时候一般只配置js之类的逻辑类的扩展名,对于css和json这种是不配置的。
     extensions: ['.js', '.jsx'], // 可以省略引入时的后缀名 
     mainFiles:['index', 'child'] // ./child/默认会引入index.js 
     alias: { // 模块别名
         // 默认导入child的时候会自动引入到配置
         child: path.resolve(__dirname, '../src/child') 
     } 
 }

extensions

如果多个文件共享相同的名称但具有不同的扩展名,webpack 将解析具有数组中第一个列出的扩展名的文件并跳过其余文件

mainFiles

解析目录时要使用的文件名。一般情况下都不会配置多个。。。

alias

创建 import 或 require 的别名,来确保模块引入变得更简单

5、使用 DllPlugin 提高打包速度

DllPlugin 和 DllReferencePlugin 用某种方法实现了拆分 bundles,同时还大幅度提升了构建的速度。"DLL" 一词代表微软最初引入的动态链接库。

目标:第三方模块只打包一次

每次打包的时候,第三方模块(lodash、react)都需要从node_modules取出模块,然后在打包到源代码中。

我们在第一次打包的时候,将第三方模块打包代码存起来,之后直接用。

1)配置webpack.dll.js

将第三方文件单独打包生成dll.js,再用library通过全局变量的方式暴露出去;

// webpack.dll.js
module.exports = {
    entry: {
        vendor: [
            'react',
            'react-dom',
            'react-router',
            'redux',
            'react-redux',
            'redux-thunk',
            'superagent',
            'classnames',
            'prop-types',
            'pubsub-js',
            'rxjs',
            'lodash',
        ],
    },
    output: {
        // filename: '[name]_dll_[chunkhash].js',
        path: path.join(__dirname, '..', 'assets', 'dll'),
        library: '_dll_[name]', // 把第三方的代码通过全局变量的方式暴露出去
    },
};

// package.json
{
    //...
    "scripts": {
         //...
        "dll": "cross-env NODE_ENV=production webpack --config webpack/webpack.dll.js --progress",
    }
}

运行npm run dll这样我们的第三方包 打包就打包到 dll 目录下

接下来就需要 把我们打包生成的dll 文件引入到项目中

需要使用到 add-asset-html-webpack-plugin

2) add-asset-html-webpack-plugin

借助addAssetHtml插件是往html文件中插入已经打包好的dll.js 文件

add-asset-html-webpack-plugin 往静态资源添加内容

  1. 安装 npm install add-asset-html-webpack-plugin --save
  2. 使用 AddAssetHtmlPlugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const webpackConfig = {
  entry: 'index.js',
  output: {
    path: 'dist',
    filename: 'index_bundle.js',
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new AddAssetHtmlPlugin({ 
        filepath: require.resolve('./dll/vendors.dll.js') 
    }),
  ],
};

3) new webpack.DllPlugin()

借助DllPlugin插件对dll.js文件进行分析,生成mainfest.josn映射文件

 plugins: [
     new webpack.DllPlugin({
         name: '[name]',
         path: path.resolve(__dirname, '../dll/[name].manifest.json')
     }) // 映射文件
 ]
)

image.png

4) new webpack.DllReferencePlugin()

2)配置webpack.common.js (借助DllReferencePlugin插件进行分析,第二次打包直接取之前的dll.js )

文件使用引入模块时,使用引入dll中模块

 // wbapack.common.js
 new webpack.DllReferencePlugin({ 
     manifest: ''// 路径 
 })

image.png

第三方模块的dll node批量化插件配置

在大型项目中,我们生成的第三方模块的dll.js文件会很多,如何动态添加plugin?

如下图有两套dll文件,还可能更多

image.png

此时一个个配置,就显得特别的麻烦,如下图这样

image.png

这里我们可以使用Node.js 批量化插件配置

const plugins = [];
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));

files.forEach(file => {
    if (/.*\.dll.js/.test(file)) {
        plugins.push(new AddAssetHtmlWebpackPlugin({
            filepath: path.resolvel(__dirname, "../dll", file)
        })
    } 
    
    if (/.*\.mainfest.js/.test(file)) {
        plugins.push(new DllReferencePlugin({
            filepath: path.resolvel(__dirname, "../dll", file)
        })
    } 
})

image.png

6、控制包文件大小

使用文件压缩

treeShaking或者optimizion.splitChunks分割代码

7、多进程打包

webpack 4 的版本 默认是单进程打包,

可以用插件thread-loaderparallel-webpackhappypack等多进程打包;

happyPack 多进程打包

 const HappyPack = require('happypack')
    
// 步骤1 module.rules下-将对.js的文件交给id为label的HappyPack实例
{
  test: /\.js$/,
  use: ['happypack/loader?id=babel']
}

// 步骤2 happyPack开启多进程打包
new HappyPack({
 // 用唯一的标识符 id 来代表当前的HappyPack 是用来处理一类特定的文件
 id: 'babel',
 // 如何处理.js 文件,用法和Loader 配置一样
 loaders: ['babel-loader?cacheDirectory']
})

thread-loader

webpack.js.org/loaders/thr…

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

8、 合理使用sourceMap:

不同的环境使用不同的sourceMap配置来生成代码调试的文件

一方面能发现代码中出错的位置

另一方面还能提高打包时间

9、结合stats分析打包结果

使用weback-bundle-anlyzer插件可以分析打包结果;通过分析打包流程,可以优化打包;

10、开发环境内存编译:

使用webpackDevServer,打包出的文件放在内存中

11、开发环境无用插件剔除:

开发环境mode设置为development

12、ParallelUglifyPlugin 多进程压缩JS

  • webpack 内置 Uglify 工具压缩JS
  • JS单线程,开启多进程压缩更快
  • 和happyPack 同理
    // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
    const ParallelUglifyPlugin = require('ParallelUglifyPlugin')
    
    new ParallelUglifyPlugin({
      // 传递给 UglifyJS 的参数
      // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
      uglifyJS: {
        output: {
          beautify: false, // 最紧凑的输出
          comments: false // 删除所有的注释
        },
        compress: {
            // 删除所有的 `console` 语句,可以兼容ie浏览器
            drop_console: true,
            // 内嵌定义了但是只用到一次的变量
            collapse_vars: true,
            // 提取出出现多次但是没有定义成变量去引用的静态值
            reduce_vars: true
         }
      }
    })
复制代码

关于开启多进程

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