由浅至深了解webpack异步加载背后的原理

本文详细介绍了webpack的异步加载和分包原理,包括相关概念如module、chunk和bundle,以及它们的区别。文章阐述了使用异步加载的好处,如利用浏览器缓存、加快加载速度和节省流量。此外,还讲解了webpack的分包配置,强调了配置项如`chunkhash`和` CommonsChunkPlugin`的作用,并讨论了`jsonpFunction`的命名问题。最后,通过实例分析了webpack如何实现异步加载,解析了打包后的代码结构和执行流程。
摘要由CSDN通过智能技术生成

源自最近对业务项目进行 webpack 异步分包加载一点点的学习总结

提纲如下:

  • 相关概念
  • webpack 分包配置
  • webpack 异步加载分包如何实现
相关概念
  • module、chunk、bundle 的概念

先来一波名词解释。先上网上一张图解释:


通过图可以很直观的分出这几个名词的概念:

1、module:我们源码目录中的每一个文件,在 webpack 中当作module来处理(webpack 原生不支持的文件类型,则通过 loader 来实现)。module组成了chunk
2、chunkwebpack打包过程中的产物,在默认一般情况下(没有考虑分包等情况),x 个webpackentry会输出 x 个bundle
3、bundlewebpack最终输出的东西,可以直接在浏览器运行的。从图中看可以看到,在抽离 css(当然也可以是图片、字体文件之类的)的情况下,一个chunk是会输出多个bundle的,但是默认情况下一般一个chunk也只是会输出一个bundle

  • hashchunkhashcontenthash

这里不进行 demo 演示了,网上相关演示已经很多。

hash。所有的 bundle 使用同一个 hash 值,跟每一次 webpack 打包的过程有关

chunkhash。根据每一个 chunk 的内容进行 hash,同一个 chunk 的所有 bundle 产物的 hash 值是一样的。因此若其中一个 bundle 的修改,同一 chunk 的所有产物 hash 也会被修改。

contenthash。计算与文件内容本身相关。

tips:需要注意的是,在热更新模式下,会导致chunkhashcontenthash计算错误,发生错误(Cannot use [chunkhash] or [contenthash] for chunk in '[name].[chunkhash].js' (use [hash] instead) )。因此热更新下只能使用hash模式或者不使用hash。在生产环境中我们一般使用contenthash或者chunkhash

说了这么多,那么使用异步加载/分包加载有什么好处呢。简单来说有以下几点

1、更好的利用浏览器缓存。如果我们一个很大的项目,不使用分包的话,每一次打包只会生成一个 js 文件,假设这个 js 打包出来有 2MB。而当日常代码发布的时候,我们可能只是修改了其中的一行代码,但是由于内容变了,打包出来的 js 的哈希值也发生改变。浏览器这个时候就要重新去加载这个 2MB 的 js 文件。而如果使用了分包,分出了几个 chunk,修改了一行代码,影响的只是这个 chunk 的哈希(这里严谨来说在不抽离 mainifest 的情况下,可能有多个哈希也会变化),其它哈希是不变的。这就能利用到 hash 不变化部分代码的缓存

2、更快的加载速度。假设进入一个页面需要加载一个 2MB 的 js,经过分包抽离后,可能进入这个页面变成了加载 4 个 500Kb 的 js。我们知道,浏览器对于同一域名的最大并发请求数是 6 个(所以 webpack 的maxAsyncRequests默认值是 6),这样这个 4 个 500KB 的 js 将同时加载,相当于只是穿行加载一个 500kb 的资源,速度也会有相应的提高。

3、如果实现的是代码异步懒加载。对于部分可能某些地方才用到的代码,在用到的时候才去加载,也能很好起到节省流量的目的。

webpack 分包配置

在这之前,先强调一次概念,splitChunk,针对的是chunk,并不是module。对于同一个 chunk 中,无论一个代码文件被同 chunk 引用了多少次,它都还是算 1 次。只有一个代码文件被多个 chunk 引用,才算是多次。

webpack 的默认分包配置如下

