面试官:谈一谈Webpack5有哪些性能优化?

610 阅读4分钟

为什么需要优化

不管是为了面试还是开发,作为一个前端佬,webpack优化都是非常重要的事情。不仅可以提高用户的体验,还可以减轻服务器的压力

一、提升开发体验优化

sourceMap源代码映射

开发时我们的代码都是经过webpack编译后的。一旦项目体积大起来。如果此时代码运行出错,它只会提示编译后的代码,而编译后的代码是很难查看错误的。编译后代码示例如下:

image.png

那么怎么查看源代码的错误呢。正所谓有需求就有市场。这时候sourceMap就应运而生了。

sourceMap是一个用来生成源代码到构建后代码的映射。它会生成一个map文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过map文件,从构建后的代码出错位置找到源代码的出错位置。从而让开发者更快的进行错误溯源。

webpack官方文档提供了很多devtool,不同的devtool构建和重新构建的速度也不一样

实际开发中我们只需关注两种:

  • 开发模式:cheap-module-source-map
    • 优点:构建速度快(相较于source-map),只包含行映射
    • 缺点:没有列映射
module.exports = {
    ...
    mode: 'development',
    devtool: 'cheap-module-source-map'
}
  • 生产模式:source-Map
    • 优点:包含行映射和列映射
    • 缺点:构建速度慢
module.exports = {
    ...
    mode: 'production',
    devtool: 'source-map'
}

二、提高打包构建速度

HMR热更新

开发时我们修改了其中一个模块代码,webpack会默认将所有模块进行打包编译,速度很慢。 如果想做到按需打包,可以使用HMR。一般只用于开发模式

module.exports = {
    ...
    mode: 'development',
    devServer: {
        host: 'localhost',
        port: 3000,
        open: true, //是否自动打开浏览器
        hot: true, //是否启用热更新
    }
}

但是当JS改变是不会热更新的,如果需要热更新,可以在入口文件添加这段语句

if(module.hot) { //如果浏览器支持热更新的话
    module.hot.accept('xxx') //xxx是需要做热更新的JS文件的路径
}

当然如果文件多的话,这样写会很麻烦,替换方案就是使用vue-loaderreact-hot-loader

特别注意: 从 webpack-dev-server v4.0.0 开始,热模块替换是默认开启的。所以现在是默认开启热更新的,即使你不配置!

oneOf

当进行规则匹配时,会从我们写的rules从头找到尾,这样明显拖慢了打包速度。oneOf就是当匹配时,只使用第一个匹配到的规则

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /.css$/,
        oneOf: [
          {
            resourceQuery: /inline/, // foo.css?inline
            use: 'url-loader',
          },
          {
            resourceQuery: /external/, // foo.css?external
            use: 'file-loader',
          },
        ],
      },
    ],
  },
};

include / exclude

  • include: 需要处理的文件
  • exclude: 不需要处理的文件

实际上node_modules中的文件是不需要进行打包的,这些文件已经被作者编译过了!我们不需要做无用功了!

module.exports = {
    ...
    //使用include来指定编译文件夹
    include: path.resolve(__dirname, '../src'),
    //使用exclude排除指定文件夹
    exclude: /node_modules/
}

注意:这两种方式只能用其中一种,否则会报错!

Cache

每次打包时,重新打包资源会比较慢,我们可以缓存资源,提高二次构建的速度。一般是对JS文件进行语法检查和babel编译耗费的时间比较多。所以主要对这里eslint和babel产生的文件进行缓存

对babel进行缓存

{
    test: /\.(js)$/,
    include: path.resolve(__dirname,'../src'),
    loader: 'babel-loader',
    options: {
        cacheDirectory: true, //开启babel缓存
        cacheCompression: false //关闭缓存文件压缩。development模式下默认为 false,production模式默认为gzip
    }
}

对eslint进行缓存

plugins: [
    new ESLintPlugin({
        context: path.reslove(__dirname,'../src'),
        cache: true,
        cacheLocation: path.reslove(__dirname,'../node_modules/cache/eslintCache') //缓存文件存放位置
    })
]

多进程打包

使用多进程打包,可以有效的提高打包的速度

我们能启动的最大的核数取决于我们的电脑,运行以下代码可以查看电脑的内核数量

const os = require('os')

console.log(os.cpus().length) //我的电脑是12

下载

npm install --save-dev thread-loader

使用

use: [
    {
        loader: 'thread-loader',
        works: 12 //进程数量
    },
    ...
]

三、减小代码体积

Tree Shaking

通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念实际上是由 ES2015 模块打包工具 rollup普及起来的。

目前,Webpack5已经默认开启了此功能无需进行配置

减小babel生成文件的体积

babel为编译的每个文件都插入了辅助代码,使得代码体积过大!

babel对一些公共方法使用了非常小的辅助代码,比如_extend,默认情况下会被添加到每一个需要它的文件中

你可以将这些辅助代码作为一个独立模块,来避免重复引入

下载

npm i @babel/plugin-transform-runtime -D

使用

{
    test: /\.(js)$/,
    include: path.resolve(__dirname,'../src'),
    loader: 'babel-loader',
    options: {
        cacheDirectory: true, //开启babel缓存
        cacheCompression: false //关闭缓存文件压缩。development模式下默认为 false,production模式默认为gzip
        plugins: ['@babel/plugin-transform-runtime']
    }
}

压缩图片

开发中如果项目中引用了很多图片,将来请求速度会比较慢。 所以我们可以对图片进行压缩,减小图片体积。如果引入的是在线链接,就不需要压缩了

下载

npm i image-minimizer-webpack-plugin imagemin -D

无损压缩

npm i imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

有损压缩

npm i imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

使用

const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin"); //引入插件

new ImageMinimizerPlugin({
    minimizer: {
      implementation: ImageMinimizerPlugin.imageminGenerate,
      options: {
        plugins: [
          ["gifsicle", { interlaced: true }],
          ["jpegtran", { progressive: true }],
          ["optipng", { optimizationLevel: 5 }],
          [
            "svgo",
            {
              plugins: [
                "preset-default",
                "prefixIds",
                {
                  name: "sortAttrs",
                  params: {
                    xmlnsOrder: "alphabetical",
                  },
                },
              ],
            },
          ],
        ],
      },
    },
  }),