在上篇文章中,我们介绍了 webpack 同步加载模块的原理。这篇文章,我们来介绍一下 webpack 异步加载模块。

异步加载模块

还是先做一些准备工作。

首先定义一个依赖模块:math.js,math.js 采用 ES6 module 导出了两个函数 add 和 minus。

export function add(a, b) {return a + b;
}export function minus(a, b) {return a - b;
}

然后定义一个入口模块:index.js,index.js 通过 import 函数导入了 math.js,然后调用了里面的 add 和 minus 方法。

import("./math").then(math => {console.log(math.add(2, 1));console.log(math.minus(2, 1));
});

最后再定义一个配置文件:webpack.config.js,内容和 webpack 同步加载模块中一样。

const path = require("path");module.exports = {mode: "development",devtool: "source-map",entry: path.join(__dirname, 'index.js'),output: {filename: "main.js",path: path.join(__dirname, 'dist')}
};

在根目录下执行webpack --config webpack.config.js,就可以在 dist 目录下看到最终的生成产物了,可以看到 dist 目录下除了 main.js 外,还多了一个 0.main.js。我们通常把 main.js 称为同步 chunk,0.main.js 称为异步 chunk

先来看一下 main.js 文件中的代码。

main.js 代码片段

首先是 webpackBootstrap 的函数体,与同步加载模块相比,它新增了很多内容:

  • webpackJsonpCallback 函数:异步加载 chunk 后的 jsonp 回调函数
  • installedChunks 对象:缓存 chunk 加载状态,main 代表入口模块,0 表示模块已加载,因为入口模块是同步 chunk,所以默认就是已加载的
  • jsonpScriptSrc 函数:拼接 chunk 请求地址
  • __webpack__require__.e 函数:异步加载 chunk 函数
  • jsonp 初始化代码:初始化 jsonp 相关配置

接下来是 webapackBootstrap 的参数部分,与同步加载模块不同的是,它移除了依赖模块 main.js(main.js 中的内容都移入到了异步 chunk 文件 0.main.js 中)。

最后是入口模块 index.js,它使用 __webpack_require__.e 来异步下载依赖模块 math.js,在 math.js 下载完毕后,再使用 __webpack_require__ 来同步加载 math.js

看到这里,大家应该有个初步感觉了:webpack 异步加载模块,其实是通过 jsonp 的方式来实现的。

接下来看一下 __webpack_require__.e_ 的函数实现。

__webpack_requiore__.e 函数实现

__webpack_require__.e 中的代码比较多,先看一头一尾:最前面定义了一个 promise 数组 promises,最后面用 Promise.all 的方式返回了 promises。因此 __webpack_require__.e 中间的代码,其实都是用来操作 promises 数组的。具体是如何操作的,我们一步步来看。

首先是缓存查找,在 installedChunks 对象中根据 chunkId 来判断对应的 chunk 是否已加载,chunk 主要有三种状态,分别对应不同的处理:

  • 0:表示 chunk 已加载,这种情况下,不做处理,直接返回
  • 数组:表示 chunk 正在加载,这种情况下,会将缓存的 promise 对象压入 promises 数组中,等待 chunk 加载完毕
  • undefined:表示 chunk 第一次加载,这种情况下的操作包括:
    • 创建一个新的 promise 对象
    • 将新创建 promise 对象的 reject、resolve 方法以及 promise 对象本身都存放到 installedChunks 对象中
    • 将 promise 对象压入 promises 数组中

接下来是模块加载,模块加载使用 jsonp 的方式,首先会动态创建一个 script 标签,src 指向异步 chunk 地址,然后将 script 标签添加到 head 中,实现异步加载 chunk 的功能。

最后是异常处理,给 script 标签添加了 onloade 和 onerror 事件处理函数onScriptCompleteonScriptComplete 在判断模块加载超时或是加载失败的情况下(缓存的 chunk 不为 0),会调用之前保存的 reject 方法返回模块加载失败的异常,同时还会将 chunk 的缓存标识设置为 undefined,标识未加载。

从前面的流程中我们可以看到,__webpack_require__.e 主要是通过 jsonp 的方式来加载异步 chunk,同时通过 promise 对象来控制异步 chunk 的加载情况:在加载失败的情况下会调用 promise 对象的 reject 方法,在加载成功的情况会执行异步 chunk 文件,也就是 0.main.js。

再来看一下 0.main.js 中的代码。

0.main.js 代码片段

0.main.js 中的内容比较简单,主要是调用 window 上挂载的全局数组 webpackJsop 的 push 方法,push 的内容包括两部分:chunkId 数组和依赖模块函数,webpackJsonp 全局数组又是在哪里定义的呢?

jsonp 初始化代码

我们回到 webpackBootStrap 函数,可以看到,webpackJsonp 是在 jsonp 初始化代码中定义的,jsonp 初始化代码主要干了以下几件事:

  1. 定义了一个全局的 webpackJsonp 数组,用来存储所有的 jsonp 回调
  2. webpackJsonp 的原生 push 方法改写为了 webpackJsonpCallback
  3. webpackJsonp 原生的 push 方法保存为了 parentJsonpFunction

所以,在 0.main.js 中调用 webpackJsonp 的 push 方法,最终执行的是 webpackJsonpCallback 中的代码。

最后,看一下 webpackJsonpCallback 的函数实现。

webpackJsonpCallback 函数实现

首先是函数参数,data 参数分为两部分:

  • chunkIds:一个数组,包含当前 chunk 文件依赖的 chunkId,以及自身的 chunkId
  • moreModule:代表当前 chunk 带来的新模块,也就是咱们前面看到的模块执行函数

