webpack5 优化

129 阅读6分钟

介绍

参考尚硅谷课程: www.bilibili.com/video/BV14T…

所谓高级配置其实就是进行Webpack优化,让我们的代码在编译/运行时性能更好~ 一般会从以下角度进行优化:

  1. 提升开发体验
  2. 提升打包构建速度
  3. 减少代码体积
  4. 优化代码运行性能

1.提升开发体验

SourceMap

为什么

开发时我们经过Weboack编译后的代码,css和js合并成了一个文件,多了很多其他代码。此时如果代码运行出现错误,提示代码错误位置我们是看不懂的。一旦开发代码文件很多的情况下,很难去发现错误出现在哪里

是什么

SourceMap是用来生成源代码与侯建后代码一一映射的文件的方案 它会生成一个xxx.map文件,里面包含源代码和构建后代码每一行,每一列的映射关系。当构建后代码出错了,会通过xxx.map文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快找到错误根源

怎么用

通过查看Webpack DevTool文档可知,SourceMap值有很多种情况 但实际开发时我们只需要关注两种情况即可:

  • 开发模式:cheap-module-source-map

优点:打包编译速度快,只包含行映射

缺点:没有列映射

module.exports={
    mode:'development',
    devtool:'cheap-module-source-map'
}
  • 生产模式:source-map

优点:包含行/列映射

缺点:打包编译速度更慢

module.exports={ 
    mode:'development', 
    devtool:'source-map'
}

2.提升打包构建速度

HotModuleReplacement

为什么

开发时我们修改了其中一个模块代码,Webpakc默认会将所有模块全部重新打包编译,速度很慢 所以我们需要做到修改某个模块代码,就只有这个模块代码进行重新打包编译,其他模块不变,这样打包速度就能变快

是什么

HotModuleReplacement(HMR/热模块替换):在程序运行中,替换,添加或删除模块,而无需重新加载整个页面

怎么用

1.基本配置

module.exports={
     devServer: {
        host: 'localhost',
        port: '3000',
        open: true,//是否自动打开浏览器,
        hot:true, //开启HMR功能,(只能用于开发环境,生产环境不需要)
  },
}

Include/Exclude

为什么

开发时我们需要使用第三方库或插件,所有文件都下载到了node_modules中了,而这些文件是不需要编译可以直接使用的。 所以我们在对js文件处理时,要排除node_modules里面的文件

是什么

  • Include 包含,只处理xxx文件
  • Exclude 排除,除了xxx文件以外都处理

怎么用

同时只能使用一种

          {
            test: /\.js$/,
            exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
            //include: path.resolve(__dirname, '../src'),//只处理src下的文件,其他的不处理
            loader: 'babel-loader',
            //可以单独写babel.config.js去配置
              options: {
                  presets: [
                    "@babel/preset-env"
                  ]
                }
          }
    new ESLintPlugin({
      //检测哪些文件
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules' //默认值
    }),

Cache

为什么

每次打包时js都要经过Eslint检查和Babel编译,速度比较慢 我们可以缓存之前的Eslint和Babel编译结果,这样第二次打包时速度就会更快了

是什么

对Eslint检查和Babel编译结果进行缓存

怎么用

Eslint:

          {
            test: /\.js$/,
            exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
            include: path.resolve(__dirname, '../src'),//只处理src下的文件,其他的不处理
            loader: 'babel-loader',
            // 可以单独写babel.config.js去配置
            options: {
              presets: [
                "@babel/preset-env"
              ],
              cacheDirectory: true,//开启babel缓存
              cacheCompression: false,//关闭缓存文件压缩
            }
          }

Babel:

    new ESLintPlugin({
      //检测哪些文件
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules',//默认值
      cache: true,
      cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache')
    }),

注意:此时打包的时候,会报一个关于oneof的错误,不清楚是哪里冲突了,于是先把oneof注释了,回头再解决

Thead

为什么

当项目越来越庞大时,打包速度越来越慢,甚至需要一个下午才能打包出来代码。这个速度是比较慢的 我们想要继续提升打包速度,其实就是要提升js的打包速度,因为其他文件都比较少 而对js文件处理主要就是eslint、babel、Terser三个工具,所以我们要提升它们的运行速度 我们可以开启多进程同时处理js文件,这样速度就比之前的单进程打包更快了

是什么

多进程打包:开启电脑的多个进程同时干一件事,速度更快

怎么用

我们启动继承的数量就是我们CPU的核数 1.如何获取CPU核数,因为每个电脑都不一样

