Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

分析 webpack 动态import的实现 #1

Open
mbaxszy7 opened this issue Jun 16, 2020 · 0 comments
Open

分析 webpack 动态import的实现 #1

mbaxszy7 opened this issue Jun 16, 2020 · 0 comments
Labels

Comments

@mbaxszy7
Copy link
Owner

要搞懂webpack 动态import的实现需要先搞懂webpack打包后产生的代码

webpack输出代码分析

  1. 代码框架分析
  • webpack打包后的代码其实是一个自执行函数,该函数的modules参数就是一个对象,该对象所有的key是文件的路径,对应的value是该文件转换后的代码。以index.js为例,对应的代码片段是:

carbon (2)

从以上代码可以看出,webpack替换了import 和 index中依赖的模块Hello。

  • 再看自执行函数的匿名函数部分

carbon

简化以后的代码如下:

carbon (1)

所以这个自执行函数会运行__webpack_require__(0)(第0项内容就是去加载 src/index.js 中的代码)

  1. 具体函数分析
  • webpack_require
    carbon (2)

__webpack_require__接受一个moduleId,就是modules参数的key。__webpack_require__内执行的模块的代码就是key对应的value。

  • 在此项目中, 当运行到index模块的代码时,会去加载hello模块的代码:webpack_require(/*! ./hello */ “./src/hello.js”)。hello.js打包后的代码如下:

carbon (3)

这里多了执行__webpack_require__.r 和 webpack_require.e。可以看到打包后的hello.js中有这样有个promise链:

__webpack_require__.e(/*! import() */ 0)
.then(__webpack_require__.bind(null, /*! ./async */ "./src/async.js"))

先去加载了bundle 0 也就是0.bundle.js, 也就是webpack code splitting 出的async.js。然后(then)执行async.js的代码。

  • webpack_require.r
    carbon (4)

  • webpack_require.e
    先来看一下__webpack_require__.e内部的代码:
    carbon (4)

大致概括一下__webpack_require__.e的代码: 根据传入的chunkId,先检查是否已经加载过了,再检查是否正在加载, 否则的话创建加载script的promise,然后去加载chunk script。如果script加载失败(超时也算),那么执行chunk promise的reject。

从这步可以看到动态import的实现已经初露端倪。要完整的理解,还需要思考一个问题:上面script加载成功的chunk promise的resolve在什么时候执行?下面来看一下项目async.js打包出来的代码(去掉了一些注释):
carbon (5)

这里重点要看window[“webpackJsonp”]。window[“webpackJsonp”]
在之前的自执行函数的匿名函数中有定义。并且window[“webpackJsonp”]
的push方法已经被重写为webpackJsonpCallback。下面就来看一下webpackJsonpCallback这个函数。

  • webpackJsonpCallback
    carbon (6)

webpackJsonpCallback大致就是在做:将传入的chunkid(个人以为叫bundleid更合理)标记为已加载,并将传入的模块挂在到installedChunks对象上(缓存),最终执行 webpack_require.e 函数返回的promise的resolve,注意resolve也是从__webpack_require__.e 函数处理过的installedChunks上取的(installedChunks[chunkId] = [resolve, reject])

总结

实现动态import的主要代码:

async () => {
    const res = await import("./async");
    console.log(res.default());
}

对应打包后的代码:

async () => {
  const res = await __webpack_require__.e(/*! import() */ 0)
  .then(__webpack_require__.bind(null, /*! ./async */ "./src/    
           async.js"));
    console.log(res.default());
}
  • 执行流程:
  1. 先执行__webpack_require__.e(0),把动态import的promise挂载到installedChunks上, 创建script 加载0.bundle.js, 返回promise

  2. 如果加载(超时也是)失败,在onScriptComplete中reject;如果加载成功,则执行加载过来的js:执行 window[webpackJsonp] 上的push方法(已经被重写为webpackJsonpCallback),将动态加载的模块(0.bundle.j)标记为已加载,并将模块(async.js)对应的代码挂载到modules参数上,最后resolve异步加载的模块。

  3. 在then后执行(webpack_require)挂载到modules参数上对应的模块(async.js)的代码

通过分析 webpack打包后的模板代码,可以看到webpack用IIFE的形式将所有模块作为modules参数传入,从entry模块开始依次执行modules,巧妙的处理了动态加载的模块。用 webpack_require 抹平了import和require之间的差异。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant