webpack进阶

webpack 进阶

参考:

三个核心概念

webpack 中的三个概念 module、chunk 和 bundle

= module:就是 js 的模块化 webpack 支持 commonJS、ES6 等模块化规范,简单来说就是你通过 import 语句引入的代码。

= chunk: chunk 是 webpack 根据功能拆分出来的,包含三种情况:

    1、你的项目入口(entry)

    2、通过 import()动态引入的代码

    3、通过 splitChunks 拆分出来的代码

    4chunk 包含着 module,可能是一对多也可能是一对一。

= bundle:bundle 是 webpack 打包之后的各个文件,一般就是和 chunk 是一对一的关系,bundle 就是对 chunk 进行编译压缩打包等处理之后的产出。

= 参考《what are module,chunk and bundle in webpack》

1. 优化构建速度

1.1 构建费时分析

这里我们需要使用插件 speed-measure-webpack-plugin,我们参考文档配置一下

$ npm i -D speed-measure-webpack-plugin

但该插件兼容不好,需要低版本的 loader 和 plugin。** webpack5.x 中为了使用费时分析去对插件进行降级或者修改配置写法是非常不划算的**,平时开发中,建议还是不要使用。

1.2 优化 resolve 配置

1.2.1 alias 路径别名

alias 用的创建  import  或  require  的别名,用来简化模块引用。

项目中愉快的使用

1.2.2 extensions 忽略文件扩展名

webpack 默认的配置

也就是用户默认可以不用写 js、json、 wasm 文件的后缀。

  1. webpack 会按照 extensions 配置从左到右的顺序尝试去解析模块
  2.   所有高频文件后缀名放前面;
  3. 手动配置后,默认配置会被覆盖
    1. 如果想保留默认配置,可以用  扩展运算符代表默认配置,例如
**1.2.3 modules ** 告诉 webpack 解析模块时应该搜索的目录

常见配置如下

告诉 webpack 优先 src 目录下查找需要解析的文件,会大大节省查找时间

1.2.4 resolveLoader 主要用于解析自己写得 loader

resolveLoader 与上面的  resolve  对象的属性集合相同, 但仅用于解析 webpack 的  loader  包。

一般情况下保持默认配置就可以了,但如果你有自定义的 Loader 就需要配置一下,不配可能会因为找不到 loader 报错

  • 例如:我们在 loader 文件夹下面,放着我们自己写的 loader

**1.3 externals **

排除不参与构建的包

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

externals  配置选项提供了「从输出的 bundle 中排除依赖」的方法。此功能通常对  library 开发人员(就是写库的) 来说是最有用的,然而也会有各种各样的应用程序用到它。

例如,从 CDN 引入 jQuery,而不是把它打包:

  1. 引入链接 ,(intergritycrossorigin)

  1. 配置 externals

  1. 使用 jQuery

我们可以用这样的方法来剥离不需要改动的一些依赖,大大节省打包构建的时间。

1.4 loader 更细致的匹配方式

  • include:符合条件的模块进行解析
  • exclude:排除符合条件的模块,不解析

**1.5 noParse **

不解析

  • 不需要解析依赖的第三方大型类库等,可以通过这个字段进行配置,以提高构建速度
  • 使用 noParse 进行忽略的模块文件中不会解析  importrequire  等语法

1.6 IgnorePlugin

防止在  import  或  require  调用时,生成以下正则表达式匹配的模块

  • resourceRegExp  匹配(test)资源请求路径的正则表达式。
  • contextRegExp  匹配(test)资源上下文(目录)的正则表达式。

配置 IgnorePlugin

目的是将插件中的非中文语音排除掉,这样就可以大大节省打包的体积了 ?

1.7 多进程配置

注意:实际上在小型项目中,开启多进程打包反而会增加时间成本,因为启动进程和进程间通信都会有一定开销。

1.7.1 thread-loader

配置在 thread-loader 之后的 loader 都会在一个单独的 worker 池(worker pool)中运行

-安装

$ npm i -D thread-loader

-配置

1.7.2** happypack **❌

同样为开启多进程打包的工具,webpack5 已弃用。

1.8 利用缓存

利用缓存可以大幅提升重复构建的速度

1.8.1 **开启  ****babel-loader 缓存**
  • babel 在转译 js 过程中时间开销比价大,将 babel-loader 的执行结果缓存起来,重新打包的时候,直接读取缓存
  • 缓存位置: node_modules/.cache/babel-loader
  • 配置:

1.8.2 cache-loader 缓存其他 loader

  • 缓存一些性能开销比较大的 loader 的处理结果
  • 缓存位置:node_modules/.cache/cache-loader
  • 安装  $ npm i -D cache-loader
  • 配置:
