Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【模块化】:Webpack 是如何将不同规范(ESM、CJS、UMD、AMD、CMD)的模块化代码打包到一起并协调它们运行的?

【模块化】:Webpack 是如何将不同规范(ESM、CJS、UMD、AMD、CMD)的模块化代码打包到一起并协调它们运行的?

作者头像
WEBJ2EE
发布于 2022-03-30 13:08:23
发布于 2022-03-30 13:08:23
7.3K20
代码可运行
举报
文章被收录于专栏:WebJ2EEWebJ2EE
运行总次数:0
代码可运行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
目录
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
代码运行次数:0
运行
AI代码解释
复制
// 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
代码运行次数:0
运行
AI代码解释
复制
// 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

Asynchronous Module Definition (AMD) has gained traction on the frontend, with RequireJS being the most popular implementation.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//    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
代码运行次数:0
运行
AI代码解释
复制
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

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.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
(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
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
webpack build

观察 webpack 的打包输出:

3.3. 分析过程中需要用到的一些 JS 知识

3.3.1. this 与 bind

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

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.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

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.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
/******/ (() => { // 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
代码运行次数:0
运行
AI代码解释
复制
{
  "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
代码运行次数:0
运行
AI代码解释
复制
{
  "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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
贴个github链接呗,大佬
贴个github链接呗,大佬
回复回复点赞举报
代码可以提供吗
代码可以提供吗
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
Webpack 打包 commonjs 和 esmodule 动态引入模块的产物对比
接 Webpack 打包 commonjs 和 esmodule 模块的产物对比 我们来继续分析。这篇文章主要来看一下动态引入,允许我们引入的模块名包含变量。
windliang
2022/09/23
9490
Webpack 打包 commonjs 和 esmodule 动态引入模块的产物对比
webpack模块化原理-Code Splitting
webpack的模块化不仅支持commonjs和es module,还能通过code splitting实现模块的动态加载。根据wepack官方文档,实现动态加载的方式有两种:import和require.ensure。
Keller
2021/12/14
9690
深入了解 webpack 模块加载原理
无论你开发使用的是 CommonJS 规范还是 ES6 模块规范,打包后的文件都统一使用 webpack 自定义的模块规范来管理、加载模块。本文将从一个简单的示例开始,来讲解 webpack 模块加载原理。
谭光志
2020/09/28
1.5K0
【前端】:模块化与Webpack
模块化开发就是封装细节,提供使用接口,彼此之间互不影响,每个模块都是实现某一特定的功能。
WEBJ2EE
2020/07/14
8510
【前端】:模块化与Webpack
【webpack 进阶】Webpack 打包后的代码是怎样的?
webpack 是我们现阶段要掌握的重要的打包工具之一,我们知道 webpack 会递归的构建依赖关系图,其中包含应用程序的每个模块,然后将这些模块打包成一个或者多个 bundle。
coder_koala
2021/03/12
1.4K0
【webpack 进阶】Webpack 打包后的代码是怎样的?
webpack启动代码源码解读
虽然每天都在用webpack,但一直觉得隔着一层神秘的面纱,对它的工作原理一直似懂非懂。它是如何用原生JS实现模块间的依赖管理的呢?对于按需加载的模块,它是通过什么方式动态获取的?打包完成后那一堆/******/开头的代码是用来干什么的?本文将围绕以上3个问题,对照着源码给出解答。
Dickensl
2022/06/14
7470
webpack启动代码源码解读
Webpack编译结果浅析
如今Webpack已经是一个不可或缺的前端构建工具,借助这个构建工具,我们可以使用比较新的技术(浏览器不能直接支持)来开发。
书童小二
2018/10/11
1.3K0
Webpack编译结果浅析
【Webpack】241-Webpack 是怎样运行的?
在平时开发中我们经常会用到Webpack这个时下最流行的前端打包工具。它打包开发代码,输出能在各种浏览器运行的代码,提升了开发至发布过程的效率。
pingan8787
2019/07/25
8770
【Webpack】241-Webpack 是怎样运行的?
Webpack原理-输出文件分析
虽然在前面的章节中你学会了如何使用 Webpack ,也大致知道其工作原理,可是你想过 Webpack 输出的 bundle.js 是什么样子的吗? 为什么原来一个个的模块文件被合并成了一个单独的文件?为什么 bundle.js 能直接运行在浏览器中? 本节将解释清楚以上问题。
IMWeb前端团队
2019/12/03
6930
Webpack原理-输出文件分析
从webpack4打包文件说起
一堆的webpack配置教程看腻了?这里有webpack4的打包及加载机制,要不了解一下?而这一切就得从打包文件说起。
elson
2018/08/01
3K1
1. 构建前后产物对比分析webpack做了些什么?
通过一个demo带你深入进入webpack@4.46.0源码的世界,分析构建原理,专栏地址,共有十篇。
tinyant
2022/11/16
8910
1. 构建前后产物对比分析webpack做了些什么?
由浅至深了解webpack异步加载背后的原理
1、module:我们源码目录中的每一个文件,在 webpack 中当作module来处理(webpack 原生不支持的文件类型,则通过 loader 来实现)。module组成了chunk。
ACK
2020/02/08
1.7K0
由浅至深了解webpack异步加载背后的原理
webpack2生成代码分析
打包一个模块 // webpack.config.js module.exports = { entry: { index: "./main.js", }, o
李成熙heyli
2018/01/05
1.9K0
揭开Vue异步组件的神秘面纱
在大型应用里,有些组件可能一开始并不显示,只有在特定条件下才会渲染,那么这种情况下该组件的资源其实不需要一开始就加载,完全可以在需要的时候再去请求,这也可以减少页面首次加载的资源体积,要在Vue中使用异步组件也很简单:
街角小林
2022/03/21
6350
揭开Vue异步组件的神秘面纱
Webpack 原理系列六: 彻底理解 Webpack 运行时
在上一篇文章 有点难的 webpack 知识点:Chunk 分包规则详解 中,我们详细讲解了 Webpack 默认的分包规则,以及一部分 seal 阶段的执行逻辑,现在我们将按 Webpack 的执行流程,继续往下深度分析实现原理,具体内容包括:
Tecvan
2021/12/09
1.6K0
Webpack 原理系列六: 彻底理解 Webpack 运行时
深入理解webpack
1 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。
用户6835371
2021/06/01
1K0
webpack务虚扫盲
打包工具的角色 所谓打包工具在web开发中主要解决的问题是: (1)文件依赖管理。毕竟现在都是模块化开发,打包工具首先就是要梳理文件之间的依赖关系。 (2)资源加载管理。web本质就是html、js和
用户1217459
2018/01/31
1.2K0
webpack务虚扫盲
揭秘webpack5模块打包
​在上一节中我们初步了解了webpack可以利用内置静态模块类型(asset module type)来处理资源文件,我们所知道的本地服务,资源的压缩,代码分割,在webpack构建的工程中有一个比较显著的特征是,模块化,要么commonjs要么esModule,在开发环境我们都是基于这两种,那么通过webpack打包后,如何让其支持浏览器能正常的加载两种不同的模式呢?
Maic
2022/07/28
9630
揭秘webpack5模块打包
webpack模块化
esModule通过该方法定义模块,重点在getter函数,通过闭包实现了esModule的特性:引用
tinyant
2022/11/16
4190
Webpack打包commonjs和esmodule混用模块的产物对比
接 Webpack 打包 commonjs 和 esmodule 模块的产物对比 继续,这篇文章来测试下 commonjs 模块和 esmodule 混用的情况,也就是 import 导入 commonjs 的模块,require 导入 esomodule 的模块,看一下它们在 Webpack 下的产物。
windliang
2022/09/23
1.7K0
Webpack打包commonjs和esmodule混用模块的产物对比
相关推荐
Webpack 打包 commonjs 和 esmodule 动态引入模块的产物对比
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验