目录
1. 背景
2. 回顾一波各种模块化规范
2.1. ESM
2.2. CJS
2.3. AMD
2.4. CMD
2.5. UMD
3. Webpack 打包产物分析
3.1. 打包测试环境
3.2. 打包测试项目
3.3. 分析过程中需要用到的一些 JS 知识
3.3.1. this 与 bind
3.3.2. Symbol.toStringTag
3.3.3. Document.currentScript
3.3.4. Promise.all
3.4. 打包产物 bundle.js(入口文件) 分析
4. 扩展阅读
4.1. ES 模块比 CJS 更好吗?
4.2. 什么是 "tree-shaking"?
4.3. pkg.module 是什么?
1. 背景
不知道大家有没有观察到
npm 上面发布的组件库
所使用的模块化规范并不是统一的
lodash-es:ESM 规范
lodash:CJS 规范
js-cookie:UMD 规范
但我们用这些库的时候
不需要针对这些库自身的模块化规范
调整我们的程序
原因是
Webpack、Rollup、Vite 这类工具
把模块化规范间的转换(兼容)工作
在暗地里偷偷干了
2. 回顾一波各种模块化规范
2.1. ESM
ES6 Module 简称 ESM。
代码语言:javascript复制// math.js
export function add(a, b) {
return a b;
}
// app.js:指定使用math模块的add命名导出
import { add } from './math.js';
console.log(add(1, 2)); // => 3
// 导入所有的命名导出作为math对象的成员
import * as math from './math.js';
console.log(math.add(1, 2)); // => 3
2.2. CJS
CommonJS 简称 CJS
代码语言:javascript复制// filename: foo.js
var $ = require('jquery');
var _ = require('underscore');
// methods
function a(){}; // private because it's omitted from module.exports (see below)
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports
// exposed public methods
module.exports = {
b: b,
c: c
};
2.3. AMD
代码语言:javascript复制Asynchronous Module Definition (AMD) has gained traction on the frontend, with RequireJS being the most popular implementation.
// filename: foo.js
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
2.4. CMD
代码语言:javascript复制define(function(require, exports, module) {
exports.max = function(a, b){
return a > b ? a : b;
};
exports.min = function(a, b){
return a > b ? b : a;
}
});
2.5. UMD
代码语言:javascript复制Since CommonJS and AMD styles have both been equally popular, it seems there’s yet no consensus. This has brought about the push for a “universal” pattern that supports both styles, which brings us to none other than the Universal Module Definition.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned (see below)
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
}));
3. Webpack 打包产物分析
3.1. 打包测试环境
webpack(5.69.1)配置(webpack.config.js):
代码语言:javascript复制const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
// !!
// 关闭 devtool,
// 在分析 webpack 打包原理时
// 可以减少一些不必要的干扰
// !!
devtool: false,
devServer: {
port: 9999,
open: true,
hot: true
},
plugins:[
new CleanWebpackPlugin(),
// 生成html
new HtmlWebpackPlugin({
title: 'webpack-demo',
filename: 'index.html',
inject: 'body'
}),
]
}
3.2. 打包测试项目
在一个项目中同时使用 ES6、CJS、CMD、AMD、UMD 5种不同的模块化规范编写代码,并同时应用静态导入、动态导入(Dynamic Import)方法来引用这些模块。观察 Webpack 是如何将这些不同模块化规范的代码打包到一起和协调它们运行的。
执行 webpack 的打包命令:
代码语言:javascript复制webpack build
观察 webpack 的打包输出:
3.3. 分析过程中需要用到的一些 JS 知识
3.3.1. this 与 bind
代码语言:javascript复制const arr = [];
// 常规用法
arr.push("normal"); // ok
console.log(arr);
// 特殊一点: 把 push 函数提出来
const arr_push = arr.push;
try{
arr_push("noop!");// throw Error
console.log(arr);
}catch(oE){console.error(oE)}
arr_push.call(arr, "call"); // 使用 call, ok
console.log(arr);
arr_push.bind(arr)("bind"); // 使用 bind, ok
console.log(arr);
3.3.2. Symbol.toStringTag
代码语言:javascript复制The Symbol.toStringTag wellknown symbol is a string valued property that is used in the creation of the default string description of an object. It is accessed internally by the Object.prototype.toString() method.
const p = console.log.bind(console.log);
// Default tags
p("---- Default tags ----");
p(Object.prototype.toString.call('foo')); // "[object String]"
p(Object.prototype.toString.call([1, 2])); // "[object Array]"
p(Object.prototype.toString.call({})); // "[object Object]"
p(Object.prototype.toString.call(3)); // "[object Number]"
p(Object.prototype.toString.call(true)); // "[object Boolean]"
p(Object.prototype.toString.call(undefined)); // "[object Undefined]"
p(Object.prototype.toString.call(null)); // "[object Null]"
p(Object.prototype.toString.call(()=>{})); // "[object Function]"
// ... and more
// Built-in toStringTag symbols
p("---- Built-in toStringTag symbols ----");
p(Object.prototype.toString.call(new Map())); // "[object Map]"
p(Object.prototype.toString.call(function* () {})); // "[object GeneratorFunction]"
p(Object.prototype.toString.call(Promise.resolve())); // "[object Promise]"
// ... and more
// Custom object,classes default to object tag
p("---- Built-in toStringTag symbols ----");
const o1 = {
exports: {}
}
p(Object.prototype.toString.call(o1));
// Custom tag with toStringTag
p("---- Custom tag with toStringTag ----");
const o2 = {
get[Symbol.toStringTag](){
return 'Module-o2'
},
exports: {}
}
p(Object.prototype.toString.call(o2));
const o3 = {
exports: {}
}
Object.defineProperty(o3, Symbol.toStringTag, { value: 'Module-o3' });
p(Object.prototype.toString.call(o3));
3.3.3. Document.currentScript
The Document.currentScript property returns the <script> element whose script is currently being processed and isn't a JavaScript module. (For modules use import.meta instead.) It's important to note that this will not reference the <script> element if the code in the script is being called as a callback or event handler; it will only reference the element while it's initially being processed
3.3.4. Promise.all
代码语言:javascript复制The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises.
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
接下来
我们从 Webpack 编译产物的入口文件
bundle.js
开始分析
3.4. 打包产物 bundle.js(入口文件) 分析
Webpack 的打包过程,除了需要将开发者写的业务代码打包外,还需要把一些用于支撑、调度这些业务代码运行的辅助代码(这类代码在 webpack 中叫做 runtime(运行时))一同打包进 bundle 中。以建筑作类比的话,业务代码相当于砖瓦水泥,是看得见摸得着能直接感知的逻辑;运行时(runtime)相当于掩埋在砖瓦之下的钢筋地基,通常不会关注但决定了整座建筑的功能、质量。
大多数 Webpack 特性都需要特定钢筋地基才能跑起来,比如:
- 模块化
- 异步按需加载
- HMR
- WASM
- Module Federation
bundle.js:
代码语言:javascript复制/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./static.js":
/*!*******************!*
!*** ./static.js ***!
*******************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* binding */ parseInt)
/* harmony export */ });
function parseInt(x){
return Number.parseInt(x);
}
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/create fake namespace object */
/******/ (() => {
/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
/******/ var leafPrototypes;
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 16: return value when it's Promise-like
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = this(value);
/******/ if(mode & 8) return value;
/******/ if(typeof value === 'object' && value) {
/******/ if((mode & 4) && value.__esModule) return value;
/******/ if((mode & 16) && typeof value.then === 'function') return value;
/******/ }
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ var def = {};
/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
/******/ }
/******/ def['default'] = () => (value);
/******/ __webpack_require__.d(ns, def);
/******/ return ns;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" chunkId ".bundle.js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ __webpack_require__.g = (function() {
/******/ if (typeof globalThis === 'object') return globalThis;
/******/ try {
/******/ return this || new Function('return this')();
/******/ } catch (e) {
/******/ if (typeof window === 'object') return window;
/******/ }
/******/ })();
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var inProgress = {};
/******/ var dataWebpackPrefix = "webpack-demo:";
/******/ // loadScript function to load a script via script tag
/******/ __webpack_require__.l = (url, done, key, chunkId) => {
/******/ if(inProgress[url]) { inProgress[url].push(done); return; }
/******/ var script, needAttach;
/******/ if(key !== undefined) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ for(var i = 0; i < scripts.length; i ) {
/******/ var s = scripts[i];
/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix key) { script = s; break; }
/******/ }
/******/ }
/******/ if(!script) {
/******/ needAttach = true;
/******/ script = document.createElement('script');
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.setAttribute("data-webpack", dataWebpackPrefix key);
/******/ script.src = url;
/******/ }
/******/ inProgress[url] = [done];
/******/ var onScriptComplete = (prev, event) => {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var doneFns = inProgress[url];
/******/ delete inProgress[url];
/******/ script.parentNode && script.parentNode.removeChild(script);
/******/ doneFns && doneFns.forEach((fn) => (fn(event)));
/******/ if(prev) return prev(event);
/******/ }
/******/ ;
/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/ script.onerror = onScriptComplete.bind(null, script.onerror);
/******/ script.onload = onScriptComplete.bind(null, script.onload);
/******/ needAttach && document.head.appendChild(script);
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ var scriptUrl;
/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location "";
/******/ var document = __webpack_require__.g.document;
/******/ if (!scriptUrl && document) {
/******/ if (document.currentScript)
/******/ scriptUrl = document.currentScript.src
/******/ if (!scriptUrl) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
/******/ }
/******/ }
/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/?.*$/, "").replace(//[^/] $/, "/");
/******/ __webpack_require__.p = scriptUrl;
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "main": 0
/******/ };
/******/
/******/ __webpack_require__.f.j = (chunkId, promises) => {
/******/ // JSONP chunk loading for javascript
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(true) { // all chunks have JS
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p __webpack_require__.u(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' chunkId ' failed.n(' errorType ': ' realSrc ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ }
/******/ }
/******/ };
/******/ __webpack_require__.l(url, loadingEnded, "chunk-" chunkId, chunkId);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ // no on chunks loaded
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/ for(;i < chunkIds.length; i ) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ installedChunks[chunkId][0]();
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/
/******/ }
/******/
/******/ var chunkLoadingGlobal = self["webpackChunkwebpack_demo"] = self["webpackChunkwebpack_demo"] || [];
/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!******************!*
!*** ./index.js ***!
******************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _static__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./static */ "./static.js");
console.log(`parseInt is: ${(0,_static__WEBPACK_IMPORTED_MODULE_0__["default"])(3.1415)}`);
__webpack_require__.e(/*! import() */ "cjs_js").then(__webpack_require__.t.bind(__webpack_require__, /*! ./cjs.js */ "./cjs.js", 23)).then((module)=>{
const sum = module.add(1, 2);
console.log(`sum is: ${sum}`);
})
__webpack_require__.e(/*! import() */ "es6_js").then(__webpack_require__.bind(__webpack_require__, /*! ./es6.js */ "./es6.js")).then((module)=>{
const square = module.default(4);
console.log(`square is: ${square}`);
})
__webpack_require__.e(/*! import() */ "cmd_js").then(__webpack_require__.t.bind(__webpack_require__, /*! ./cmd.js */ "./cmd.js", 23)).then((module)=>{
const max = module.max(4, 8);
console.log(`max is: ${max}`);
})
__webpack_require__.e(/*! import() */ "amd_js").then(__webpack_require__.t.bind(__webpack_require__, /*! ./amd.js */ "./amd.js", 23)).then((module)=>{
const floor = module.floor(4.3);
console.log(`floor is: ${floor}`);
})
__webpack_require__.e(/*! import() */ "umd_js").then(__webpack_require__.t.bind(__webpack_require__, /*! ./umd.js */ "./umd.js", 23)).then((module)=>{
const round = module.round(4.5);
console.log(`round is: ${round}`);
})
})();
/******/ })()
;
Webpack 的编译产物看上去有点奇葩
让我们来仔细捋一捋
梳理一下脉络
bundle 整体由一个 IIFE(webpackBootstrap) 包裹,主要包含:
- __webpack_modules__ 对象,包含了除入口外的所有模块。
- 注1:源码入口模块中,以静态方式引入的模块,会被直接编译到这里。
- 注2:源码入口模块中,以动态方式引入的模块,会在运行时按需被添加到这个对象中。
- __webpack_module_cache__ 对象,存储的是已经被引用(初始化)过的模块。
- 注:同一个模块被引入多次,但只会被初始化一次。
- __webpack_require__ 函数,实现模块引用(require) 逻辑
- __webpack_require__.r ,ES模块工具函数,用于标记某模块是一个 ES 模块
- __webpack_require__.d ,ES模块工具函数,用于转换ES模块导出的内容;
- __webpack_require__.o,工具函数,本质就是hasOwnProperty,用于判定对象自身属性中是否具有指定的属性。
上面这几个函数和对象
构成了 Webpack 运行时的“基本特性”
—— 模块化 ——
下面这几个函数和对象则
构成了 Webpack 运行时的“高级特性”
—— 异步模块的加载、运行能力 ——
- __webpack_require__.e :是异步模块(chunk)加载功能的入口。
- 注:__webpack_require__.e 采用了中间件模式。
- 注:所有需要注册给 __webpack_require__.e 的中间件,都需要注册到 __webpack_require__.f 对象中。
- 注:__webpack_require__.f.j 则是实现了异步模块(chunk )路径拼接、缓存、异常处理功能的一个中间件。
- __webpack_require__.l :基于 JSONP 的异步模块(chunk )加载与执行函数
- __webpack_require__.u :用于拼接异步模块名称的函数
- __webpack_require__.g:工具函数,获取全局对象。
- __webpack_require__.p :存储的是自动获取的 publicPath,用于后续加载 chunk 时计算完整 URL 使用。
异步模块是被下载后是如何与
__webpack_modules__、installedChunks
联动的呢?
- chunkLoadingGlobal:每一个被下载异步模块(chunk)都会把自己存储到(push)一个全局数组中。
- 但是chunkLoadingGlobal.push 这个动作被函数 webpackJsonpCallback 劫持(替换了)了,需要先完成与 installedChunks、__webpack_modules__ 的联动,然后才会被存储到 chunkLoadingGlobal 数组中。
4. 扩展阅读
4.1. ES 模块比 CJS 更好吗?
ES modules are an official standard and the clear path forward for JavaScript code structure, whereas CommonJS modules are an idiosyncratic legacy format that served as a stopgap solution before ES modules had been proposed. ES modules allow static analysis that helps with optimizations like tree-shaking and scope-hoisting, and provide advanced features like circular references and live bindings.
4.2. 什么是 "tree-shaking"?
Tree-shaking, also known as "live code inclusion", is a process of eliminating code that is not actually used in a given project. It is a form of dead code elimination but can be much more efficient than other approaches with regard to output size. The name is derived from the abstract syntax tree of the modules (not the module graph). The algorithm first marks all relevant statements and then "shakes the syntax tree" to remove all dead code. It is similar in idea to the mark-and-sweep garbage collection algorithm. Even though this algorithm is not restricted to ES modules, they make it much more efficient as they allow us to treat all modules together as a big abstract syntax tree with shared bindings.
4.3. pkg.module 是什么?
pkg.module will point to a module that has ES2015 module syntax but otherwise only syntax features that the target environments support.
Typically, a library will be accompanied with a package.json file (this is mandatory if you're publishing on npm, for example). That file will often specify a pkg.main property - something like this:
代码语言:javascript复制{
"name": "my-package",
"version": "0.1.0",
"main": "dist/my-package.js"
}
That instructs Browserify or Webpack or [insert module bundler here] to include the contents of dist/my-package.js – plus any dependencies it has – in your bundle when you call require('my-package') in your app or library.
But for ES2015-aware tools like Rollup, using the CommonJS (or Universal Module Definition) build isn't ideal, because we can't then take advantage of ES2015 module features. So assuming that you've written your package as ES2015 modules, you can generate an ES2015 module build alongside your CommonJS/UMD build:
代码语言:javascript复制{
"name": "my-package",
"version": "0.1.0",
"main": "dist/my-package.umd.js",
"module": "dist/my-package.esm.js"
}
Now we're cooking with gas - my-package continues to work for everyone using legacy module formats, but people using ES2015 module bundlers such as Rollup don't have to compromise. Everyone wins!
参考:
UMD:https://github.com/umdjs/umd AMD:https://github.com/amdjs/amdjs-api/wiki RequireJS:http://requirejs.org/ CMD:https://github.com/seajs/seajs/issues/242 SeaJS:http://www.zhangxinxu.com/sp/seajs/ pkg.module: https://github.com/rollup/rollup/wiki/pkg.module es模块化语法回顾: https://www.rollupjs.org/guide/en/#es-module-syntax