//nodejs核心模块,直接使用
const os =require('os')
//cpu核数
const threads=os.cpus().length

2.下载包:npm i thread-loader -D 3.使用

          {
            test: /\.js$/,
            exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
            use: [
              {
                loader: 'thread-loader',//开启多进程
                options: {
                  works: threads
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true,//开启babel缓存
                  cacheCompression: false,//关闭缓存文件压缩
                }
              }
            ]
          }

Eslint开启多进程

    new ESLintPlugin({
      //检测哪些文件
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules',//默认值
      cache: true,
      cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),
      threads,//开启多进程
    }),

3.减少代码体积

Tree Shaking

为什么

开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库 如果没有特殊处理的话,我们打包时会引入整个库,但实际上我们可能只用上极小部分功能 这样整个库都打包进来的话,体积太大

是什么

Tree Shaking是一个术语,同程用于描述JavaScript中没有使用上的代码

注意:它依赖ES Moudle

怎么用

Webpack已经默认开启了这个功能,无需其他配置

Babel

为什么

Babel 为编译的每个文件都插入了辅助代码,使代码体积过大! Babel 对一些公共方法使用了非常小的辅助代码,比如_extend。默认情况下会被添加到每一个需要它的文件中 你可以将这些辅助代码作为一个独立模块,来避免重复引入

是什么

@babel/plugin-transform-runtime:禁用了Babel自动对每个文件的runtime注入,而是引入@babel/plugin-transform-runtime 并且使所有辅助代码从这里引用

怎么用

下载包:npm i @babel/plugin-transform-runtime -D

          {
            test: /\.js$/,
            exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
            use: [
              {
                loader: 'thread-loader',//开启多进程
                options: {
                  works: threads
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true,//开启babel缓存
                  cacheCompression: false,//关闭缓存文件压缩
                  plugins: ['babel/plugin-transform-runtime'],//减少代码体积
                }
              }
            ]
          }

Imgae Minimizer

为什么

开发中如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢 我们可以对图片进行压缩,减少图片体积 注意:如果项目中图片都是在线链接,那就不需要了。本地项目静态图片才需要进行压缩

是什么

imgae-minimizer-webpack-plugin:用来压缩图片的插件

怎么用

1.下载包 npm i imgae-minimizer-webpack-plugin imagemin -D 还有剩下包需要下载,有两种模式:

  • 无损压缩 npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
  • 有损压缩 npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D 无损/有损的区别

4.优化代码运行性能

Code Split

为什么

打包代码时会将所有js文件打包到一个文件中,体积太大了。如果我们只要渲染首页,就应该只加载首页的js文件,其他我呢间不应该加载 所以我们需要将打包生成的文件进行代码分割,生成多个js文件,渲染哪个页面就只加载某个js文件,这样加载的资源就少,速度就更快

是什么

代码分割(Code Split)主要做了两件事: 1.分割文件:将打包生成的文件进行分割,生成了多个js文件 2.按需加载:需要哪个文件就加载哪个文件

怎么用

代码分割实现方式有不同的方式,为了更加体现它们之间的差异,我们会分别创建新的文件来演示

第一种多入口打包

  entry: {
    //有多个入口文件,多入口
    app: './src/app.js',
    main:'./src/main.js'
  },
  output: {
    //文件输出的路径
    //__dirname nodejs的变量,代表当前文件的文件夹目录
    path: path.resolve(__dirname, '../dist'),
    //入口文件打包的输出文件名
    filename: '[name].js',//webpack命名方式。[name]以文件名命名
    //打包前,自动清理上次打包的文件夹,并再次打包
    clean: true
  },

第二种方式optimization splitChunks 使用demo1的目录结构创建demo2,简单配置下webpack

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
  },
  mode: 'production',
};

optimization splitChunks的作用就是:

可以将mode_modules中代码单独打包到一个chunk最终输出 自动分析多入口chunk中有没有公共的文件,有的话就会将公共文件单独打包一个chunk(单入口情况下)

第三种方式使用js方法 在以下代码中加入imort()方法

function sum(...arg) {
  return arg.reduce((p, c) => p + c, 0);
}
import(/* webpackChunkName: 'test' */'./test').then(({ mul, count }) => {
  // 文件加载成功
  // eslint-disable-next-line
  console.log(mul(2, 5));
})
.catch(() => {
  // eslint-disable-next-line
  console.log('文件加载失败');
})

// eslint-disable-next-line no-console
console.log(sum(2, 3));