Skip to main content

浏览器正常运行流程-静态-同步

总览

测试用例

webpack-dynamic-import:index.js

webpack-dynamic-import:import()原理

测试用例: 编译前

index.js

import _ from 'lodash';
import print from './print'

function component() {
const element = document.createElement('div');
const button = document.createElement('button');
const br = document.createElement('br');

button.innerHTML = 'Click me and look at the console!';
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button);

// 静态加载
button.onclick = e => {
print()
};

return element;
}

document.body.appendChild(component());

print.js

console.log(
'The print.js module has loaded! See the network tab in dev tools...'
);

export default () => {
console.log('Button Clicked: Here\'s "some text"!');
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Getting Started</title>
</head>

<body>
<script src="../dist/index.bundle.js"></script>
</body>
</html>

打包后:IIFE(立即执行函数)

里面全都是立即执行函数 index.bundle.js

(() => { // webpackBootstrap
var __webpack_modules__ = ({

/***/ "./node_modules/lodash/lodash.js":
/***/ (function (module, exports, __webpack_require__) {

eval("/* module decorator */ module = __webpack_require__.nmd(module);\nvar __WEBPACK_AMD_DEFINE_RESULT__;/**\n * @license\n * Lodash <https://lodash.com/>\n * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>\n * Released under MIT license <https://lodash.com/license>\n * =====省略lodash代码===?");

/***/
}),

/***/ "./src/index.js":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\n/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lodash__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _print__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./print */ \"./src/print.js\");\n\r\n\r\n\r\nfunction component() {\r\n const element = document.createElement('div');\r\n const button = document.createElement('button');\r\n const br = document.createElement('br');\r\n\r\n button.innerHTML = 'Click me and look at the console!';\r\n element.innerHTML = lodash__WEBPACK_IMPORTED_MODULE_0___default().join(['Hello', 'webpack'], ' ');\r\n element.appendChild(br);\r\n element.appendChild(button);\r\n\r\n\r\n // 动态加载\r\n // button.onclick = e => import(/* webpackChunkName: \"print\" */ './print').then(module => {\r\n // const print = module.default;\r\n\r\n // print();\r\n // });\r\n\r\n // 静态加载\r\n button.onclick = e => {\r\n (0,_print__WEBPACK_IMPORTED_MODULE_1__[\"default\"])()\r\n };\r\n\r\n return element;\r\n}\r\n\r\ndocument.body.appendChild(component());\r\n\n\n//# sourceURL=webpack://webpack-helloworld/./src/index.js?");
}),

/***/ "./src/print.js":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconsole.log(\r\n 'The print.js module has loaded! See the network tab in dev tools...'\r\n);\r\n\r\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (() => {\r\n console.log('Button Clicked: Here\\'s \"some text\"!');\r\n});\n\n//# sourceURL=webpack://webpack-helloworld/./src/print.js?");

/***/
})
});
/************************************************************************/
// 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] = {
id: moduleId,
loaded: false,
exports: {}
};

// Execute the module function
__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);

// Flag the module as loaded
module.loaded = true;

// Return the exports of the module
return module.exports;
}

/************************************************************************/
/* webpack/runtime/compat get default export */
(() => {
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = (module) => {
var getter = module && module.__esModule ?
() => (module['default']) :
() => (module);
__webpack_require__.d(getter, { a: getter });
return getter;
};
})();

/* 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/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/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/node module decorator */
(() => {
__webpack_require__.nmd = (module) => {
module.paths = [];
if (!module.children) module.children = [];
return module;
};
})();

var __webpack_exports__ = __webpack_require__("./src/index.js");

})();

__webpack_modules__是一个对象

对象的 key 就是每个 js 模块的相对路径,value 就是一个函数(我们下面称之为模块函数)。

IIFE 末尾执行 会先 require 入口模块:

var __webpack_exports__ = __webpack_require__("./src/index.js");

__webpack_require__()加载一个模块,并在最后返回模块 module.exports 变量,入口模块会在执行时 require 其他模块例如 lodash,以下为简化后的代码,从而不断的加载所依赖的模块,形成依赖树

如下的./src/index.js模块__webpack_require__(..)函数中就引用了其他的文件

  • ./node_modules/lodash/lodash.js
  • print.js
/***/ "./src/index.js":
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\n/* harmony import */ var lodash__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(lodash__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _print__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./print */ \"./src/print.js\");\n\r\n\r\n\r\nfunction component() {\r\n const element = document.createElement('div');\r\n const button = document.createElement('button');\r\n const br = document.createElement('br');\r\n\r\n button.innerHTML = 'Click me and look at the console!';\r\n element.innerHTML = lodash__WEBPACK_IMPORTED_MODULE_0___default().join(['Hello', 'webpack'], ' ');\r\n element.appendChild(br);\r\n element.appendChild(button);\r\n\r\n\r\n // 动态加载\r\n // button.onclick = e => import(/* webpackChunkName: \"print\" */ './print').then(module => {\r\n // const print = module.default;\r\n\r\n // print();\r\n // });\r\n\r\n // 静态加载\r\n button.onclick = e => {\r\n (0,_print__WEBPACK_IMPORTED_MODULE_1__[\"default\"])()\r\n };\r\n\r\n return element;\r\n}\r\n\r\ndocument.body.appendChild(component());\r\n\n\n//# sourceURL=webpack://webpack-helloworld/./src/index.js?");
})
});

webpack 是如何支持 ESM 的

可能大家已经发现,上面的写法是 ESM 的写法;对于模块化的一些方案的了解,Javascript 中的 CJS, AMD, UMD 和 ESM是什么?

就是为 'webpack_exports` 添加一个属性 __esModule,值为 true

    (() => {
// define __esModule on exports
__webpack_require__.r = (exports) => {
console.log('__webpack_require__.r', exports)
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });

}
Object.defineProperty(exports, '__esModule', { value: true });

};

})();

__webpack_require__.n

__webpack_require__.n会判断module是否为es模块,当__esModule为 true 的时候,标识 module 为es 模块,默认返回module.default,否则返回module。

最后看 __webpack_require__.d,主要的工作就是将上面的 getter 函数绑定到 exports 中的属性 a 的 getter 上
    /* webpack/runtime/compat get default export */
(() => {
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = (module) => {
var getter = module && module.__esModule ?
() => (module['default']) :
() => (module);
__webpack_require__.d(getter, { a: getter });
return getter;

};

})();