Skip to content

Instantly share code, notes, and snippets.

@MrKou47
Last active March 30, 2018 09:13
Show Gist options
  • Save MrKou47/64832ea1fa4e8486cd8687a475e4cfbe to your computer and use it in GitHub Desktop.
Save MrKou47/64832ea1fa4e8486cd8687a475e4cfbe to your computer and use it in GitHub Desktop.
dynamic import 测试
webpackJsonp([0],[
/* 0 */,
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
const a = 2;
/* harmony default export */ __webpack_exports__["default"] = (a);
function run() {
console.log(a);
}
run();
/***/ })
]);
/**
* 立即执行函数
* modules: any[] 传入entry module的id
*
* return: object
*/
(function (modules) { // webpackBootstrap modules is an array
// install a JSONP callback for chunk loading
var parentJsonpFunction = window["webpackJsonp"];
/**
* chunkIds: 异步加载的模块的id list -> [0]
* moreModules: [undefined, function(module, __webpack_exports__, __webpack_require__){}]
* executeModules: undefined
*/
window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
// add "moreModules" to the modules object,
// 异步加载完成的模块 同样被 视为 模块,与同步的模块同等地位。但不表示此模块已安装,只是表示它是被依赖进来的
// then flag all "chunkIds" as loaded and fire callback
// 如果 所有chunk 都已经被 **加载** 完毕 则触发callback
// 也就是说,webpack 并不保证异步模块中的代码全部执行完成,只要触发了 webpackJsonp 就代码此模块已经执行完成
var moduleId, chunkId, i = 0, resolves = []/* 保存 promise resolve 函数的 list */, result;
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0; // 表示此需异步加载的模块已经加载完成,将其置为 0
}
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
while (resolves.length) { // 逐个执行resolve并从数组中移除
resolves.shift()();
}
};
// The module cache
var installedModules = {};
// objects to store loaded and loading chunks
var installedChunks = {
1: 0
};
/** The require function: realize commonjs require func
* 实现的 require 方法。代码中的require方法都将被此方法所代替
* @param {*number} moduleId
*/
function __webpack_require__(moduleId) {
// Check if module is in cache
// 如果当前模块已经被加载完成,则直接从 installedModules 中返回该模块
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
/**
* 初始化一个空模块,包含了三个属性:模块id,是否已经load,和 暴露出的 变量/方法。
* 同时将此模块插入到 installedModules 中
*/
var module = installedModules[moduleId] = {
i: moduleId, // module id
l: false, // load flag
exports: {} // what's export
};
// Execute the module function
// 执行模块方法
/**
* 我们定义的模块(文件), 都会被转换成函数的形式。例如:
*
* const a = 2;
* function test() { console.log(a) }
* test();
* ⬇️️️️️️⬇️⬇️️⬇️⬇️
* function(module, exports, __webpack_require__){
* const a = 2;
* function test() { console.log(a) }
* test();
* }
*
* 转换后的函数接收三个参数:整个模块,模块暴露的对象,以及 __webpck_require__ 方法。此方法用来替代代码中诸如 const http = require('http'); 代码中的 require 关键字
* 通过 .call(module.exports, module, module.exports, __webpack_require__) 将模块的 this 替换为之前生成的空模块,然后执行模块中的方法。
*
*/
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
// 模块执行完成后,将模块置为已加载状态
module.l = true;
// Return the exports of the module
return module.exports; // 返回此模块暴露的对象
}
/**
* 动态加载模块的方法,用来异步加载并执行一个模块
* @param {*number} chunkId webpack 生成的文件的 chunkId。生成的文件名(如果未配置)形如 chunkId.[filename].js
* 如 __webpack_require__.e(0) 就是加载并执行 0.bundle.js
*
* 下面的注释将以 __webpack_require__.e(0) 为例子
*/
__webpack_require__.e = function requireEnsure(chunkId /* 传入 0 */) {
// 同样实现了一个缓存机制,和加载模块类似
// 此时 installedChunks = { 1:0 }, 则 installedChunkData 为 undefined
var installedChunkData = installedChunks[chunkId];
if (installedChunkData === 0) { // 没懂
return new Promise(function (resolve) { resolve(); });
}
// 用来表示正在加载模块的一个 Promise。installedChunkData === undefined 跳过
if (installedChunkData) {
return installedChunkData[2];
}
// setup Promise in chunk cache
var promise = new Promise(function (resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
/**
* installedChunkData:
* (2) [f, f]
* > 0: f ()
* > 1: f ()
*/
installedChunkData[2] = promise;
// start chunk loading
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.async = true;
script.timeout = 120000;
// nonce: number once 只被使用一次的随机数值
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
// 关键一步,设置script标签的src。可以看到是通过拼接预设的配置来完成的
script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
var timeout = setTimeout(onScriptComplete, 120000); // 超时 12000ms === 120s === 2min 的回调函数。目前没有找到设置超时时常的地方
script.onerror = script.onload = onScriptComplete;
// 模块加载完成后
function onScriptComplete() {
// avoid mem leaks in IE. 避免 ie 中的内存泄漏
script.onerror = script.onload = null;
clearTimeout(timeout);
var chunk = installedChunks[chunkId];
if (chunk !== 0) {
if (chunk) {
chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
}
installedChunks[chunkId] = undefined;
}
};
/**
* 将 script 标签插入到 head。一般情况下,在插入成功后会执行 onScriptComplete。
* 插入后会自动执行 0.bundle.js 的代码
*
* 此时可以看一下 0.bundle.js 的代码。可以看到我们之前的 should-be-import 文件中的代码转换成了函数执行的方式。
* 而执行的方式也就是上面向 window 对象中注入的 webpackJsonp函数
*/
head.appendChild(script);
return promise;
};
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// on error function for async loading
__webpack_require__.oe = function (err) { console.error(err); throw err; };
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
})
([
/* 0 entry module */
(function (module, exports, __webpack_require__) {
// 上面也说了异步加载的模块与同步依赖的模块同级别,只是未引入。
// 在 __webpack_require__.e resolve 之后,使用 bind 创建一个新的 __webpack_require__ 函数,参数为 chunk 的 id
const importPromise = __webpack_require__.e(0).then(__webpack_require__.bind(null, 1));
setTimeout(() => {
importPromise
.then(v => {
console.log(v);
});
}, 4000);
})
]);
const importPromise = import('./should-be-import.js');
setTimeout(() => {
importPromise
.then(v => {
console.log(v);
});
}, 4000);
const a = 2;
export default a;
function run() {
console.log(a);
}
run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment