UzumakiItachi
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

UzumakiItachi

起风了,唯有努力生存。
首页
  • JavaSript
  • Vue

    • Vue2
    • Vue3
  • React

    • React_18
  • WebPack
  • 浏览器相关
  • 工程化相关
  • 工作中遇到的问题以及解决方案
  • Git
  • 面试
  • 学习
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 个人产出
  • 实用工具
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • WebPack

    • WebPack热更新原理
    • 实现一个通用的WebPack配置包
    • WebPack打包多页面应用
    • WebPack构建速度以及体积优化策略
    • 手写一个简易的WebPack
    • 实现一个简易的webpack-loader
    • 实现一个简易的Webpack-plugin
    • 深入WebPack code splitting
      • 前言
      • SplitChunksPlugin
      • 动态import
      • 魔术注释
      • 总结
    • 实现一个合成雪碧图的loader
    • 实现一个压缩文件的plugin
    • webpack5都升级了哪些东西
    • 实现一个自动收集路由信息的webpack插件
  • 浏览器相关

  • 工程化相关

  • 工作中遇到的问题以及解决方案

  • Git

  • Vite

  • 一些小工具

  • 算法

  • 服务器

  • HTTP

  • 技术
  • WebPack
hanhanbuku
2023-05-19
目录

深入WebPack code splitting

# 前言

在默认情况下,webpack会将所有的资源都打入一个文件内,这样一来如果我们的项目很大,那就会导致最终生成的文件非常大。会大大的加长请求时间,导致用户体验不好。所以代码分割是webpack中非常重要的一个优化手段。下面就一起来看看该如果去做吧

# SplitChunksPlugin

SplitChunksPlugin是webpack4开始自带的一个开箱即用的工具,他可以灵活的按需分割代码块。

splitChunks: {
        // 表示选择哪些 chunks 进行分割,可选值有:async,initial 和 all
        chunks: "async",
        // 表示新分离出的 chunk 必须大于等于 minSize,20000,约 20kb。
        minSize: 20000,
        // 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块,仅在剩余单个 chunk 时生效
        minRemainingSize: 0,
        // 表示一个模块至少应被 minChunks 个 chunk 所包含才能分割。默认为 1。
        minChunks: 1,
        // 表示按需加载文件时,并行请求的最大数目。
        maxAsyncRequests: 30,
        // 表示加载入口文件时,并行请求的最大数目。
        maxInitialRequests: 30,
        // 强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略
        enforceSizeThreshold: 50000,
        // cacheGroups 下可以可以配置多个组,每个组根据 test 设置条件,符合 test 条件的模块,就分配到该组。模块可以被多个组引用,但最终会根据 priority 来决定打包到哪个组中。默认将所有来自 node_modules 目录的模块打包至 vendors 组,将两个以上的 chunk 所共享的模块打包至 default 组。
        cacheGroups: {
            defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    // 一个模块可以属于多个缓存组。优化将优先考虑具有更高 priority(优先级)的缓存组。
                    priority: -10,
                    // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
                    reuseExistingChunk: true,
            },
            default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
            }
        }
}
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 只会对异步调用的模块进行分割(chunks: "async"),并且默认情况下处理的 chunk 至少要有 20kb ,过小的模块不会被包含进去。

cacheGroups 缓存组是施行分割的重中之重,他可以使用来自 splitChunks.* 的任何选项,但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。默认配置中已经给我们提供了 Vendors 组和一个 defalut 组,**Vendors **组中使用 test: /[\/]node_modules[\/]/ 匹配了 node_modules 中的所有符合规则的模块。

Tip

当 webpack 处理文件路径时,它们始终包含 Unix 系统中的 /和 Windows 系统中的 \。这就是为什么在{cacheGroup}.test 字段中使用[\/]来表示路径分隔符的原因。{cacheGroup}.test 中的 /或 \会在跨平台使用时产生问题。

例如我们想要把项目中使用的某个库拆分出来,就可以这样配置