接下来是执行逻辑:

  1. 遍历 chunkIds 数组,判断 installedChunks 对象中的 chunk 是否处于加载中状态,如果返回数组,则表示正在加载中,在这种情况下,会取出缓存的 resolve 方法(installedChunks[chunkId][0]),放到 resolves 数组中,等下统一执行,同时,还会将对应的 chuk 设置为已加载
  2. moreModules 合并到 modules 对象中,便于后续同步加载
  3. 将 data 参数保存到 webpackJsonp 全局数组中
  4. 遍历 resolves 数组,执行前面缓存的 resolve 方法并清空数组,保证该模块加载开始前所有前置依赖内容包括它自身都已经被加载完毕

总结

总结一下,webpack 采用 jsonp 的方式来实现模块的异步加载:

  • 通过 __webpack_require__.e 实现动态加载
  • 通过 webpackJsonpCallback 实现异步回调

最后,我们用流程图来总结一下 webpack 异步加载模块的流程。

webpack 异步加载模块流程图

模块化加载_webpack模块化原理-异步加载模块相关推荐

  1. Unity3D研究院之异步加载游戏场景与异步加载游戏资源进度条(三十一)

    异步任务相信大家应该不会陌生,那么本章内容MOMO将带领大家学习Unity中的一些异步任务.在同步加载游戏场景的时候通常会使用方法 Application.LoadLevel("yourSc ...

  2. android 图片加载 软引用_Android 异步加载网络图片并缓存到本地 软引用 学习分享(转)...

    迪 王. 于 星期四, 20/02/2014 - 21:36 提交 在android应用开发的时候,加载网络图片是一个非常重要的部分,很多图片不可能放在本地,所以就必须要从服务器或者网络读取图片. 软 ...

  3. js的阻塞加载、延迟加载和异步加载

    1.阻塞加载: 平常默认使用的都是阻塞加载.例如:                                        阻塞加载会阻止浏览器的后续处理,停止了后续的文件的解析,执行,如图像的 ...

  4. html 加载高德地图,vue异步加载高德地图

    几种加载js的方式 同步加载 异步加载 延迟加载 同步加载 用的最多的一种方式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的 ...

  5. 浏览器渲染阻塞与优化-详解推迟加载、异步加载。

    我认为一个前端工程师是否优秀,很大程度上取决于对前端性能上优化的功力.所以性能优化对前端真的很重要!!! 本文介绍了什么是阻塞.为什么会阻塞?阻塞优化常用的5种方式以及他们的注意事项. 浏览器渲染阻塞 ...

  6. lazy(懒加载)模式和异步加载模式详解

    说到懒加载,其实就是延迟会再加载,没有想象中的那么神秘,其实我们时刻都在用懒加载, 第一种:简单的延迟创建控件,比如说,创建一个属性变量,我们用get方法来获取生成这个变量就是用到了懒加载,详细点说就 ...

  7. 【前端】JS异步加载

    文章目录 为什么要异步加载 如何实现异步加载 参考 为什么要异步加载 两个原因其实是一个意思. 原因1: JS是单线程的语言,它会同步的执行代码,从上往下执行 但是,一旦网络不好,或要加载的js文件过 ...

  8. iOS开发swift版异步加载网络图片(带缓存和缺省图片)

    iOS开发之swift版异步加载网络图片 与SDWebImage异步加载网络图片的功能相似,只是代码比较简单,功能没有SD的完善与强大,支持缺省添加图片,支持本地缓存. 异步加载图片的核心代码如下: ...

  9. Unreal Engine 4 —— 异步加载关卡的实现方法及思考

    这篇文章介绍了在ue4中实现异步加载关卡的方法,以及我对应的思考. 背景 在很多游戏中都有用到关卡的异步加载,关卡的异步加载指的是在游戏的某个阶段,使用多线程的方法进行新关卡对应的内容加载.从而能够免 ...

最新文章

  1. P3902 递增(LIS+树状数组)
  2. Guava之RateLimiter的设计
  3. 关于SAMBA的关键参考资源
  4. Angular 项目工程文件结构介绍
  5. AngularJs-指令和指令之间的交互(动感超人)
  6. java两个对象属性比较
  7. Clos Network
  8. 常见积分求导公式表--便于记忆
  9. SONY索尼摄像机Z280断电KLV.RSV.MXF视频打不开数据恢复成功
  10. 透视投影的原理和实现
  11. win10桌面不见了如何找到
  12. 2019年创新中国网课答案
  13. JS 拖拽对齐参考线+自动吸附+对齐到网格
  14. SimpleRemote -开源的远程连接管理工具
  15. Flutter-RefreshIndicator不能下拉刷新
  16. 卷积神经网络 神经网络,卷积神经网络基础知识
  17. StringIO cStringIO
  18. 与欢聚十年终成就百度 不吃老本的YY直播还能有“黄金时代吗?
  19. 有哪些编辑图片加文字的软件?这些软件值得收藏
  20. 【Spring】一文带你吃透基于注解的DI技术

热门文章

  1. vim java.dict_配置 VIM 英语字典
  2. 06 外键的三种分类
  3. 40. Combination Sum II
  4. C#设计模式--工厂方法模式
  5. 最简单的WebService
  6. foobar2000 配置
  7. WSL2安装GUI界面与音视频
  8. YVU420PackedSemiPlanar32m4ka与YUV420PackedSemiPlanar64x32Tile2m8ka
  9. netcore之托管程序
  10. 查看Oracle的procedures,Oracle通过shell脚本查看procedure的信息