1.8.3 hard-source-webpack-plugin
  • hard-source-webpack-plugin  为模块提供了中间缓存,重复构建时间大约可以减少 80%,但是在  webpack5 中已经内置了模块缓存,不需要再使用此插件
1.8.4 cache 持久化缓存 真的变快很多
通过配置** cache **缓存生成的 webpack 模块和 chunk,来改善构建速度。

**1.8.5 dll ****❌**

在 webpack5.x 中已经不建议使用这种方式进行模块缓存,因为其已经内置了更好体验的 cache 方法

2. 优化构建结果

2.1 构建结果分析

借助插件 webpack-bundle-analyzer 我们可以直观的看到打包结果中,文件的体积大小、各模块依赖关系、文件是够重复等问题,极大的方便我们在进行项目优化的时候,进行问题诊断。

  1. 安装

$ npm i -D webpack-bundle-analyzer

  1. 配置插件

  1. 修改启动命令

2.2 压缩 CSS / 压缩 JS

在生成环境下打包默认会开启 js 压缩,但是当我们手动配置 optimization 选项之后,就不再默认对 js 进行压缩,需要我们手动去配置。

  1. css 安装  optimize-css-assets-webpack-plugin
  2. 因为 webpack5 内置了terser-webpack-plugin 插件,所以我们不需重复安装

2.3 清除无用的 CSS

purgecss-webpack-plugin 会单独提取 CSS 并清除用不到的 CSS

$ npm i -D purgecss-webpack-plugin

添加配置

2.4 Tree-shaking

Tree-shaking 作用是剔除没有使用的代码,以降低包的体积

webpack 默认支持,需要在 .bablerc 里面设置  model:false,即可在生产环境下默认开启

了解更多 Tree-shaking 知识,推荐阅读 👉🏻 从过去到现在,聊聊 Tree-shaking

2.5 Scope Hoisting

Scope Hoisting 即作用域提升,原理是将多个模块放在同一个作用域下,并重命名防止命名冲突,通过这种方式可以减少函数声明和内存开销

  • webpack 默认支持,在生产环境下默认开启
  • 只支持 es6 代码

3. 优化运行时体验

运行时优化的核心就是提升首屏的加载速度,主要方式就是

  • 降低首屏加载文件体积,首屏不需要的文件进行预加载或者按需加载

3.1 入口点分割

配置多个打包入口,多页打包,这里不过多介绍

3.2 splitChunks 分包配置

optimization.splitChunks 是基于 SplitChunksPlugin 插件实现的

默认情况下,它只会影响到按需加载的 chunks,因为修改 initial chunks 会影响到项目的 HTML 文件中的脚本标签。

webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于  node_modules  文件夹
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积)
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30
  1. 默认配置介绍

  1. 项目中的使用。配置后开发环境可能打包无效。生产环境时可以的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
splitChunks: {
cacheGroups: { // 配置提取模块的方案
default: false,
styles: {
name: 'styles',
test: /\.(s?css|less|sass)$/,
chunks: 'all',
enforce: true,
priority: 10,
},
common: {
name: 'chunk-common',
chunks: 'all',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
enforce: true,
reuseExistingChunk: true,
},
vendors: {
name: 'chunk-vendors',
test: /[\/]node_modules[\/]/,
chunks: 'all',
priority: 2,
enforce: true,
reuseExistingChunk: true,
},
// ... 根据不同项目再细化拆分内容
}
}

    3. 使用魔法注释 设置chunkName

     = chunkName 可以相同,打包出来后,设置了相同 chunkName 的 import 文件会一起编译为一个 bundle

3.3 代码懒加载

针对首屏加载不太需要的一些资源。

desc.js

index.js

= 使用**import() **实现懒加载,通常用于懒加载组件。

import() 返回一个 promise。 可以传递一个函数,其参数default是 import 文件中  exports  的导出。

3.4 prefetch 与 preload

⚠️ 魔法注释可能由于不知名原因失效,暂时无解。 或许是 webpack5 的问题

当需要异步加载的文件比较大时,想上面那样点击的时候去加载也会影响到我们的体验,这个时候我们就可以考虑使用 prefetch 来进行预拉取

3.4.1 prefetch
  • prefetch (预获取):浏览器空闲的时候进行资源的拉取

改造一下上面的代码

3.4.2 preload
  • preload (预加载):提前加载后面会用到的关键资源。可以理解为和同步资源一起加载
  • ⚠️ 因为会提前拉取资源,如果不是特殊需要,谨慎使用

官网示例:


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!