webpack5 高级

145 阅读9分钟

参考:

首页 | 尚硅谷 Web 前端之 Webpack5 教程 (yk2012.github.io)

学习资源:

Devtool | webpack 中文文档 (docschina.org)

Can I use... Support tables for HTML5, CSS3, etc

准确的代码错误提示(开发和生产模式)

webpack 配置文件下新增节点 devtool:此选项控制是否生成,以及如何生成 source map

source map:源代码映射,是一个用来生成源代码与构建后代码一一映射的文件的方案。

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

// 开发模式下常用
module.exports = {
  // 其他省略
  mode: "development",
  devtool: "cheap-module-source-map", // 打包编译速度快,只包含行映射
};

// 生产模式下常用
module.exports = {
  // 其他省略
  mode: "production",
  devtool: "source-map", // 包含行/列映射
};

提高打包构建速度

只打包编译修改的模块(开发模式下)

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

module.exports = {
  // 其他省略
  devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
    hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)
  },
};

让文件找到自己的 loader 后不再往下进行(开发和生产模式)

使用oneOf,不使用的情况下 打包时每个文件都会经过所有 loader 处理,虽然因为 test 正则原因实际没有处理上,但是都要过一遍。比较慢。

module.exports = {
  module: {
    rules: [
      {
        oneOf: [ // 只匹配一次
          {
            // 用来匹配 .css 结尾的文件
            test: /\.css$/,
            // use 数组里面 Loader 执行顺序是从右到左
            use: ["style-loader", "css-loader"],
          },
        ],
      },
    ],
  },
};

排除对某些文件进行编译处理(开发和生产模式下)

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

include:包含,只处理 xxx 文件
exclude:排除,除了 xxx 文件以外其他文件都处理

缓存 ESLint检查 和 Babel 编译结果,提高打包速度(开发和生产模式下)


// Babel
options: {
              cacheDirectory: true, // 开启babel编译缓存
              cacheCompression: false, // 缓存文件不要压缩
            },
            
// ESLint
 cache: true, // 开启缓存
 // 缓存目录
 cacheLocation: path.resolve(
   __dirname,
   "../node_modules/.cache/.eslintcache"
),

开启多进程打包(开发和生产模式下)

我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。 而对 js 文件处理主要就是 eslint 、babel、Terser 三个工具,所以我们要提升它们的运行速度。 我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。

注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。

下载 loader

-   thread-loader:开启多进程进行打包

减少代码体积

Tree Shaking

Tree Shaking 是一个术语,通常用于描述移除 JavaScript 中的没有使用上的代码。

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

减少Babel编译后的体积

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

下载插件 plugin:

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

图片压缩

  1. 下载包
-   image-minimizer-webpack-plugin:压缩图片
-   imagemin:默认情况下优化图像,可以处理所有类型的图像

还有剩下包需要下载,有两种模式:

  • 无损压缩
imagemin-gifsicle 
imagemin-jpegtran 
imagemin-optipng 
imagemin-svgo
  • 有损压缩
imagemin-gifsicle 
imagemin-mozjpeg 
imagemin-pngquant 
imagemin-svgo

优化代码运行性能

代码分割

代码分割(Code Split):

  1. 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
  2. 按需加载:需要哪个文件就加载哪个文件

代码分割实现方式有不同的方式:

  1. 多入口:配置了几个入口,至少输出几个 js 文件
  2. 提取重复代码:如果多入口文件中都引用了同一份代码,我们不希望这份代码被打包到两个文件中,导致代码重复,体积更大。我们需要提取多入口的重复代码,只打包生成一个 js 文件,其他文件引用它就好。
  3. 按需加载,动态导入:一旦通过 import 动态导入语法导入模块,模块就被代码分割,同时也能按需加载了
  4. 单入口:使用单入口+代码分割+动态导入方式来进行配置
  5. 给动态导入文件取名称
document.getElementById("btn").onClick = function () {
  // eslint会对动态导入语法报错,需要修改eslint配置文件
  // webpackChunkName: "math":这是webpack动态导入模块命名的方式
  // "math"将来就会作为[name]的值显示。
  import(/* webpackChunkName: "math" */ "./js/math.js").then(({ count }) => {
    console.log(count(2, 1));
  });
};

eslint 配置: 下载 plugin

-   eslint-plugin-import:解决动态导入import语法报错问题 --> 实际使用eslint-plugin-import的规则解决的
  1. 统一命名配置

Preload / Prefetch(预加载)

  1. 原因: 我们前面已经做了代码分割,同时会使用 import 动态导入语法来进行代码按需加载(我们也叫懒加载,比如路由懒加载就是这样实现的)。

但是加载速度还不够好,比如:是用户点击按钮时才加载这个资源的,如果资源体积很大,那么用户会感觉到明显卡顿效果。

我们想在浏览器空闲时间,加载后续需要使用的资源。我们就需要用上 Preload 或 Prefetch 技术。

  1. 介绍
  • Preload:告诉浏览器立即加载资源,只能加载当前页面需要使用的资源
  • Prefetch:告诉浏览器在空闲时才开始加载资源。可以加载当前页面资源,也可以加载下一个页面需要使用的资源。
  1. 下载包
-   @vue/preload-webpack-plugin

Network Cache

Core-js(js 兼容性处理)

  1. 原因:过去我们使用 babel 对 js 代码进行了兼容性处理,其中使用@babel/preset-env 智能预设来处理兼容性问题。它能将 ES6 的一些语法进行编译转换,比如箭头函数、点点点运算符等。但是如果是 async 函数、promise 对象、数组的一些方法(includes)等,它没办法处理。

  2. 介绍

  • core-js 是专门用来做 ES6 以及以上 API 的 polyfill。
  • polyfill翻译过来叫做垫片/补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性。
  1. ESlint 报错处理
  • 下载包
-  @babel/eslint-parser
  • .eslintrc.js 配置
  1. 使用 core-js
  • 下载包
-  core-js:兼容性补丁
  • 手动全部引入
import "core-js";
  • 手动按需引入
import "core-js/es/promise";
  • 自动按需引入 配置 babel 配置文件
module.exports = {
  // 智能预设:能够编译ES6语法
  presets: [
    [
      "@babel/preset-env",
      // 按需加载core-js的polyfill
      { useBuiltIns: "usage", corejs: { version: "3", proposals: true } },
    ],
  ],
};

PWA

  1. 原因:开发 Web App 项目,项目一旦处于网络离线情况,就没法访问了。我们希望给项目提供离线体验。
  2. 介绍:渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。其中最重要的是,在 离线(offline) 时应用程序能够继续运行功能。内部通过 Service Workers 技术实现的。
  3. 使用
  • 下载包
-   workbox-webpack-plugin

总结

我们从 4 个角度对 webpack 和代码进行了优化:

  1. 提升开发体验
  • 使用 Source Map 让开发或上线时代码报错能有更加准确的错误提示。
  1. 提升 webpack 提升打包构建速度
  • 使用 HotModuleReplacement 让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快。
  • 使用 OneOf 让资源文件一旦被某个 loader 处理了,就不会继续遍历了,打包速度更快。
  • 使用 Include/Exclude 排除或只检测某些文件,处理的文件更少,速度更快。
  • 使用 Cache 对 eslint 和 babel 处理的结果进行缓存,让第二次打包速度更快。
  • 使用 Thead 多进程处理 eslint 和 babel 任务,速度更快。(需要注意的是,进程启动通信都有开销的,要在比较多代码处理时使用才有效果)
  1. 减少代码体积
  • 使用 Tree Shaking 剔除了没有使用的多余代码,让代码体积更小。
  • 使用 @babel/plugin-transform-runtime 插件对 babel 进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小。
  • 使用 Image Minimizer 对项目中图片进行压缩,体积更小,请求速度更快。(需要注意的是,如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩。)
  1. 优化代码运行性能
  • 使用 Code Split 对代码进行分割成多个 js 文件,从而使单个文件体积更小,并行加载 js 速度更快。并通过 import 动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不加载资源。
  • 使用 Preload / Prefetch 对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好。
  • 使用 Network Cache 能对输出资源文件进行更好的命名,将来好做缓存,从而用户体验更好。
  • 使用 Core-js 对 js 进行兼容性处理,让我们代码能运行在低版本浏览器。
  • 使用 PWA 能让代码离线也能访问,从而提升用户体验。