module.exports = {
   
  optimization: {
   
    splitChunks: {
   
      // **`splitChunks.chunks: 'async'`**。表示哪些类型的chunk会参与split。默认是异步加载的chunk。值还可以是`initial`(表示入口同步chunk)、`all`(相当于`initial`+`async`)。
      chunks: "async",
      // minSize 表示符合代码分割产生的新生成chunk的最小大小。默认是大于30kb的才会生成新的chunk
      minSize: 30000,
      // maxSize 表示webpack会尝试将大于maxSize的chunk拆分成更小的chunk,拆解后的值需要大于minSize
      maxSize: 0,
      // 一个模块被最少多少个chunk共享时参与split
      minChunks: 1,
      // 最大异步请求数。该值可以理解为一个异步chunk,被抽离出同时加载的chunk数不超过该值。若为1,该异步chunk将不会抽离出任意代码块
      maxAsyncRequests: 5,
      // 入口chunk最大请求数。在多entry chunk的情况下会用到,表示多entry chunk公共代码抽出的最大同时加载的chunk数
      maxInitialRequests: 3,
      // 初始chunk最大请求数。
      // 多个chunk拆分出小chunk时,这个chunk的名字由多个chunk与连接符组合成
      automaticNameDelimiter: "~",
      // 表示chunk的名字自动生成(由cacheGroups的key、entry名字)
      name: true,
      // cacheGroups 表示分包分组规则,每一个分组会继承于default
      // priority表示优先级,一个chunk可能被多个分组规则命中时,会使用优先级较高的
      // test提供时 表示哪些模块会被抽离
      cacheGroups: {
   
        vendors: {
   
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
   
          minChunks: 2,
          priority: -20,
          // 复用已经生成的chunk
          reuseExistingChunk: true
        }
      }
    }
  }
};

还有一个很重要的配置是output.jsonpFunction(默认是webpackJsonp)。这是用于异步加载 chunk 的时候一个全局变量。如果多 webpack 环境下,为了防止该函数命名冲撞产生问题,最好设置成一个比较唯一的值。

一般而言,没有最完美的分包配置,只有最合适当前项目场景需求的配置。很多时候,默认配置已经足够可用了。

通常来说,为了保证 hash 的稳定性,建议:

1、使用webpack.HashedModuleIdsPlugin。这个插件会根据模块的相对路径生成一个四位数的 hash 作为模块 id。默认情况下 webpack 是使用模块数字自增 id 来命名,当插入一个模块占用了一个 id(或者一个删去一个模块)时,后续所有的模块 id 都受到影响,导致模块 id 变化引起打包文件的 hash 变化。使用这个插件就能解决这个问题。

2、chunkid 也是自增的,同样可能遇到模块 id 的问题。可以通过设置optimization.namedChunks为 true(默认 dev 模式下为 true,prod 模式为 false),将chunk的名字使用命名chunk

1、2 后的效果如下。

3、抽离 css 使用mini-css-extract-plugin。hash 模式使用contenthash

这里以腾讯云某控制台页面以下为例,使用 webpack 路有异步加载效果后如下。可以看到,第一次访问页面。这里是先请求到一个总的入口 js,然后根据我们访问的路由(路由 1),再去加载这个路由相关的代码。这里可以看到我们异步加载的 js 数为 5,就相当于上面提到的默认配置项maxAsyncRequests,通过waterfall可以看到这里是并发请求的。如果再进去其它路由(路由 2)的话,只会加载一个其它路由的 js(或者还有当前没有加载过的 vendor js)。这里如果只修改了路由 1 的自己单独业务代码,vendor 相关的 hash 和其它路由的 hash 也不是不会变,这些文件就能很好的利用了浏览器缓存了

webpack 异步加载分包如何实现

我们知道,默认情况下,浏览器环境的 js 是不支持import和异步import('xxx').

webpack 异步加载原理是利用 require.ensure 这个 API,它可以将指定的文件打包成一个可以异步加载chunk。具体步骤如下: 1. 在 webpack 配置文件中,通过引入 require.ensure API,将需要异步加载的文件进行单独打包。 2. 在 webpack 的输出的 runtime 代码中,会包含异步 chunk 的 id 及路径信息。 3. 当页面需要加载异步 chunk 时,会根据异步 chunk 的 id 发起请求,并将其下载到客户端。 4. 异步 chunk 下载完成后,可以通过回调函数或 Promise 的方式执行相应的代码逻辑。 以上就是 webpack 异步加载的基本原理。通过将需要异步加载的代码单独打包成 chunk,并在需要的时候动态加载,可以提高页面的加载速度和性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [详解webpack2异步加载套路](https://download.csdn.net/download/weixin_38520192/13606882)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [从Webpack的同步/异步加载加载顺序到循环依赖到开发习惯](https://blog.csdn.net/weixin_42274805/article/details/123639416)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Webpack异步加载原理及分包策略](https://blog.csdn.net/qq_41581588/article/details/127007481)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek技术前线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值