webpack5打包性能优化

417 阅读6分钟

性能优化

打包代码分离

当我们很多文件,打包后都打包到了bundle.js一个文件当中,这肯定是不友好的,在浏览器加载bundle.js文件时,可能会耗费大量的时间。因此,我们可以将打包的文件进行分离,实现文件的懒加载效果。

1.多入口

配置多入口,打包时,会将文件打包到独立的文件当中。

2.入口依赖

如果在上例中,index.js和main.js都使用到了lodash这个第三方库,那么该库会被打包两次。

我们可以通过共享依赖来解决该问题。

  entry: {
    index:{import:'./src/index.js',dependOn:'shared'},
    main:{import:'./src/main.js',dependOn:'shared'},
    shared:['lodash']
  },

SplitChunks

通过splitChunks的分包模式来进行代码分离。

主要是通过optimization属性来配置优化。

配置地址:webpack.docschina.org/configurati…

optimization.chunkIds

告知 webpack 当选择模块 id 时需要使用哪种算法。将 optimization.chunkIds 设置为 false 会告知 webpack 没有任何内置的算法会被使用,但自定义的算法会由插件提供。

  • 如果环境是开发环境,那么 optimization.chunkIds 会被设置成 'named',但当在生产环境中时,它会被设置成 'deterministic'
'natural'按使用顺序的数字 id。
'named'对调试更友好的可读的 id。
'deterministic'在不同的编译中不变的短数字 id。有益于长期缓存。在生产模式中会默认开启。

cacheGroups

缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。将它们设置为 false以禁用任何默认缓存组。

optimization: {
  chunkIds:'named',// 
  splitChunks: {
    // include all types of chunks
    chunks: 'all', // 异步和通过加载的模块都进行分包
    cacheGroups: {
      Vendors: {
        test: /[\/]node_modules[\/]/,
        priority: -10,//优先级
        filename:'[id]_vendors.js'
      },
      default: {
        minChunks: 1,
        priority: -20,
        filename:'common_[name].js'
      },
    },
  },
},

cdn打包优化

项目代码打包配置

如果要将项目部署到cdn服务器上,那么要配置打包的publicPath;

output: {
  filename: '[name].bundle.js',
  path: path.resolve(__dirname, './dist'),
  chunkFilename: 'chunk_[id]_[name].js',
  publickPath:'' //cdn地址
},
第三方包打包优化

例如我们要cdn引入lodash和dayjs库。

  1. 配置webpack,不打包这两个库

webpack.config.js

//生产环境下配置
externals: {
  lodash: '_',
  dayjs: 'dayjs'
},
  1. 在模板index.html中手动引入cdn地址,通过ejs来判断是否为生产环境,只有在生产环境下才使用cdn引入。
 <% if(process.env.NODE_ENV === 'production'){   %>
    <script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<% } %>

常用的开源cdn服务:

国际上使用比较多的是unpkg、JSDelivr、cdnjs;

国内也有一个比较好用的CDN是bootcdn;

css打包优化

MiniCssExtractPlugin可以帮助我们将css提取到一个独立的css文件中。

安装依赖

npm install mini-css-extract-plugin -D

配置:

在webpack.com.config.js中使用插件

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
//使用插件
plugins: [
  new MiniCssExtractPlugin({
    filename: 'css/[name][contenthash:8].css',
    chunkFilename: 'css/[name][contenthash:8].css'
  })
],

在webpack.pro.config.js使用MiniCssExtractPlugin的loader

rules: [{
  test: /.css$/, // 匹配.css文件,
  exclude: /node_modules/,
  use: [
    MiniCssExtractPlugin.loader, //使用loader
    {
      loader: 'css-loader',
      options: {
        importLoaders: 1,
        // 0 => no loaders (default);
        // 1 => postcss-loader;
        // 2 => postcss-loader, sass-loader
      },
    },
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [require('postcss-preset-env')],
          
        },
      },
    },
  ], // 使用css-loader,style-loader
}, ]

在webpack.dev.config.js依旧使用style-loader。

rules: [{
  test: /.css$/, // 匹配.css文件,
  exclude: /node_modules/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        importLoaders: 1,
        // 0 => no loaders (default);
        // 1 => postcss-loader;
        // 2 => postcss-loader, sass-loader
      },
    },
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [require('postcss-preset-env')],

        },
      },
    },
  ], // 使用css-loader,style-loader
}, ]

最后,这样我们就完成了再生产环境时,将css打包到单独的文件的效果了。而在开发环境依旧是打包到style标签当中。

hash,contenthash,chunckhash

在我们给打包的文件进行命名的时候,会使用placeholder,placeholder中有几个属性比较相似:

hash、chunkhash、contenthash

hash本身是通过MD4的散列函数处理后,生成一个128位的hash值(32个十六进制);

hash值的生成和整个项目有关系:

比如我们现在有两个入口index.js和main.js;

它们分别会输出到不同的bundle文件中,并且在文件名称中我们有使用hash;