cacheGroups: {
  react: {
    name: 'react',
      test: /[\\/]node_modules[\\/](react)/,
      chunks: 'all',
      priority: -5,
  },
 },
1
2
3
4
5
6
7
8

更多的配置项可以查看官网详细配置 (opens new window)

# 动态import

使用import()方法,也可以将资源打入一个独立的chunk中,下面来看一下使用方法

// import { mul } from './test'
import $ from 'jquery'

import('./test').then(({ mul }) => {
    console.log(mul(2,3))
})

console.log($)
// console.log(mul(2, 3))

1
2
3
4
5
6
7
8
9
10

ps:import方法返回的是一个promise,所以如果有用到里面的东西记得在then回调中使用。

值得注意的是,这种语法还有一种很方便的`````“动态引用”```的方式,他可以加入一些适当的表达式,举个例子,假设我们需要加载适当的主题:

const themeType = getUserTheme();
import(`./themes/${themeType}`).then((module) => {
  // do sth aboout theme
});

1
2
3
4
5

这样我们就可以“动态”加载我们需要的异步模块,实现的原理主要在于两点:

  • 1.至少需要包含模块相关的路径信息,打包可以限定于一个特定的目录或文件集。
  • 2.根据路径信息 webpack 在打包时会把 ./themes 中的所有文件打包进新的 chunk 中,以便需要时使用到。

# 魔术注释

有些小伙伴可能会问了,使用splitChunks可以通过cache-group配置项来指定分割出来的chunk的名称,那通过import()可不可以指定名称呢?答案是 当然可以,下面就来看看在webpack中如何给动态导入的chunk指定名称吧

import(/* webpackChunkName: "my-chunk-name" */'./test')

1
2

只需要加上这样一段注释,webpack就会为这个chunk加上你自定义的名称 魔术注释不仅仅可以帮我们修改chunk 名这么简单,他还可以实现譬如预加载等功能,这里举个例子:

我们通过希望在点击按钮时才加载我们需要的模块功能,代码可以这样:

// index.js
document.querySelector('#btn').onclick = function () {
  import('./test').then(({ mul }) => {
    console.log(mul(2, 3));
  });
};

1
2
3
4
5
6
7
//test.js
function mul(a, b) {
  return a * b;
}
console.log('test 被加载了');
export { mul };

1
2
3
4
5
6
7

在我们点击按钮的同时确实加载了test.js 的文件资源。但是如果这个模块是一个很大的模块,在点击时进行加载可能会造成长时间 loading 等用户体验不是很好的效果,这个时候我们可以使用我们的 /* webpackPrefetch: true */ 方式进行预获取,来看下效果:

// index,js

document.querySelector('#btn').onclick = function () {
  import(/* webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(2, 3));
  });
};

1
2
3
4
5
6
7
8

整个过程中,在画面初始加载的时候,test.js 的资源就已经被预先加载了,而在我们点击按钮时,会从(prefetch cache) 中读取内容。这就是模块预获取的过程。另外我们还有/* webpackPreload: true */ 的方式进行预加载。

但是 prefetch 和preload听起来感觉差不多,实际上他们的加载时机等是完全不同的:

  • preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
  • preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
  • preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。

# 总结

以上就是webpack中对代码分割的基本实践,代码分割的主旨就是减少页面加载请求的资源体积过大导致体验不好的问题。当然也不要盲目的进行分割,因为代码分割虽然减少了文件体积,但同样的他也增加了文件的请求次数。所以一定要合理地进行优化,过度的以及不合适地优化可能会适得其反

编辑 (opens new window)
上次更新: 2023/05/22, 16:48:55
实现一个简易的Webpack-plugin
实现一个合成雪碧图的loader

← 实现一个简易的Webpack-plugin 实现一个合成雪碧图的loader→

最近更新
01
小程序实现全局状态管理
07-09
02
前端检测更新,自动刷新网页
06-09
03
swiper渲染大量数据的优化方案
06-06
更多文章>
Theme by Vdoing | Copyright © 2023-2025 UzumakiItachi | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式