如今 Webpack https://webpack.JS.org/ 已经是一个不可或缺的前端构建工具, 借助这个构建工具, 我们可以使用比较新的技术 (浏览器不能直接支持) 来开发.
你是否好奇你写的代码经过 Webpack 构建之后会生成什么东西? 是否有时调试遇到莫名其妙的问题?
本文不讲如何进行配置, 只是基于几个基础的例子, 简要分析一下 webpack@4.20.2 构建后的代码结构, 当然了, 并不全面, 时间问题能力问题还不能理解到位.
代码比较长, 生成的代码也比较晦涩比较绕, 也可能条理不顺, 客官坐好咧~
Webpack 的运行机制
Webpack 的编译结果分析
无依赖的单个模块
有依赖的单个模块
多个入口模块
异步加载模块
提取公共模块
提取 runtime 运行时模块
开发一个 loader, 加载模块
开发一个插件 plugin, 加载模块
一, Webpack 的运行机制
Webpack 的运行过程实际上可以归纳为这个步骤
读取配置参数 -> 相关事件绑定(插件参与) -> 识别各入口 Entry 模块 -> 编译文件(loader 参与)-> 生成文件
首先读取我们的配置文件如 webpack.config.JS, 然后事件流就参与进来绑定相关的事件, Webpack 中的事件使用 Tapable https://GitHub.com/webpack/tapable 来管理, 在这一阶段, 除了绑定 webpack 内置的一大堆事件之外, 还支持自定义的一些事件处理.
配置中的 plugins 部分, 实际上也可以看作是一些自定义的事件处理, 因为插件将在定义的 "相关时刻" 插入到编译过程中处理资源, 这里的 "相关时刻" 指的就是 订阅 - 发布 模式中的发布环节
webpack 支持多个入口模块, 所以还需要进行各入口模块的分析(这里的入口模块只能为 JS 模块), 比如以下两个入口模块
分析完入口模块, 接下来分析该模块的依赖, 并使用相关 loader 进行编译(如果需要 loader 的话), 真正的编译环节是在这里.
期间会使用 AST 抽象语法树来分析语法, 直到编译完成, 输出到相应的文件中
可以来看看这篇文章 Webpack 运行机制 https://juejin.im/post/5badd0c5e51d450e4437f07a
二, Webpack 编译结果
由最简单的例子开始
2.1 无依赖的单个模块
- ./main.JS
- console.log('main');
- ./webpack.config.JS
- module.exports = {
- // entry: './main',
- entry: {
- main: './main'
- },
- mode: 'none',
- output: {
- path: path.resolve(__dirname, 'dist'),
- filename: '[name].JS'
- }
- };
注意, 在 webpack4 中默认的 mode 对 development 和 production 进行了一些特殊配置, 为了简化, 这里就设置成 none
编译一个文件, 将在 dist 目录中生成
- ./dist/main.JS
- /******/ (function(modules) {
- // webpackBootstrap
- /******/ // The module cache
- /******/ var installedModules = {
- };
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/
- }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId,
- /******/ l: false,
- /******/ exports: {
- }
- /******/
- };
- /******/
- /******/ // Execute the module function
- /******/ 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;
- /******/
- }
- /******/
- /******/
- /******/ // 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, {
- enumerable: true, get: getter
- });
- /******/
- }
- /******/
- };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, {
- value: 'Module'
- });
- /******/
- }
- /******/ Object.defineProperty(exports, '__esModule', {
- value: true
- });
- /******/
- };
- /******/
- /******/ // 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 & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', {
- enumerable: true, value: value
- });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) {
- return value[key];
- }.bind(null, key));
- /******/ return ns;
- /******/
- };
- /******/
- /******/ // 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 = "";
- /******/
- /******/
- /******/ // Load entry module and return exports
- /******/ return __webpack_require__(__webpack_require__.s = 0);
- /******/
- })
- /************************************************************************/
- /******/ ([
- /* 0 */
- /***/ (function(module, exports) {
- console.log('main');
- /***/
- })
- /******/ ]);
可以看到首先是一个匿名函数, 在 87 行时自执行传入
- [
- /* 0 */
- /***/ (function(module, exports) {
- console.log('main');
- /***/
- })
- /******/ ]
这个是 modules, 表示有一个模块需要加载
第 3 行使用 installedModules 来缓存已经加载的模块
webpack 由最初支持 commonjs 模块规范, 到后来要支持 es6 的模块等, 为了兼容不同的模块机制, 定义了一个 __webpack_require__ 函数作为 webpack 内部的 require
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- // 如果模块已经加载则直接使用
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/
- }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId, // 模块 ID
- /******/ l: false, // 模块是否已加载
- /******/ exports: {
- } // 模块的导出项
- /******/
- };
- /******/
- /******/ // Execute the module function
- /******/ 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; // 返回模块的导出项目
- /******/
- }
其中, 这个调用非常重要
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
结合匿名函数传入的参数来看, modules[moduleId] 其实就是这个
- (function(module, exports) {
- console.log('main');
- /***/
- })
- 第一个参数 module.exports 实际上就是上面模块的导出项, 是为了保证 this 能正确地指向 module, 第二第三个参数按着顺序来, 第四个参数一般用于依赖
- 因为这里 main.JS 没有依赖其他模块, 所以没有传进来
- 最后 return module.exports; 实际上就是返回了模块的导出项, 在上面的 84 行中, 入口模块被引入 . 从而自动地加载第一个模块并执行
- return __webpack_require__(__webpack_require__.s = 0); // __webpack_require__.s 为入口文件, 此处引用模块 ID
- 另外再看其它代码,
- /******/ // expose the modules object (__webpack_modules__)
- /******/ __webpack_require__.m = modules; // 将模块存起来
- /******/
- /******/ // expose the module cache
- /******/ __webpack_require__.c = installedModules; // 将已经加载的模块存起来
- /******/ // __webpack_public_path__
- /******/ __webpack_require__.p = ""; // 设置的 publicPath
这里没什么可说的, 这里的 publicPath 对应于 output 中的配置, 如
- output: {
- publicPath: './dist/',
- path: path.resolve(__dirname, 'dist'),
- filename: '[name].JS'
- },
另外
- /******/ // define getter function for harmony exports
- /******/ __webpack_require__.d = function(exports, name, getter) {
- /******/ if(!__webpack_require__.o(exports, name)) {
- /******/ Object.defineProperty(exports, name, {
- enumerable: true, get: getter
- });
- /******/
- }
- /******/
- };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, {
- value: 'Module'
- });
- /******/
- }
- /******/ Object.defineProperty(exports, '__esModule', {
- value: true
- });
- /******/
- };
- /******/ // Object.prototype.hasOwnProperty.call
- /******/ __webpack_require__.o = function(object, property) {
- return Object.prototype.hasOwnProperty.call(object, property);
- };
这里 __webpack_require__.o 这里只是 hasOwnProperty 的包装
__webpack_require__.d 这里是对 exports 定义一个属性(当前模块未用到, 暂且如此, 理解不到位)
__webpack_require__.r 这里是对 es6 模块中的 export 的支持(当前模块未用到, 暂且如此, 理解不到位)
还有这个, 这个就更难理解了
- /******/ // 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 & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', {
- enumerable: true, value: value
- });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) {
- return value[key];
- }.bind(null, key));
- /******/ return ns;
- /******/
- };
- /******/
- /******/ // 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;
- /******/
- };
__webpack_require__.t 暂时不说明了, 还看不懂怎么调用的..
__webpack_require__.n 这个主要也是为 es6 模块服务的, 也没能理解好, 知道的可以在评论区留言哈~
2. 有依赖的单个模块
先使用最基础的 commonjs 模块规范 require, exports ,module.exports 有助于理解上面那个模块的导出项目
- ./main.JS
- let number = require('./number');
- console.log('main', number);
- ./number.JS
- let n = 10;
- exports.n = n;
编译后, 生成的文件变化的只是匿名函数传入的部分
- ./dist/main.JS
- // 省略
- /******/ ([
- /* 0 */
- /***/ (function(module, exports, __webpack_require__) {
- let number = __webpack_require__(1);
- console.log('main', number);
- /***/
- }),
- /* 1 */
- /***/ (function(module, exports) {
- let n = 10;
- exports.n = n;
- /***/
- })
- /******/ ]);
注意到前面的数字即是模块的 ID, 也可图中的一致
这里__webpack_require__参数被传进来, main.JS 中引入 number 这个模块 __webpack_require__(1);
number 模块中 exports.n = n, 注意这里的 exports 即是调用时的第二个参数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
所以此时 n 属性被存入 module 的 export 导出项中, 从而__webpack_require__(1) 就能获取这个导出项
换种方式, 使用 es6 的模块导出
更改 ./number.JS
- let n = 10;
- export {
- n
- };
编译后 ./dist/main.JS
- /******/ ([
- /* 0 */
- /***/ (function(module, exports, __webpack_require__) {
- let number = __webpack_require__(1);
- console.log('main', number);
- /***/
- }),
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() {
- return n;
- });
- let n = 10;
- /***/
- })
- /******/ ]);
可以看到模块 1 变了, 为了兼容 export , 使用 __webpack_require__.r 定义了它为 es6 模块, 再使用__webpack_require__.d 将 n 保存到模块的导出项中
__webpack_require__.d 函数中的 getter 即为 这里的 function() { return n; }, 通过设置为对象的 get 属性, 可以获取到 n 这个返回值
- var o = {
- };
- Object.defineProperty(o, 'abc', {
- get: function() {
- return 123;
- }
- });
- console.log(o.abc); // 123
所以将 let n = 10 定义在后面也是没问题的, 因为 getter 是在 number 模块被调用返回之后才使用的
接着, 我们把引入依赖文件改为 import
- ./main.JS
- import {
- n
- } from './number';
- console.log('main', n);
编译后 ./dist/main.JS
- /******/ ([
- /* 0 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["n"]);
- /***/
- }),
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() {
- return n;
- });
- let n = 10;
- /***/
- })
- /******/ ]);
同样的, 这时 main 模块用到了 es6 的模块引入方式, 所以 __webpack_require__.r(__webpack_exports__);
var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
这个 __webpack_require__(1) 实际上就是 number 模块的模块导出项, 自然就能取到属性 n 了
接下来, 着眼那个 default 字眼, 继续更换模块的导入导出方式
- ./main.JS
- import n from './number';
- console.log('main', n);
- ./number.JS
- let n = 10;
- export default n;
- ./dist/main.JS
- /******/ ([
- /* 0 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["default"]);
- /***/
- }),
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- let n = 10;
- /* harmony default export */ __webpack_exports__["default"] = (n);
- /***/
- })
- /******/ ]);
可以看到, 变化只是属性变成了 default
再来一种 es6 的方式
- ./main.JS
- import n from './number';
- console.log('main', n);
- ./number.JS
- import {
- str as n
- } from './str';
- export default n;
- ./str.JS
- export var str = 10;
编译后
- ./dist/main.JS
- /******/ ([
- /* 0 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["default"]);
- /***/
- }),
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _str__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
- /* harmony default export */ __webpack_exports__["default"] = (_str__WEBPACK_IMPORTED_MODULE_0__["str"]);
- /***/
- }),
- /* 2 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() {
- return str;
- });
- var str = 10;
- /***/
- })
- /******/ ]);
可以看到 {str as n} 也是没什么影响的, 通过上面的例子应该基本能理解模块的依赖了
3. 多个入口模块
如果不提取多模块之间的公共部分, 多个入口模块和单个的不同之处就是多了一个文件而已, 它们是独立的.
所以这里就不多说了
4. 异步加载模块
webpack 支持使用 require.ensure 来异步加载模块
- ./main.JS
- console.log('main');
- setTimeout(() => {
- require([], (require) => {
- let number = require('./number');
- console.log(number.n);
- });
- }, 1000);
- ./number.JS
- let n = 10;
- export {
- n
- };
webpack.config.JS 中要加上 publicPath, 防止异步模块加载路径出错
- output: {
- publicPath: './dist/',
- path: path.resolve(__dirname, 'dist'),
- filename: '[name].JS'
- }
编译后, 生成的 1.JS 即为异步的模块 number
- ./dist/1.JS
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[1],[
- /* 0 */,
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() {
- return n;
- });
- let n = 10;
- /***/
- })
- ]]);
- 可以看到, 这里首先获取 (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []), 再调用 push 传入模块及其依赖
- JSONP 类似我们跨域中的动态插入脚本, 这里也是一样, 动态插入一个 script 标签, 把 src 设置好就加载这个异步模块了
- push 参数中第一个为当前异步模块
- 看看 ./dist/main.JS
- /******/ (function(modules) {
- // webpackBootstrap
- /******/ // install a JSONP callback for chunk loading
- /******/ function webpackJsonpCallback(data) {
- /******/ var chunkIds = data[0];
- /******/ var moreModules = data[1];
- /******/
- /******/
- /******/ // add "moreModules" to the modules object,
- /******/ // then flag all "chunkIds" as loaded and fire callback
- /******/ var moduleId, chunkId, i = 0, resolves = [];
- /******/ for(;i <chunkIds.length; i++) {
- /******/ chunkId = chunkIds[i];
- /******/ if(installedChunks[chunkId]) {
- /******/ resolves.push(installedChunks[chunkId][0]);
- /******/
- }
- /******/ installedChunks[chunkId] = 0;
- /******/
- }
- /******/ for(moduleId in moreModules) {
- /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
- /******/ modules[moduleId] = moreModules[moduleId];
- /******/
- }
- /******/
- }
- /******/ if(parentJsonpFunction) parentJsonpFunction(data);
- /******/
- /******/ while(resolves.length) {
- /******/ resolves.shift()();
- /******/
- }
- /******/
- /******/
- };
- /******/
- /******/
- /******/ // The module cache
- /******/ var installedModules = {
- };
- /******/
- /******/ // object to store loaded and loading chunks
- /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
- /******/ // Promise = chunk loading, 0 = chunk loaded
- /******/ var installedChunks = {
- /******/ 0: 0
- /******/
- };
- /******/
- /******/
- /******/
- /******/ // script path function
- /******/ function jsonpScriptSrc(chunkId) {
- /******/ return __webpack_require__.p + ""+ ({}[chunkId]||chunkId) +".JS"
- /******/ }
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/ }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId,
- /******/ l: false,
- /******/ exports: {}
- /******/ };
- /******/
- /******/ // Execute the module function
- /******/ 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;
- /******/ }
- /******/
- /******/ // This file contains only the entry chunk.
- /******/ // The chunk loading function for additional chunks
- /******/ __webpack_require__.e = function requireEnsure(chunkId) {
- /******/ var promises = [];
- /******/
- /******/
- /******/ // JSONP chunk loading for JavaScript
- /******/
- /******/ var installedChunkData = installedChunks[chunkId];
- /******/ if(installedChunkData !== 0) { // 0 means "already installed".
- /******/
- /******/ // a Promise means "currently loading".
- /******/ if(installedChunkData) {
- /******/ promises.push(installedChunkData[2]);
- /******/ } else {
- /******/ // setup Promise in chunk cache
- /******/ var promise = new Promise(function(resolve, reject) {
- /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
- /******/ });
- /******/ promises.push(installedChunkData[2] = promise);
- /******/
- /******/ // start chunk loading
- /******/ var head = document.getElementsByTagName('head')[0];
- /******/ var script = document.createElement('script');
- /******/ var onScriptComplete;
- /******/
- /******/ script.charset = 'utf-8';
- /******/ script.timeout = 120;
- /******/ if (__webpack_require__.nc) {
- /******/ script.setAttribute("nonce", __webpack_require__.nc);
- /******/ }
- /******/ script.src = jsonpScriptSrc(chunkId);
- /******/
- /******/ onScriptComplete = function (event) {
- /******/ // avoid mem leaks in IE.
- /******/ script.onerror = script.onload = null;
- /******/ clearTimeout(timeout);
- /******/ var chunk = installedChunks[chunkId];
- /******/ if(chunk !== 0) {
- /******/ if(chunk) {
- /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
- /******/ var realSrc = event && event.target && event.target.src;
- /******/ var error = new Error('Loading chunk' + chunkId + 'failed.\n(' + errorType + ':' + realSrc + ')');
- /******/ error.type = errorType;
- /******/ error.request = realSrc;
- /******/ chunk[1](error);
- /******/ }
- /******/ installedChunks[chunkId] = undefined;
- /******/ }
- /******/ };
- /******/ var timeout = setTimeout(function(){
- /******/ onScriptComplete({ type: 'timeout', target: script });
- /******/ }, 120000);
- /******/ script.onerror = script.onload = onScriptComplete;
- /******/ head.appendChild(script);
- /******/ }
- /******/ }
- /******/ return Promise.all(promises);
- /******/ };
- /******/
- /******/ // 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, { enumerable: true, get: getter });
- /******/ }
- /******/ };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
- /******/ }
- /******/ Object.defineProperty(exports, '__esModule', { value: true });
- /******/ };
- /******/
- /******/ // 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 & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
- /******/ return ns;
- /******/ };
- /******/
- /******/ // 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 = "./dist/";
- /******/
- /******/ // on error function for async loading
- /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
- /******/
- /******/ var jsonpArray = Windows["webpackJsonp"] = Windows["webpackJsonp"] || [];
- /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
- /******/ jsonpArray.push = webpackJsonpCallback;
- /******/ jsonpArray = jsonpArray.slice();
- /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
- /******/ var parentJsonpFunction = oldJsonpFunction;
- /******/
- /******/
- /******/ // Load entry module and return exports
- /******/ return __webpack_require__(__webpack_require__.s = 0);
- /******/
- })
- /************************************************************************/
- /******/ ([
- /* 0 */
- /***/ (function(module, exports, __webpack_require__) {
- console.log('main');
- setTimeout(() => {
- __webpack_require__.e(/* AMD require */ 1).then(function() {
- var __WEBPACK_AMD_REQUIRE_ARRAY__ = []; ((require) => {
- let number = __webpack_require__(1);
- console.log(number.n);
- }).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);
- }).catch(__webpack_require__.oe);
- }, 1000);
- /***/
- })
- /******/ ]);
这下蹦出了许多代码, 从这里开始会比较绕, 需要有耐心!
按照代码执行顺序来分析, 思路就清晰了
38 行中定义了 installedChunks 这个新变量, 它指代依赖模块(不仅包括此处的异步模块, 也包括后续会说到的公共模块, runtime 模块等), 而上面 installedModules 指的是所有的模块
- /******/ // object to store loaded and loading chunks
- /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
- /******/ // Promise = chunk loading, 0 = chunk loaded
- /******/ var installedChunks = {
- /******/ 0: 0
- /******/
- };
前面的 0 表示模块 ID, 在这里指的就是 ./main.JS 这个入口模块了, 它初始的状态就被 webpack 设置成已加载
- /******/ // script path function
- /******/ function jsonpScriptSrc(chunkId) {
- /******/ return __webpack_require__.p + ""+ ({}[chunkId]||chunkId) +".JS"
- /******/
- }
这里就是异步模块的路径了,({}[chunkId]||chunkId) 这个只是为了防止出错做的处理
__webpack_require__ 函数的内容没变
75 行多了一个 __webpack_require__.e 用来加载异步模块, 这个稍后再讲
继续到 182 行开始
- /******/ // __webpack_public_path__
- /******/ __webpack_require__.p = "./dist/";
- /******/
- /******/ // on error function for async loading
- /******/ __webpack_require__.oe = function(err) {
- console.error(err); throw err;
- };
- /******/
- /******/ var jsonpArray = Windows["webpackJsonp"] = Windows["webpackJsonp"] || [];
- /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
- /******/ jsonpArray.push = webpackJsonpCallback;
- /******/ jsonpArray = jsonpArray.slice();
- /******/ for(var i = 0; i <jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
- /******/ var parentJsonpFunction = oldJsonpFunction;
- /******/
- /******/
- /******/ // Load entry module and return exports
- /******/ return __webpack_require__(__webpack_require__.s = 0);
这里的 publicPath 就是我们刚刚设置的
__webpack_require__.oe 只是用于处理错误
初始会判断是否有 Windows["webpackJsonp"]存在, 有的话就缓存起来, 并将 this 的指向设置好 jsonpArray.push.bind(jsonpArray)
要理清楚 jsonpArray.push , 它不是简单的数组, 所以有些绕, 它指向了第 3 行 webpackJsonpCallback 这个函数
如果初始已经有待加载的依赖模块, 则在 for 循环中直接加载. 此处初始阶段是没有值的, 所以可以直接略过
要看明白 webpackJsonpCallback 这个函数, 得从调用它的地方开始, 在 216 行中开始调用
- setTimeout(() => {
- __webpack_require__.e(/* AMD require */ 1).then(function() {
- var __WEBPACK_AMD_REQUIRE_ARRAY__ = []; ((require) => {
- let number = __webpack_require__(1);
- console.log(number.n);
- }).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);
- }).catch(__webpack_require__.oe);
- }, 1000);
- /******/ // This file contains only the entry chunk.
- /******/ // The chunk loading function for additional chunks
- /******/ __webpack_require__.e = function requireEnsure(chunkId) {
- /******/ var promises = []; // promise 队列, 支持模块加载完成后多个异步回调
- /******/
- /******/
- /******/ // JSONP chunk loading for JavaScript
- /******/
- /******/ var installedChunkData = installedChunks[chunkId];
- // 未加载
- /******/ if(installedChunkData !== 0) {
- // 0 means "already installed".
- /******/
- /******/ // a Promise means "currently loading".
- // 加载中, 则支持下一个回调加入
- /******/ if(installedChunkData) {
- /******/ promises.push(installedChunkData[2]);
- /******/
- } else {
- // 初始化一个 promise 来加载
- /******/ // setup Promise in chunk cache
- /******/ var promise = new Promise(function(resolve, reject) {
- // 将 resolve 和 reject 存入模块中, 方便其他地方调用
- /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
- /******/
- });
- // installedChunkData 的第三项即为一个 promise 对象, 并存入 promises 队列中
- /******/ promises.push(installedChunkData[2] = promise);
- /******/
- /******/ // start chunk loading
- /******/ var head = document.getElementsByTagName('head')[0];
- /******/ var script = document.createElement('script');
- /******/ var onScriptComplete;
- /******/
- /******/ script.charset = 'utf-8';
- /******/ script.timeout = 120;
- /******/ if (__webpack_require__.nc) {
- /******/ script.setAttribute("nonce", __webpack_require__.nc);
- /******/
- }
- // 设置异步模块的路径
- /******/ script.src = jsonpScriptSrc(chunkId);
- /******/
- /******/ onScriptComplete = function (event) {
- /******/ // avoid mem leaks in IE.
- /******/ script.onerror = script.onload = null;
- /******/ clearTimeout(timeout);
- /******/ var chunk = installedChunks[chunkId];
- /******/ if(chunk !== 0) {
- /******/ if(chunk) {
- /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
- /******/ var realSrc = event && event.target && event.target.src;
- /******/ var error = new Error('Loading chunk' + chunkId + 'failed.\n(' + errorType + ':' + realSrc + ')');
- /******/ error.type = errorType;
- /******/ error.request = realSrc;
- // 调用 reject
- /******/ chunk[1](error);
- /******/
- }
- /******/ installedChunks[chunkId] = undefined;
- /******/
- }
- /******/
- };
- /******/ var timeout = setTimeout(function(){
- /******/ onScriptComplete({
- type: 'timeout', target: script
- });
- /******/
- }, 120000);
- /******/ script.onerror = script.onload = onScriptComplete;
- // 在 head 标签中插入脚本
- /******/ head.appendChild(script);
- /******/
- }
- /******/
- }
- /******/ return Promise.all(promises);
- /******/
- };
一秒钟后加载这个异步模块 ./1.JS , 该模块加载完成后就开始执行
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[1],[
- /* 0 */,
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() {
- return n;
- });
- let n = 10;
- /***/
- })
- ]]);
- 此时的 Windows["webpackJsonp"] 已经被这句代码影响, jsonpArray.push = webpackJsonpCallback; 所以 push 实际上调用的是 webpackJsonpCallback 函数
- /******/ // install a JSONP callback for chunk loading
- /******/ function webpackJsonpCallback(data) {
- /******/ var chunkIds = data[0]; // 依赖的模块 ID, 此时是[1]
- /******/ var moreModules = data[1]; // 依赖的模块内容
- /******/
- /******/
- /******/ // add "moreModules" to the modules object,
- /******/ // then flag all "chunkIds" as loaded and fire callback
- /******/ var moduleId, chunkId, i = 0, resolves = [];
- // 遍历依赖的模块进行加载
- /******/ for(;i <chunkIds.length; i++) {
- /******/ chunkId = chunkIds[i];
- /******/ if(installedChunks[chunkId]) {
- /******/ resolves.push(installedChunks[chunkId][0]); // 存储将要执行的 resolve
- /******/
- }
- /******/ installedChunks[chunkId] = 0; // 标记已加载
- /******/
- }
- /******/ for(moduleId in moreModules) {
- /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
- /******/ modules[moduleId] = moreModules[moduleId]; // 更新模块组
- /******/
- }
- /******/
- }
- /******/ if(parentJsonpFunction) parentJsonpFunction(data);
- /******/
- /******/ while(resolves.length) {
- /******/ resolves.shift()(); // 执行所有 resolve
- /******/
- }
- /******/
- /******/
- };
如果多依赖一个呢
- ./main.JS
- console.log('main');
- setTimeout(() => {
- require(['./str'], (require) => {
- let number = require('./number');
- console.log(number.n);
- });
- }, 1000);
这时只有 ./1.JS 改变了, 差不不大, 一样的道理
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[1],[
- /* 0 */,
- /* 1 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() {
- return str;
- });
- var str = 10;
- /***/
- }),
- /* 2 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() {
- return n;
- });
- let n = 10;
- /***/
- })
- ]]);
- 5. 提取公共模块
- ./webpack.config.JS
- entry: {
- main: './main',
- test: './test'
- },
- optimization: {
- // 提取公共部分为 common.JS, 使劲地提取吧..
- splitChunks: {
- name: 'common',
- chunks: 'all',
- minSize: 1
- }
- },
- ./main.JS
- import './chunk';
- import {
- n
- } from './number';
- console.log('main', n);
- ./test.JS
- import './chunk';
- console.log('test');
- 编译后
- ./dist/common.JS
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[1],[
- /* 0 */,
- /* 1 */
- /***/ (function(module, exports) {
- console.log('chunk');
- /***/
- })
- ]]);
可以看到 chunk 模块 (ID 为 1) 被共用, 被提取出来
再看看 ./dist/test.JS
- /******/ (function(modules) {
- // webpackBootstrap
- /******/ // install a JSONP callback for chunk loading
- /******/ function webpackJsonpCallback(data) {
- /******/ var chunkIds = data[0];
- /******/ var moreModules = data[1];
- /******/ var executeModules = data[2];
- /******/
- /******/ // add "moreModules" to the modules object,
- /******/ // then flag all "chunkIds" as loaded and fire callback
- /******/ var moduleId, chunkId, i = 0, resolves = [];
- /******/ for(;i <chunkIds.length; i++) {
- /******/ chunkId = chunkIds[i];
- /******/ if(installedChunks[chunkId]) {
- /******/ resolves.push(installedChunks[chunkId][0]);
- /******/
- }
- /******/ installedChunks[chunkId] = 0;
- /******/
- }
- /******/ for(moduleId in moreModules) {
- /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
- /******/ modules[moduleId] = moreModules[moduleId];
- /******/
- }
- /******/
- }
- /******/ if(parentJsonpFunction) parentJsonpFunction(data);
- /******/
- /******/ while(resolves.length) {
- /******/ resolves.shift()();
- /******/
- }
- /******/
- /******/ // add entry modules from loaded chunk to deferred list
- /******/ deferredModules.push.apply(deferredModules, executeModules || []);
- /******/
- /******/ // run deferred modules when all chunks ready
- /******/ return checkDeferredModules();
- /******/
- };
- /******/ function checkDeferredModules() {
- /******/ var result;
- /******/ for(var i = 0; i < deferredModules.length; i++) {
- /******/ var deferredModule = deferredModules[i];
- /******/ var fulfilled = true;
- /******/ for(var j = 1; j < deferredModule.length; j++) {
- /******/ var depId = deferredModule[j];
- /******/ if(installedChunks[depId] !== 0) fulfilled = false;
- /******/
- }
- /******/ if(fulfilled) {
- /******/ deferredModules.splice(i--, 1);
- /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
- /******/
- }
- /******/
- }
- /******/ return result;
- /******/
- }
- /******/
- /******/ // The module cache
- /******/ var installedModules = {
- };
- /******/
- /******/ // object to store loaded and loading chunks
- /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
- /******/ // Promise = chunk loading, 0 = chunk loaded
- /******/ var installedChunks = {
- /******/ 2: 0
- /******/
- };
- /******/
- /******/ var deferredModules = [];
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/
- }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId,
- /******/ l: false,
- /******/ exports: {
- }
- /******/
- };
- /******/
- /******/ // Execute the module function
- /******/ 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;
- /******/
- }
- /******/
- /******/
- /******/ // 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, {
- enumerable: true, get: getter
- });
- /******/
- }
- /******/
- };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, {
- value: 'Module'
- });
- /******/
- }
- /******/ Object.defineProperty(exports, '__esModule', {
- value: true
- });
- /******/
- };
- /******/
- /******/ // 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 & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', {
- enumerable: true, value: value
- });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) {
- return value[key];
- }.bind(null, key));
- /******/ return ns;
- /******/
- };
- /******/
- /******/ // 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 = "./dist/";
- /******/
- /******/ var jsonpArray = Windows["webpackJsonp"] = Windows["webpackJsonp"] || [];
- /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
- /******/ jsonpArray.push = webpackJsonpCallback;
- /******/ jsonpArray = jsonpArray.slice();
- /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
- /******/ var parentJsonpFunction = oldJsonpFunction;
- /******/
- /******/
- /******/ // add entry module to deferred list
- /******/ deferredModules.push([3,1]);
- /******/ // run deferred modules when ready
- /******/ return checkDeferredModules();
- /******/
- })
- /************************************************************************/
- /******/ ({
- /***/ 3:
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_chunk__WEBPACK_IMPORTED_MODULE_0__);
- console.log('test');
- /***/
- })
- /******/
- });
- 先看 150 行, 初始不再马上加载入口模块, 而是先将入口模块和其依赖的公共模块保存起来, 再进行处理加载
- /******/ // add entry module to deferred list
- /******/ deferredModules.push([3,1]); // 这里的 3 为 test 模块, 1 为 chunk 公共模块
- /******/ // run deferred modules when ready
- /******/ return checkDeferredModules();
- /******/ function checkDeferredModules() {
- /******/ var result;
- // deferredModules 的结构长这样 [[3,1]], 对每一项进行处理
- /******/ for(var i = 0; i < deferredModules.length; i++) {
- /******/ var deferredModule = deferredModules[i];
- /******/ var fulfilled = true;
- // 从第二项开始, 为依赖的模块
- /******/ for(var j = 1; j < deferredModule.length; j++) {
- /******/ var depId = deferredModule[j];
- // 依赖的模块未加载
- /******/ if(installedChunks[depId] !== 0) fulfilled = false;
- /******/
- }
- // 已经加载, 则清除, 并开始加载入口模块, deferredModule 的第一项即为这里的 test 入口模块
- /******/ if(fulfilled) {
- /******/ deferredModules.splice(i--, 1);
- /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
- /******/
- }
- /******/
- }
- /******/ return result;
- /******/
- }
- 注意到这里也有 webpackJsonpCallback 函数, 不过它的参数数组中有三项, 第三项 var executeModules = data[2]; 暂时还没用到, 先略过
- /******/ // add entry modules from loaded chunk to deferred list
- /******/ deferredModules.push.apply(deferredModules, executeModules || []);
- /******/
- /******/ // run deferred modules when all chunks ready
- /******/ return checkDeferredModules();
- 上面这个, 主要是为了兼容公共模块和入口模块的兼容顺序, 什么意思呢?
- 假如没有这段代码, 那么这样是可行的
- <script type="text/JavaScript" src="./dist/common.JS"></script>
- <script type="text/JavaScript" src="./dist/main.JS"></script>
- 但 common 放后面就不行
- <script type="text/JavaScript" src="./dist/main.JS"></script>
- <script type="text/JavaScript" src="./dist/common.JS"></script>
- common 放在后面会导致初始调用 checkDeferredModules 时 公共模块的 fulfilled 为 false, 此时将无法加载入口模块
- 所以需要在 webpackJsonpCallback 中再判断处理一次
- 6. 提取 runtime 运行时模块
- 上面代码中,./dist/main.JS 和 ./dist/test.JS 都有很多运行时的代码, 我们可以将其提取出来, 一并放到 common.JS 中
- ./webpack.config.JS
- optimization: {
- // 提取 runtime 代码到 common.JS 文件中
- runtimeChunk: {
- name: 'common'
- },
- // 提取公共部分为 common.JS, 使劲地提取吧..
- splitChunks: {
- name: 'common',
- chunks: 'all',
- minSize: 1
- }
- },
- 编译后, 看看 ./dist/test.JS 干净了许多
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[2],{
- /***/ 3:
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_chunk__WEBPACK_IMPORTED_MODULE_0__);
- console.log('test');
- /***/
- })
- },[[3,1]]]);
- 不过, 注意到这里 push 的参数多了第三项 [[3,1]], 根据上面的分析, 这个 1 应该就是公共模块了
- 来看看 ./dist/common.JS
- /******/ (function(modules) {
- // webpackBootstrap
- /******/ // install a JSONP callback for chunk loading
- /******/ function webpackJsonpCallback(data) {
- /******/ var chunkIds = data[0];
- /******/ var moreModules = data[1];
- /******/ var executeModules = data[2];
- /******/
- /******/ // add "moreModules" to the modules object,
- /******/ // then flag all "chunkIds" as loaded and fire callback
- /******/ var moduleId, chunkId, i = 0, resolves = [];
- /******/ for(;i <chunkIds.length; i++) {
- /******/ chunkId = chunkIds[i];
- /******/ if(installedChunks[chunkId]) {
- /******/ resolves.push(installedChunks[chunkId][0]);
- /******/
- }
- /******/ installedChunks[chunkId] = 0;
- /******/
- }
- /******/ for(moduleId in moreModules) {
- /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
- /******/ modules[moduleId] = moreModules[moduleId];
- /******/
- }
- /******/
- }
- /******/ if(parentJsonpFunction) parentJsonpFunction(data);
- /******/
- /******/ while(resolves.length) {
- /******/ resolves.shift()();
- /******/
- }
- /******/
- /******/ // add entry modules from loaded chunk to deferred list
- /******/ deferredModules.push.apply(deferredModules, executeModules || []);
- /******/
- /******/ // run deferred modules when all chunks ready
- /******/ return checkDeferredModules();
- /******/
- };
- /******/ function checkDeferredModules() {
- /******/ var result;
- /******/ for(var i = 0; i < deferredModules.length; i++) {
- /******/ var deferredModule = deferredModules[i];
- /******/ var fulfilled = true;
- /******/ for(var j = 1; j < deferredModule.length; j++) {
- /******/ var depId = deferredModule[j];
- /******/ if(installedChunks[depId] !== 0) fulfilled = false;
- /******/
- }
- /******/ if(fulfilled) {
- /******/ deferredModules.splice(i--, 1);
- /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
- /******/
- }
- /******/
- }
- /******/ return result;
- /******/
- }
- /******/
- /******/ // The module cache
- /******/ var installedModules = {
- };
- /******/
- /******/ // object to store loaded and loading chunks
- /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
- /******/ // Promise = chunk loading, 0 = chunk loaded
- /******/ var installedChunks = {
- /******/ 1: 0
- /******/
- };
- /******/
- /******/ var deferredModules = [];
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/
- }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId,
- /******/ l: false,
- /******/ exports: {
- }
- /******/
- };
- /******/
- /******/ // Execute the module function
- /******/ 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;
- /******/
- }
- /******/
- /******/
- /******/ // 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, {
- enumerable: true, get: getter
- });
- /******/
- }
- /******/
- };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, {
- value: 'Module'
- });
- /******/
- }
- /******/ Object.defineProperty(exports, '__esModule', {
- value: true
- });
- /******/
- };
- /******/
- /******/ // 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 & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', {
- enumerable: true, value: value
- });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) {
- return value[key];
- }.bind(null, key));
- /******/ return ns;
- /******/
- };
- /******/
- /******/ // 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 = "./dist/";
- /******/
- /******/ var jsonpArray = Windows["webpackJsonp"] = Windows["webpackJsonp"] || [];
- /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
- /******/ jsonpArray.push = webpackJsonpCallback;
- /******/ jsonpArray = jsonpArray.slice();
- /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
- /******/ var parentJsonpFunction = oldJsonpFunction;
- /******/
- /******/
- /******/ // run deferred modules from other chunks
- /******/ checkDeferredModules();
- /******/
- })
- /************************************************************************/
- /******/ ([
- /* 0 */,
- /* 1 */
- /***/ (function(module, exports) {
- console.log('chunk');
- /***/
- })
- /******/ ]);
58 行直接将 chunk 模块设置为已加载了, 因为它现在处于 common 模块中, 初始就是已加载
- /******/ var installedChunks = {
- /******/ 1: 0
- /******/
- };
而 150 行上面不再出现 deferredModules 的赋值, 它由 ./dist/test.JS 的第三个参数传入来更新
- var executeModules = data[2];
- .
- .
- .
- /******/ // add entry modules from loaded chunk to deferred list
- /******/ deferredModules.push.apply(deferredModules, executeModules || []);
- /******/
- /******/ // run deferred modules when all chunks ready
- /******/ return checkDeferredModules();
7. 开发一个 loader, 加载模块
loader 会参与到模块的编译中, 并输出到生成的文件里. 这里用个例子来说明一下
开发一个 loader, 原理很简单, 其实就是传入参数, 就可以自行处理了
- ./loader.JS
- const loaderUtils = require('loader-utils');
- /**
- * 简单的 loader
- * @param {
- [type]
- } content [description]
- * @return {
- [type]
- } [description]
- */
- module.exports = function(content) {
- // 获取 loader 的参数
- let options = loaderUtils.getOptions(this);
- console.log('loader-options', options);
- console.log(content.split(/\r\n|\r|\n/g));
- // 做一些处理, 并返回即可
- this.callback(null, JSON.stringify(content.split(/\r\n|\r|\n/g)));
- };
- ./webpack.config.JS
- ...
- module: {
- rules: [{
- test: /\.CSS$/,
- loaders: [{
- loader: path.resolve('./loader.JS'),
- options: {
- CSS: 123
- }
- }]
- }]
- },
- ./test.CSS
- .home {
- width: 100px;
- height: 200px;
- }
- ./main.JS
- import './test.CSS';
- console.log('main');
编译后
- ./dist/main.JS
- (Windows["webpackJsonp"] = Windows["webpackJsonp"] || []).push([[1],[
- /* 0 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony import */ var _test_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
- /* harmony import */ var _test_css__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_test_css__WEBPACK_IMPORTED_MODULE_0__);
- console.log('main');
- /***/
- }),
- /* 1 */
- /***/ (function(module, exports) {
- ["",".home {
- "," width: 100px;"," height: 200px;","
- }",""]
- /***/
- })
- ],[[0,0]]]);
这里的模块 0 其实就是 ./main.JS 了, 模块 1 是 test.CSS, 可以看到 CSS 经过 loader 解析之后, 内容是会扔到生成的文件里面的
[[0,0]] 是 webpack 初始化生成的, 这里不必理会
8. 开发一个插件 plugin, 加载模块
使用一个插件, 看看插件是怎么和编译过程结合起来的
为了简便, 这里就自行开发一个简单的插件
开发插件可以类似 webpack 那样, 基于 tapable 进行开发, 使用 订阅 - 发布 模式
先配置一些 ./webpack.config.JS
- const webpack = require('webpack');
- const path = require('path');
- const todayPlugin = require('./todayPlugin.JS');
- module.exports = {
- ...
- plugins: [
- new todayPlugin({
- test: 123
- })
- ]
- };
- ./todayPlugin.JS
- // 使用 SyncHook
- const {
- SyncHook
- } = require('tapable');
- /**
- * 自定义的插件
- */
- class todayPlugin {
- constructor(options) {
- // 获取插件的参数
- this.options = options;
- console.log('plugin-options', this.options);
- }
- /**
- * 提供 webpack 对插件进行调用
- * @param {
- [type]
- } compiler [description]
- * @return {
- [type]
- } [description]
- */
- apply(compiler) {
- // 实例化, 创建一个 hook
- compiler.hooks.todayHook = new SyncHook(['day']);
- // 事件订阅, 这里的 day 参数需要和实例化时传递的参数一致
- compiler.hooks.todayHook.tap('logToday', (day) => {
- console.log('today', day);
- });
- // 选择在 webpack 的 compiler done 触发时做处理
- compiler.hooks.done.tap('setToday', () => {
- // 触发我们的事件(即事件发布)
- compiler.hooks.todayHook.call(new Date);
- });
- }
- }
- module.exports = todayPlugin;
编译后
在生成的文件中, 并没有看到踪迹
当然了, 也不能由此就得出结论插件不会影响到生成的文件, 只是看起来如此
编译结果就分析到这里了, 说实话, 非常乱 .......
具体到底是由源码里面哪段代码控制的, 就不得而知了, 源码实在是庞大, 目前定位到两个比较关键的文件, 脑壳不疼的时候再看吧
来源: https://www.cnblogs.com/imwtr/p/9770135.html