这个时候,如果修改了index.js文件中的内容,那么hash会发生变化;

那就意味着两个文件的名称都会发生变化;

chunkhash可以有效的解决上面的问题,它会根据不同的入口进行借来解析来生成hash值:

比如我们修改了index.js,那么main.js的chunkhash是不会发生改变的;

contenthash表示生成的文件hash名称,只和内容有关系:

比如我们的index.js,引入了一个style.css,style.css有被抽取到一个独立的css文件中;

这个css文件在命名时,如果我们使用的是chunkhash;

那么当index.js文件的内容发生变化时,css文件的命名也会发生变化;

这个时候我们可以使用contenthash;

案例:

output: {
  filename: '[name].[hash:8].bundle.js',
    path: path.resolve(__dirname, '../dist'),
      chunkFilename: 'chunk_[id]_[name].js',
}

plugins: [
  new MiniCssExtractPlugin({
    //打包的css文件名,只与自身内容有关
    filename: 'css/[name][contenthash:8].css',
    chunkFilename: 'css/[name][contenthash:8].css'
  })
]

建议使用contenthash,因为当内容不变时,打包后文件名改变了,那么浏览器就要重新加载该文件资源,会造成资源加载耗时。

css-minimizer-webpack-plugin 压缩css文件

主要用来去除无用的空格等。

安装:

npm install css-minimizer-webpack-plugin -D

在wepback.pro.config.js中配置:

module.exports = {
 optimization: {
        minimize: true,
        minimizer: [
            new CssminimizerPlugin()
        ]
    },
}

terser

github地址: github.com/terser/ters…

Terser是一个JavaScript的解释(Parser)、Mangler(绞肉机)/Compressor(压缩机)的工具集。

在webpack中,production环境默认就是使用terser-webpack-plugin对代码进行处理的。

我们也可以配置自己的想实现的功能。

案例:配置打包后去除console,debugger

module.exports = {
  optimization: {
    minimize: true,//自定义配置,minimize需要设置为true
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: {
            drop_debugger: true, //去除debugger
            drop_console: true //去除console
          }
        },
      }),
    ]
  },
}

tree shaking

tree shaking 用于消除我们项目无用的代码(没有使用的代码)。

有两种优化的手段:

  1. usedExports: 通过标记某些函数是否被使用,之后通过Terser来进行优化的;
  2. sideEffects: 跳过整个模块/文件,直接查看该文件是否有副作用;

这两个优化手段,在mode=production时,已经默认配置为true了。

一、usedExports

  optimization: {
        usedExports: true, //设置为true,会清除未使用的函数
        minimize: true,
        minimizer: [
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    compress: {
                        drop_debugger: true,
                        drop_console: true
                    },
                    keep_classnames: true,
                    keep_fnames: true,
                    toplevel: true
                },
            }),
            new CssminimizerPlugin()
        ]
    },

二、sideEffects

  optimization: {
        sideEffects: true, //设置为true
        minimize: true,
        minimizer: [
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    compress: {
                        drop_debugger: true,
                        drop_console: true
                    },
                    keep_classnames: true,
                    keep_fnames: true,
                    toplevel: true
                },
            }),
            new CssminimizerPlugin()
        ]
    },

package.json中配置sideEffects,配置有副作用的文件。那么打包的时候就不会清除这些模块。

"sideEffects": [
    "./src/js/xix.js",//规定有副作用
    "*.css"
  ],

对css进行tree shaking

使用purgecss可以实现对css的tree shaking. 主要用来清除我们没有使用到的css等。

如我们没有使用.title类,但是在css文件中编写了.title类,那么就可以进行tree shaking 对它进行清除。

安装插件:

npm install purgecss-webpack-plugin

配置插件:

optimization: {
  minimize: true,
    minimizer: [
      new PurgecssPlugin({
        //该路径下的文件都要进行tree shaking
        paths: glob.sync(`${resolveApp('./src')}/**/*`, { nodir: true }), 
        //配置白名单,如body,html的css属性不被清除
        safelist: function() {
          return {
            standard: ['body', 'html']
          }
        }
      })
    ]
}

HTTP压缩

当客户端请求资源时,服务端返回压缩后的代码,那么传输速率会更快。常见的压缩算法就是gzip.

接下来,我们通过webpack来实现代码的压缩。

安装compression-webpack-plugin 插件

配置plugin:

plugins: [
  new CompressionPlugin({
    test: /.(js|css)$/, //匹配js或css文件
  })
],

此时打包后的就有了相应的gzip文件了

HTML文件中代码的压缩

HtmlWebpackPlugin插件除了可以生成模板外,还可以对html文件进行压缩,去除空格和换行等。

new HtmlWebpackPlugin({
  // 模板文件
  template: './public/index.html',
  // 模板中使用到的title属性
  title: 'webpack项目',
  minify: true //压缩代码
})

事实上在mode为produciton时,minify就会自动设置为true,进行代码压缩。