经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JSJS库框架 » JavaScript » 查看文章
Webpack编译结果浅析-渔人码头-
来源:cnblogs  作者:-渔人码头-  时间:2018/10/11 9:20:35  对本文有异议

如今Webpack已经是一个不可或缺的前端构建工具,借助这个构建工具,我们可以使用比较新的技术(浏览器不能直接支持)来开发。

你是否好奇你写的代码经过Webpack构建之后会生成什么东西?是否有时调试遇到莫名其妙的问题?

本文不讲如何进行配置,只是基于几个基础的例子,简要分析一下 webpack@4.20.2 构建后的代码结构,当然了,并不全面,时间问题能力问题还不能理解到位。

代码比较长,生成的代码也比较晦涩比较绕,也可能条理不顺,客官坐好咧~

 

 

 

一、Webpack的运行机制

Webpack的运行过程实际上可以归纳为这个步骤

读取配置参数 -> 相关事件绑定(插件参与) ->  识别各入口Entry模块 -> 编译文件(loader参与)-> 生成文件

首先读取我们的配置文件如 webpack.config.js,然后事件流就参与进来绑定相关的事件,Webpack中的事件使用 Tapable 来管理,在这一阶段,除了绑定webpack内置的一大堆事件之外,还支持自定义的一些事件处理。

配置中的 plugins部分,实际上也可以看作是一些自定义的事件处理,因为插件将在定义的”相关时刻“插入到编译过程中处理资源,这里的”相关时刻“指的就是 订阅-发布 模式中的发布环节

webpack支持多个入口模块,所以还需要进行各入口模块的分析(这里的入口模块只能为JS模块),比如以下两个入口模块

分析完入口模块,接下来分析该模块的依赖,并使用相关loader进行编译(如果需要loader的话),真正的编译环节是在这里。

期间会使用AST抽象语法树来分析语法,直到编译完成,输出到相应的文件中

可以来看看这篇文章 Webpack运行机制

 

二、Webpack编译结果

由最简单的例子开始

2.1 无依赖的单个模块

./main.js

  1. console.log('main');

./webpack.config.js

  1. module.exports = {
  2. // entry: './main',
  3. entry: {
  4. main: './main'
  5. },
  6. mode: 'none',
  7. output: {
  8. path: path.resolve(__dirname, 'dist'),
  9. filename: '[name].js'
  10. }
  11. };

注意,在webpack4中默认的mode对 development和production进行了一些特殊配置,为了简化,这里就设置成none

编译一个文件,将在dist目录中生成

./dist/main.js

  1. 1 /******/ (function(modules) { // webpackBootstrap
  2. 2 /******/ // The module cache
  3. 3 /******/ var installedModules = {};
  4. 4 /******/
  5. 5 /******/ // The require function
  6. 6 /******/ function __webpack_require__(moduleId) {
  7. 7 /******/
  8. 8 /******/ // Check if module is in cache
  9. 9 /******/ if(installedModules[moduleId]) {
  10. 10 /******/ return installedModules[moduleId].exports;
  11. 11 /******/ }
  12. 12 /******/ // Create a new module (and put it into the cache)
  13. 13 /******/ var module = installedModules[moduleId] = {
  14. 14 /******/ i: moduleId,
  15. 15 /******/ l: false,
  16. 16 /******/ exports: {}
  17. 17 /******/ };
  18. 18 /******/
  19. 19 /******/ // Execute the module function
  20. 20 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  21. 21 /******/
  22. 22 /******/ // Flag the module as loaded
  23. 23 /******/ module.l = true;
  24. 24 /******/
  25. 25 /******/ // Return the exports of the module
  26. 26 /******/ return module.exports;
  27. 27 /******/ }
  28. 28 /******/
  29. 29 /******/
  30. 30 /******/ // expose the modules object (__webpack_modules__)
  31. 31 /******/ __webpack_require__.m = modules;
  32. 32 /******/
  33. 33 /******/ // expose the module cache
  34. 34 /******/ __webpack_require__.c = installedModules;
  35. 35 /******/
  36. 36 /******/ // define getter function for harmony exports
  37. 37 /******/ __webpack_require__.d = function(exports, name, getter) {
  38. 38 /******/ if(!__webpack_require__.o(exports, name)) {
  39. 39 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  40. 40 /******/ }
  41. 41 /******/ };
  42. 42 /******/
  43. 43 /******/ // define __esModule on exports
  44. 44 /******/ __webpack_require__.r = function(exports) {
  45. 45 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  46. 46 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  47. 47 /******/ }
  48. 48 /******/ Object.defineProperty(exports, '__esModule', { value: true });
  49. 49 /******/ };
  50. 50 /******/
  51. 51 /******/ // create a fake namespace object
  52. 52 /******/ // mode & 1: value is a module id, require it
  53. 53 /******/ // mode & 2: merge all properties of value into the ns
  54. 54 /******/ // mode & 4: return value when already ns object
  55. 55 /******/ // mode & 8|1: behave like require
  56. 56 /******/ __webpack_require__.t = function(value, mode) {
  57. 57 /******/ if(mode & 1) value = __webpack_require__(value);
  58. 58 /******/ if(mode & 8) return value;
  59. 59 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  60. 60 /******/ var ns = Object.create(null);
  61. 61 /******/ __webpack_require__.r(ns);
  62. 62 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  63. 63 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  64. 64 /******/ return ns;
  65. 65 /******/ };
  66. 66 /******/
  67. 67 /******/ // getDefaultExport function for compatibility with non-harmony modules
  68. 68 /******/ __webpack_require__.n = function(module) {
  69. 69 /******/ var getter = module && module.__esModule ?
  70. 70 /******/ function getDefault() { return module['default']; } :
  71. 71 /******/ function getModuleExports() { return module; };
  72. 72 /******/ __webpack_require__.d(getter, 'a', getter);
  73. 73 /******/ return getter;
  74. 74 /******/ };
  75. 75 /******/
  76. 76 /******/ // Object.prototype.hasOwnProperty.call
  77. 77 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  78. 78 /******/
  79. 79 /******/ // __webpack_public_path__
  80. 80 /******/ __webpack_require__.p = "";
  81. 81 /******/
  82. 82 /******/
  83. 83 /******/ // Load entry module and return exports
  84. 84 /******/ return __webpack_require__(__webpack_require__.s = 0);
  85. 85 /******/ })
  86. 86 /************************************************************************/
  87. 87 /******/ ([
  88. 88 /* 0 */
  89. 89 /***/ (function(module, exports) {
  90. 90
  91. 91
  92. 92 console.log('main');
  93. 93
  94. 94
  95. 95 /***/ })
  96. 96 /******/ ]);

可以看到首先是一个匿名函数,在87行时自执行传入

  1. [
  2. /* 0 */
  3. /***/ (function(module, exports) {
  4. console.log('main');
  5. /***/ })
  6. /******/ ]

这个是modules,表示有一个模块需要加载

第3行使用 installedModules 来缓存已经加载的模块

webpack由最初支持 commonjs模块规范,到后来要支持es6的模块等,为了兼容不同的模块机制,定义了一个 __webpack_require__ 函数作为webpack内部的require

  1. /******/ // The require function
  2. /******/ function __webpack_require__(moduleId) {
  3. /******/
  4. /******/ // Check if module is in cache
  5. // 如果模块已经加载则直接使用
  6. /******/ if(installedModules[moduleId]) {
  7. /******/ return installedModules[moduleId].exports;
  8. /******/ }
  9. /******/ // Create a new module (and put it into the cache)
  10. /******/ var module = installedModules[moduleId] = {
  11. /******/ i: moduleId, // 模块ID
  12. /******/ l: false, // 模块是否已加载
  13. /******/ exports: {} // 模块的导出项
  14. /******/ };
  15. /******/
  16. /******/ // Execute the module function
  17. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  18. /******/
  19. /******/ // Flag the module as loaded
  20. /******/ module.l = true; // 标记已经加载
  21. /******/
  22. /******/ // Return the exports of the module
  23. /******/ return module.exports; // 返回模块的导出项目
  24. /******/ }

其中,这个调用非常重要

  1. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)

结合匿名函数传入的参数来看,modules[moduleId] 其实就是这个

  1. (function(module, exports) {
  2. console.log('main');
  3. /***/ })

第一个参数 module.exports 实际上就是上面模块的导出项,是为了保证this能正确地指向module,第二第三个参数按着顺序来,第四个参数一般用于依赖

因为这里 main.js没有依赖其他模块,所以没有传进来

最后 return module.exports; 实际上就是返回了模块的导出项,在上面的84行中,入口模块被引入 。从而自动地加载第一个模块并执行

  1. return __webpack_require__(__webpack_require__.s = 0); // __webpack_require__.s为入口文件,此处引用模块ID

另外再看其它代码,

  1. /******/ // expose the modules object (__webpack_modules__)
  2. /******/ __webpack_require__.m = modules; // 将模块存起来
  3. /******/
  4. /******/ // expose the module cache
  5. /******/ __webpack_require__.c = installedModules; // 将已经加载的模块存起来
  6.  
  7. /******/ // __webpack_public_path__
  8. /******/ __webpack_require__.p = ""; // 设置的 publicPath

这里没什么可说的,这里的publicPath对应于 output中的配置,如

  1. output: {
  2. publicPath: './dist/',
  3. path: path.resolve(__dirname, 'dist'),
  4. filename: '[name].js'
  5. },

另外

  1. /******/ // define getter function for harmony exports
  2. /******/ __webpack_require__.d = function(exports, name, getter) {
  3. /******/ if(!__webpack_require__.o(exports, name)) {
  4. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  5. /******/ }
  6. /******/ };
  7. /******/
  8. /******/ // define __esModule on exports
  9. /******/ __webpack_require__.r = function(exports) {
  10. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  11. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  12. /******/ }
  13. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  14. /******/ };
  15. /******/ // Object.prototype.hasOwnProperty.call
  16. /******/ __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的支持(当前模块未用到,暂且如此,理解不到位)

还有这个,这个就更难理解了

  1. /******/ // create a fake namespace object
  2. /******/ // mode & 1: value is a module id, require it
  3. /******/ // mode & 2: merge all properties of value into the ns
  4. /******/ // mode & 4: return value when already ns object
  5. /******/ // mode & 8|1: behave like require
  6. /******/ __webpack_require__.t = function(value, mode) {
  7. /******/ if(mode & 1) value = __webpack_require__(value);
  8. /******/ if(mode & 8) return value;
  9. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  10. /******/ var ns = Object.create(null);
  11. /******/ __webpack_require__.r(ns);
  12. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  13. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  14. /******/ return ns;
  15. /******/ };
  16. /******/
  17. /******/ // getDefaultExport function for compatibility with non-harmony modules
  18. /******/ __webpack_require__.n = function(module) {
  19. /******/ var getter = module && module.__esModule ?
  20. /******/ function getDefault() { return module['default']; } :
  21. /******/ function getModuleExports() { return module; };
  22. /******/ __webpack_require__.d(getter, 'a', getter);
  23. /******/ return getter;
  24. /******/ };

__webpack_require__.t 暂时不说明了,还看不懂怎么调用的..

__webpack_require__.n 这个主要也是为 es6模块服务的,也没能理解好,知道的可以在评论区留言哈~

 

2. 有依赖的单个模块

先使用最基础的commonjs模块规范  require, exports ,module.exports 有助于理解上面那个模块的导出项目

./main.js

  1. let number = require('./number');
  2. console.log('main', number);

 

./number.js

  1. let n = 10;
  2. exports.n = n;

编译后,生成的文件变化的只是匿名函数传入的部分

./dist/main.js

  1. // 省略
  2.  
  3. /******/ ([
  4. /* 0 */
  5. /***/ (function(module, exports, __webpack_require__) {
  6. let number = __webpack_require__(1);
  7. console.log('main', number);
  8. /***/ }),
  9. /* 1 */
  10. /***/ (function(module, exports) {
  11. let n = 10;
  12. exports.n = n;
  13. /***/ })
  14. /******/ ]);

注意到前面的数字即是模块的ID,也可图中的一致

这里__webpack_require__参数被传进来,main.js中引入number这个模块 __webpack_require__(1);

number模块中 exports.n = n,注意这里的 exports即是调用时的第二个参数

  1. modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

所以此时 n属性被存入module的export导出项中,从而__webpack_require__(1) 就能获取这个导出项

 

换种方式,使用es6的模块导出

更改 ./number.js

  1. let n = 10;
  2. export {
  3. n
  4. };

编译后 ./dist/main.js

  1. /******/ ([
  2. /* 0 */
  3. /***/ (function(module, exports, __webpack_require__) {
  4. let number = __webpack_require__(1);
  5. console.log('main', number);
  6. /***/ }),
  7. /* 1 */
  8. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  9. "use strict";
  10. __webpack_require__.r(__webpack_exports__);
  11. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return n; });
  12. let n = 10;
  13. /***/ })
  14. /******/ ]);

可以看到模块1变了,为了兼容 export ,使用 __webpack_require__.r 定义了它为es6模块,再使用__webpack_require__.d 将 n保存到模块的导出项中

__webpack_require__.d 函数中的 getter即为 这里的 function() { return n; },通过设置为对象的get属性,可以获取到 n这个返回值

  1. var o = {};
  2. Object.defineProperty(o, 'abc', {
  3. get: function() {
  4. return 123;
  5. }
  6. });
  7. console.log(o.abc); // 123

所以将 let n = 10 定义在后面也是没问题的,因为getter是在number模块被调用返回之后才使用的

 

接着,我们把引入依赖文件改为import

./main.js

  1. import {n} from './number';
  2. console.log('main', n);

编译后 ./dist/main.js

  1. /******/ ([
  2. /* 0 */
  3. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4. "use strict";
  5. __webpack_require__.r(__webpack_exports__);
  6. /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
  7. console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["n"]);
  8. /***/ }),
  9. /* 1 */
  10. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  11. "use strict";
  12. __webpack_require__.r(__webpack_exports__);
  13. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return n; });
  14. let n = 10;
  15. /***/ })
  16. /******/ ]);

同样的,这时main模块用到了es6的模块引入方式,所以 __webpack_require__.r(__webpack_exports__);

  1. var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);

这个 __webpack_require__(1) 实际上就是 number模块的模块导出项,自然就能取到属性 n 了

 

接下来,着眼那个 default字眼,继续更换模块的导入导出方式

./main.js

  1. import n from './number';
  2. console.log('main', n);

./number.js

  1. let n = 10;
  2. export default n;

./dist/main.js

  1. /******/ ([
  2. /* 0 */
  3. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4. "use strict";
  5. __webpack_require__.r(__webpack_exports__);
  6. /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
  7. console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["default"]);
  8. /***/ }),
  9. /* 1 */
  10. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  11. "use strict";
  12. __webpack_require__.r(__webpack_exports__);
  13. let n = 10;
  14. /* harmony default export */ __webpack_exports__["default"] = (n);
  15. /***/ })
  16. /******/ ]);

可以看到,变化只是属性变成了default

 

再来一种 es6的方式

./main.js

  1. import n from './number';
  2. console.log('main', n);

 

./number.js

  1. import {str as n} from './str';
  2. export default n;

 

./str.js

  1. export var str = 10;

 

编译后

./dist/main.js

  1. /******/ ([
  2. /* 0 */
  3. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4. "use strict";
  5. __webpack_require__.r(__webpack_exports__);
  6. /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
  7. console.log('main', _number__WEBPACK_IMPORTED_MODULE_0__["default"]);
  8. /***/ }),
  9. /* 1 */
  10. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  11. "use strict";
  12. __webpack_require__.r(__webpack_exports__);
  13. /* harmony import */ var _str__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
  14. /* harmony default export */ __webpack_exports__["default"] = (_str__WEBPACK_IMPORTED_MODULE_0__["str"]);
  15. /***/ }),
  16. /* 2 */
  17. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  18. "use strict";
  19. __webpack_require__.r(__webpack_exports__);
  20. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
  21. var str = 10;
  22. /***/ })
  23. /******/ ]);

可以看到 {str as n} 也是没什么影响的,通过上面的例子应该基本能理解模块的依赖了

 

3. 多个入口模块

如果不提取多模块之间的公共部分,多个入口模块和单个的不同之处就是多了一个文件而已,它们是独立的。

所以这里就不多说了

 

4. 异步加载模块

webpack支持使用require.ensure来异步加载模块

./main.js

  1. console.log('main');
  2. setTimeout(() => {
  3. require([], (require) => {
  4. let number = require('./number');
  5. console.log(number.n);
  6. });
  7. }, 1000);

 

./number.js

  1. let n = 10;
  2. export {
  3. n
  4. };

webpack.config.js中要加上 publicPath,防止异步模块加载路径出错

  1. output: {
  2. publicPath: './dist/',
  3. path: path.resolve(__dirname, 'dist'),
  4. filename: '[name].js'
  5. }

编译后,生成的 1.js即为异步的模块number

./dist/1.js

  1. (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
  2. /* 0 */,
  3. /* 1 */
  4. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5. "use strict";
  6. __webpack_require__.r(__webpack_exports__);
  7. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return n; });
  8. let n = 10;
  9. /***/ })
  10. ]]);

可以看到,这里首先获取 (window["webpackJsonp"] = window["webpackJsonp"] || []), 再调用 push 传入模块及其依赖

jsonp类似我们跨域中的动态插入脚本,这里也是一样,动态插入一个script标签,把src设置好就加载这个异步模块了

push参数中第一个为当前异步模块

 

看看 ./dist/main.js

  1. 1 /******/ (function(modules) { // webpackBootstrap
  2. 2 /******/ // install a JSONP callback for chunk loading
  3. 3 /******/ function webpackJsonpCallback(data) {
  4. 4 /******/ var chunkIds = data[0];
  5. 5 /******/ var moreModules = data[1];
  6. 6 /******/
  7. 7 /******/
  8. 8 /******/ // add "moreModules" to the modules object,
  9. 9 /******/ // then flag all "chunkIds" as loaded and fire callback
  10. 10 /******/ var moduleId, chunkId, i = 0, resolves = [];
  11. 11 /******/ for(;i < chunkIds.length; i++) {
  12. 12 /******/ chunkId = chunkIds[i];
  13. 13 /******/ if(installedChunks[chunkId]) {
  14. 14 /******/ resolves.push(installedChunks[chunkId][0]);
  15. 15 /******/ }
  16. 16 /******/ installedChunks[chunkId] = 0;
  17. 17 /******/ }
  18. 18 /******/ for(moduleId in moreModules) {
  19. 19 /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  20. 20 /******/ modules[moduleId] = moreModules[moduleId];
  21. 21 /******/ }
  22. 22 /******/ }
  23. 23 /******/ if(parentJsonpFunction) parentJsonpFunction(data);
  24. 24 /******/
  25. 25 /******/ while(resolves.length) {
  26. 26 /******/ resolves.shift()();
  27. 27 /******/ }
  28. 28 /******/
  29. 29 /******/ };
  30. 30 /******/
  31. 31 /******/
  32. 32 /******/ // The module cache
  33. 33 /******/ var installedModules = {};
  34. 34 /******/
  35. 35 /******/ // object to store loaded and loading chunks
  36. 36 /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
  37. 37 /******/ // Promise = chunk loading, 0 = chunk loaded
  38. 38 /******/ var installedChunks = {
  39. 39 /******/ 0: 0
  40. 40 /******/ };
  41. 41 /******/
  42. 42 /******/
  43. 43 /******/
  44. 44 /******/ // script path function
  45. 45 /******/ function jsonpScriptSrc(chunkId) {
  46. 46 /******/ return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".js"
  47. 47 /******/ }
  48. 48 /******/
  49. 49 /******/ // The require function
  50. 50 /******/ function __webpack_require__(moduleId) {
  51. 51 /******/
  52. 52 /******/ // Check if module is in cache
  53. 53 /******/ if(installedModules[moduleId]) {
  54. 54 /******/ return installedModules[moduleId].exports;
  55. 55 /******/ }
  56. 56 /******/ // Create a new module (and put it into the cache)
  57. 57 /******/ var module = installedModules[moduleId] = {
  58. 58 /******/ i: moduleId,
  59. 59 /******/ l: false,
  60. 60 /******/ exports: {}
  61. 61 /******/ };
  62. 62 /******/
  63. 63 /******/ // Execute the module function
  64. 64 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  65. 65 /******/
  66. 66 /******/ // Flag the module as loaded
  67. 67 /******/ module.l = true;
  68. 68 /******/
  69. 69 /******/ // Return the exports of the module
  70. 70 /******/ return module.exports;
  71. 71 /******/ }
  72. 72 /******/
  73. 73 /******/ // This file contains only the entry chunk.
  74. 74 /******/ // The chunk loading function for additional chunks
  75. 75 /******/ __webpack_require__.e = function requireEnsure(chunkId) {
  76. 76 /******/ var promises = [];
  77. 77 /******/
  78. 78 /******/
  79. 79 /******/ // JSONP chunk loading for javascript
  80. 80 /******/
  81. 81 /******/ var installedChunkData = installedChunks[chunkId];
  82. 82 /******/ if(installedChunkData !== 0) { // 0 means "already installed".
  83. 83 /******/
  84. 84 /******/ // a Promise means "currently loading".
  85. 85 /******/ if(installedChunkData) {
  86. 86 /******/ promises.push(installedChunkData[2]);
  87. 87 /******/ } else {
  88. 88 /******/ // setup Promise in chunk cache
  89. 89 /******/ var promise = new Promise(function(resolve, reject) {
  90. 90 /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
  91. 91 /******/ });
  92. 92 /******/ promises.push(installedChunkData[2] = promise);
  93. 93 /******/
  94. 94 /******/ // start chunk loading
  95. 95 /******/ var head = document.getElementsByTagName('head')[0];
  96. 96 /******/ var script = document.createElement('script');
  97. 97 /******/ var onScriptComplete;
  98. 98 /******/
  99. 99 /******/ script.charset = 'utf-8';
  100. 100 /******/ script.timeout = 120;
  101. 101 /******/ if (__webpack_require__.nc) {
  102. 102 /******/ script.setAttribute("nonce", __webpack_require__.nc);
  103. 103 /******/ }
  104. 104 /******/ script.src = jsonpScriptSrc(chunkId);
  105. 105 /******/
  106. 106 /******/ onScriptComplete = function (event) {
  107. 107 /******/ // avoid mem leaks in IE.
  108. 108 /******/ script.onerror = script.onload = null;
  109. 109 /******/ clearTimeout(timeout);
  110. 110 /******/ var chunk = installedChunks[chunkId];
  111. 111 /******/ if(chunk !== 0) {
  112. 112 /******/ if(chunk) {
  113. 113 /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
  114. 114 /******/ var realSrc = event && event.target && event.target.src;
  115. 115 /******/ var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')');
  116. 116 /******/ error.type = errorType;
  117. 117 /******/ error.request = realSrc;
  118. 118 /******/ chunk[1](error);
  119. 119 /******/ }
  120. 120 /******/ installedChunks[chunkId] = undefined;
  121. 121 /******/ }
  122. 122 /******/ };
  123. 123 /******/ var timeout = setTimeout(function(){
  124. 124 /******/ onScriptComplete({ type: 'timeout', target: script });
  125. 125 /******/ }, 120000);
  126. 126 /******/ script.onerror = script.onload = onScriptComplete;
  127. 127 /******/ head.appendChild(script);
  128. 128 /******/ }
  129. 129 /******/ }
  130. 130 /******/ return Promise.all(promises);
  131. 131 /******/ };
  132. 132 /******/
  133. 133 /******/ // expose the modules object (__webpack_modules__)
  134. 134 /******/ __webpack_require__.m = modules;
  135. 135 /******/
  136. 136 /******/ // expose the module cache
  137. 137 /******/ __webpack_require__.c = installedModules;
  138. 138 /******/
  139. 139 /******/ // define getter function for harmony exports
  140. 140 /******/ __webpack_require__.d = function(exports, name, getter) {
  141. 141 /******/ if(!__webpack_require__.o(exports, name)) {
  142. 142 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  143. 143 /******/ }
  144. 144 /******/ };
  145. 145 /******/
  146. 146 /******/ // define __esModule on exports
  147. 147 /******/ __webpack_require__.r = function(exports) {
  148. 148 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  149. 149 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  150. 150 /******/ }
  151. 151 /******/ Object.defineProperty(exports, '__esModule', { value: true });
  152. 152 /******/ };
  153. 153 /******/
  154. 154 /******/ // create a fake namespace object
  155. 155 /******/ // mode & 1: value is a module id, require it
  156. 156 /******/ // mode & 2: merge all properties of value into the ns
  157. 157 /******/ // mode & 4: return value when already ns object
  158. 158 /******/ // mode & 8|1: behave like require
  159. 159 /******/ __webpack_require__.t = function(value, mode) {
  160. 160 /******/ if(mode & 1) value = __webpack_require__(value);
  161. 161 /******/ if(mode & 8) return value;
  162. 162 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  163. 163 /******/ var ns = Object.create(null);
  164. 164 /******/ __webpack_require__.r(ns);
  165. 165 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  166. 166 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  167. 167 /******/ return ns;
  168. 168 /******/ };
  169. 169 /******/
  170. 170 /******/ // getDefaultExport function for compatibility with non-harmony modules
  171. 171 /******/ __webpack_require__.n = function(module) {
  172. 172 /******/ var getter = module && module.__esModule ?
  173. 173 /******/ function getDefault() { return module['default']; } :
  174. 174 /******/ function getModuleExports() { return module; };
  175. 175 /******/ __webpack_require__.d(getter, 'a', getter);
  176. 176 /******/ return getter;
  177. 177 /******/ };
  178. 178 /******/
  179. 179 /******/ // Object.prototype.hasOwnProperty.call
  180. 180 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  181. 181 /******/
  182. 182 /******/ // __webpack_public_path__
  183. 183 /******/ __webpack_require__.p = "./dist/";
  184. 184 /******/
  185. 185 /******/ // on error function for async loading
  186. 186 /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
  187. 187 /******/
  188. 188 /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
  189. 189 /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  190. 190 /******/ jsonpArray.push = webpackJsonpCallback;
  191. 191 /******/ jsonpArray = jsonpArray.slice();
  192. 192 /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  193. 193 /******/ var parentJsonpFunction = oldJsonpFunction;
  194. 194 /******/
  195. 195 /******/
  196. 196 /******/ // Load entry module and return exports
  197. 197 /******/ return __webpack_require__(__webpack_require__.s = 0);
  198. 198 /******/ })
  199. 199 /************************************************************************/
  200. 200 /******/ ([
  201. 201 /* 0 */
  202. 202 /***/ (function(module, exports, __webpack_require__) {
  203. 203
  204. 204
  205. 205
  206. 206 console.log('main');
  207. 207
  208. 208 setTimeout(() => {
  209. 209 __webpack_require__.e(/* AMD require */ 1).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = []; ((require) => {
  210. 210 let number = __webpack_require__(1);
  211. 211
  212. 212 console.log(number.n);
  213. 213 }).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}).catch(__webpack_require__.oe);
  214. 214 }, 1000);
  215. 215
  216. 216
  217. 217 /***/ })
  218. 218 /******/ ]);

这下蹦出了许多代码,从这里开始会比较绕,需要有耐心!

按照代码执行顺序来分析,思路就清晰了

38行中定义了installedChunks这个新变量,它指代依赖模块(不仅包括此处的异步模块,也包括后续会说到的公共模块,runtime模块等),而上面installedModules指的是所有的模块

  1. /******/ // object to store loaded and loading chunks
  2. /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
  3. /******/ // Promise = chunk loading, 0 = chunk loaded
  4. /******/ var installedChunks = {
  5. /******/ 0: 0
  6. /******/ };

前面的0表示模块ID,在这里指的就是 ./main.js这个入口模块了,它初始的状态就被webpack设置成已加载

 

  1. /******/ // script path function
  2. /******/ function jsonpScriptSrc(chunkId) {
  3. /******/ return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".js"
  4. /******/ }

这里就是异步模块的路径了,({}[chunkId]||chunkId) 这个只是为了防止出错做的处理

__webpack_require__ 函数的内容没变

75行多了一个 __webpack_require__.e 用来加载异步模块,这个稍后再讲

继续到182行开始

  1. /******/ // __webpack_public_path__
  2. /******/ __webpack_require__.p = "./dist/";
  3. /******/
  4. /******/ // on error function for async loading
  5. /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
  6. /******/
  7. /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
  8. /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  9. /******/ jsonpArray.push = webpackJsonpCallback;
  10. /******/ jsonpArray = jsonpArray.slice();
  11. /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  12. /******/ var parentJsonpFunction = oldJsonpFunction;
  13. /******/
  14. /******/
  15. /******/ // Load entry module and return exports
  16. /******/ return __webpack_require__(__webpack_require__.s = 0);

这里的publicPath就是我们刚刚设置的

__webpack_require__.oe 只是用于处理错误

初始会判断是否有window["webpackJsonp"]存在,有的话就缓存起来,并将this的指向设置好 jsonpArray.push.bind(jsonpArray)

要理清楚 jsonpArray.push ,它不是简单的数组,所以有些绕,它指向了第3行webpackJsonpCallback这个函数

如果初始已经有待加载的依赖模块,则在for循环中直接加载。此处初始阶段是没有值的,所以可以直接略过

要看明白webpackJsonpCallback这个函数,得从调用它的地方开始,在216行中开始调用

  1. setTimeout(() => {
  2. __webpack_require__.e(/* AMD require */ 1).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = []; ((require) => {
  3. let number = __webpack_require__(1);
  4. console.log(number.n);
  5. }).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}).catch(__webpack_require__.oe);
  6. }, 1000);

 

  1. /******/ // This file contains only the entry chunk.
  2. /******/ // The chunk loading function for additional chunks
  3. /******/ __webpack_require__.e = function requireEnsure(chunkId) {
  4. /******/ var promises = []; // promise队列,支持模块加载完成后多个异步回调
  5. /******/
  6. /******/
  7. /******/ // JSONP chunk loading for javascript
  8. /******/
  9. /******/ var installedChunkData = installedChunks[chunkId];
  10. // 未加载
  11. /******/ if(installedChunkData !== 0) { // 0 means "already installed".
  12. /******/
  13. /******/ // a Promise means "currently loading".
  14. // 加载中,则支持下一个回调加入
  15. /******/ if(installedChunkData) {
  16. /******/ promises.push(installedChunkData[2]);
  17. /******/ } else {
  18. // 初始化一个promise来加载
  19. /******/ // setup Promise in chunk cache
  20. /******/ var promise = new Promise(function(resolve, reject) {
  21. // 将resolve和reject存入模块中,方便其他地方调用
  22. /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
  23. /******/ });
  24. // installedChunkData的第三项即为一个promise对象,并存入promises队列中
  25. /******/ promises.push(installedChunkData[2] = promise);
  26. /******/
  27. /******/ // start chunk loading
  28. /******/ var head = document.getElementsByTagName('head')[0];
  29. /******/ var script = document.createElement('script');
  30. /******/ var onScriptComplete;
  31. /******/
  32. /******/ script.charset = 'utf-8';
  33. /******/ script.timeout = 120;
  34. /******/ if (__webpack_require__.nc) {
  35. /******/ script.setAttribute("nonce", __webpack_require__.nc);
  36. /******/ }
  37. // 设置异步模块的路径
  38. /******/ script.src = jsonpScriptSrc(chunkId);
  39. /******/
  40. /******/ onScriptComplete = function (event) {
  41. /******/ // avoid mem leaks in IE.
  42. /******/ script.onerror = script.onload = null;
  43. /******/ clearTimeout(timeout);
  44. /******/ var chunk = installedChunks[chunkId];
  45. /******/ if(chunk !== 0) {
  46. /******/ if(chunk) {
  47. /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
  48. /******/ var realSrc = event && event.target && event.target.src;
  49. /******/ var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')');
  50. /******/ error.type = errorType;
  51. /******/ error.request = realSrc;
  52. // 调用reject
  53. /******/ chunk[1](error);
  54. /******/ }
  55. /******/ installedChunks[chunkId] = undefined;
  56. /******/ }
  57. /******/ };
  58. /******/ var timeout = setTimeout(function(){
  59. /******/ onScriptComplete({ type: 'timeout', target: script });
  60. /******/ }, 120000);
  61. /******/ script.onerror = script.onload = onScriptComplete;
  62. // 在head标签中插入脚本
  63. /******/ head.appendChild(script);
  64. /******/ }
  65. /******/ }
  66. /******/ return Promise.all(promises);
  67. /******/ };

 

一秒钟后加载这个异步模块 ./1.js ,该模块加载完成后就开始执行

  1. (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
  2. /* 0 */,
  3. /* 1 */
  4. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5. "use strict";
  6. __webpack_require__.r(__webpack_exports__);
  7. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return n; });
  8. let n = 10;
  9. /***/ })
  10. ]]);

此时的 window["webpackJsonp"] 已经被这句代码影响,jsonpArray.push = webpackJsonpCallback; 所以push实际上调用的是 webpackJsonpCallback函数

  1. /******/ // install a JSONP callback for chunk loading
  2. /******/ function webpackJsonpCallback(data) {
  3. /******/ var chunkIds = data[0]; // 依赖的模块ID,此时是[1]
  4. /******/ var moreModules = data[1]; // 依赖的模块内容
  5. /******/
  6. /******/
  7. /******/ // add "moreModules" to the modules object,
  8. /******/ // then flag all "chunkIds" as loaded and fire callback
  9. /******/ var moduleId, chunkId, i = 0, resolves = [];
  10. // 遍历依赖的模块进行加载
  11. /******/ for(;i < chunkIds.length; i++) {
  12. /******/ chunkId = chunkIds[i];
  13. /******/ if(installedChunks[chunkId]) {
  14. /******/ resolves.push(installedChunks[chunkId][0]); // 存储将要执行的resolve
  15. /******/ }
  16. /******/ installedChunks[chunkId] = 0; // 标记已加载
  17. /******/ }
  18. /******/ for(moduleId in moreModules) {
  19. /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  20. /******/ modules[moduleId] = moreModules[moduleId]; // 更新模块组
  21. /******/ }
  22. /******/ }
  23. /******/ if(parentJsonpFunction) parentJsonpFunction(data);
  24. /******/
  25. /******/ while(resolves.length) {
  26. /******/ resolves.shift()(); // 执行所有resolve
  27. /******/ }
  28. /******/
  29. /******/ };

如果多依赖一个呢

./main.js

  1. console.log('main');
  2. setTimeout(() => {
  3. require(['./str'], (require) => {
  4. let number = require('./number');
  5. console.log(number.n);
  6. });
  7. }, 1000);

这时只有 ./1.js改变了,差不不大,一样的道理

  1. (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
  2. /* 0 */,
  3. /* 1 */
  4. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  5. "use strict";
  6. __webpack_require__.r(__webpack_exports__);
  7. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str", function() { return str; });
  8. var str = 10;
  9. /***/ }),
  10. /* 2 */
  11. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  12. "use strict";
  13. __webpack_require__.r(__webpack_exports__);
  14. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return n; });
  15. let n = 10;
  16. /***/ })
  17. ]]);

 

5. 提取公共模块

./webpack.config.js

  1. entry: {
  2. main: './main',
  3. test: './test'
  4. },
  5. optimization: {
  6. // 提取公共部分为common.js,使劲地提取吧..
  7. splitChunks: {
  8. name: 'common',
  9. chunks: 'all',
  10. minSize: 1
  11. }
  12. },

./main.js

  1. import './chunk';
  2. import {n} from './number';
  3. console.log('main', n);

 

./test.js

  1. import './chunk';
  2. console.log('test');

编译后

./dist/common.js

  1. (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
  2. /* 0 */,
  3. /* 1 */
  4. /***/ (function(module, exports) {
  5. console.log('chunk');
  6. /***/ })
  7. ]]);

可以看到 chunk模块(ID为1)被共用,被提取出来

再看看 ./dist/test.js

  1. 1 /******/ (function(modules) { // webpackBootstrap
  2. 2 /******/ // install a JSONP callback for chunk loading
  3. 3 /******/ function webpackJsonpCallback(data) {
  4. 4 /******/ var chunkIds = data[0];
  5. 5 /******/ var moreModules = data[1];
  6. 6 /******/ var executeModules = data[2];
  7. 7 /******/
  8. 8 /******/ // add "moreModules" to the modules object,
  9. 9 /******/ // then flag all "chunkIds" as loaded and fire callback
  10. 10 /******/ var moduleId, chunkId, i = 0, resolves = [];
  11. 11 /******/ for(;i < chunkIds.length; i++) {
  12. 12 /******/ chunkId = chunkIds[i];
  13. 13 /******/ if(installedChunks[chunkId]) {
  14. 14 /******/ resolves.push(installedChunks[chunkId][0]);
  15. 15 /******/ }
  16. 16 /******/ installedChunks[chunkId] = 0;
  17. 17 /******/ }
  18. 18 /******/ for(moduleId in moreModules) {
  19. 19 /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  20. 20 /******/ modules[moduleId] = moreModules[moduleId];
  21. 21 /******/ }
  22. 22 /******/ }
  23. 23 /******/ if(parentJsonpFunction) parentJsonpFunction(data);
  24. 24 /******/
  25. 25 /******/ while(resolves.length) {
  26. 26 /******/ resolves.shift()();
  27. 27 /******/ }
  28. 28 /******/
  29. 29 /******/ // add entry modules from loaded chunk to deferred list
  30. 30 /******/ deferredModules.push.apply(deferredModules, executeModules || []);
  31. 31 /******/
  32. 32 /******/ // run deferred modules when all chunks ready
  33. 33 /******/ return checkDeferredModules();
  34. 34 /******/ };
  35. 35 /******/ function checkDeferredModules() {
  36. 36 /******/ var result;
  37. 37 /******/ for(var i = 0; i < deferredModules.length; i++) {
  38. 38 /******/ var deferredModule = deferredModules[i];
  39. 39 /******/ var fulfilled = true;
  40. 40 /******/ for(var j = 1; j < deferredModule.length; j++) {
  41. 41 /******/ var depId = deferredModule[j];
  42. 42 /******/ if(installedChunks[depId] !== 0) fulfilled = false;
  43. 43 /******/ }
  44. 44 /******/ if(fulfilled) {
  45. 45 /******/ deferredModules.splice(i--, 1);
  46. 46 /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
  47. 47 /******/ }
  48. 48 /******/ }
  49. 49 /******/ return result;
  50. 50 /******/ }
  51. 51 /******/
  52. 52 /******/ // The module cache
  53. 53 /******/ var installedModules = {};
  54. 54 /******/
  55. 55 /******/ // object to store loaded and loading chunks
  56. 56 /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
  57. 57 /******/ // Promise = chunk loading, 0 = chunk loaded
  58. 58 /******/ var installedChunks = {
  59. 59 /******/ 2: 0
  60. 60 /******/ };
  61. 61 /******/
  62. 62 /******/ var deferredModules = [];
  63. 63 /******/
  64. 64 /******/ // The require function
  65. 65 /******/ function __webpack_require__(moduleId) {
  66. 66 /******/
  67. 67 /******/ // Check if module is in cache
  68. 68 /******/ if(installedModules[moduleId]) {
  69. 69 /******/ return installedModules[moduleId].exports;
  70. 70 /******/ }
  71. 71 /******/ // Create a new module (and put it into the cache)
  72. 72 /******/ var module = installedModules[moduleId] = {
  73. 73 /******/ i: moduleId,
  74. 74 /******/ l: false,
  75. 75 /******/ exports: {}
  76. 76 /******/ };
  77. 77 /******/
  78. 78 /******/ // Execute the module function
  79. 79 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  80. 80 /******/
  81. 81 /******/ // Flag the module as loaded
  82. 82 /******/ module.l = true;
  83. 83 /******/
  84. 84 /******/ // Return the exports of the module
  85. 85 /******/ return module.exports;
  86. 86 /******/ }
  87. 87 /******/
  88. 88 /******/
  89. 89 /******/ // expose the modules object (__webpack_modules__)
  90. 90 /******/ __webpack_require__.m = modules;
  91. 91 /******/
  92. 92 /******/ // expose the module cache
  93. 93 /******/ __webpack_require__.c = installedModules;
  94. 94 /******/
  95. 95 /******/ // define getter function for harmony exports
  96. 96 /******/ __webpack_require__.d = function(exports, name, getter) {
  97. 97 /******/ if(!__webpack_require__.o(exports, name)) {
  98. 98 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  99. 99 /******/ }
  100. 100 /******/ };
  101. 101 /******/
  102. 102 /******/ // define __esModule on exports
  103. 103 /******/ __webpack_require__.r = function(exports) {
  104. 104 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  105. 105 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  106. 106 /******/ }
  107. 107 /******/ Object.defineProperty(exports, '__esModule', { value: true });
  108. 108 /******/ };
  109. 109 /******/
  110. 110 /******/ // create a fake namespace object
  111. 111 /******/ // mode & 1: value is a module id, require it
  112. 112 /******/ // mode & 2: merge all properties of value into the ns
  113. 113 /******/ // mode & 4: return value when already ns object
  114. 114 /******/ // mode & 8|1: behave like require
  115. 115 /******/ __webpack_require__.t = function(value, mode) {
  116. 116 /******/ if(mode & 1) value = __webpack_require__(value);
  117. 117 /******/ if(mode & 8) return value;
  118. 118 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  119. 119 /******/ var ns = Object.create(null);
  120. 120 /******/ __webpack_require__.r(ns);
  121. 121 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  122. 122 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  123. 123 /******/ return ns;
  124. 124 /******/ };
  125. 125 /******/
  126. 126 /******/ // getDefaultExport function for compatibility with non-harmony modules
  127. 127 /******/ __webpack_require__.n = function(module) {
  128. 128 /******/ var getter = module && module.__esModule ?
  129. 129 /******/ function getDefault() { return module['default']; } :
  130. 130 /******/ function getModuleExports() { return module; };
  131. 131 /******/ __webpack_require__.d(getter, 'a', getter);
  132. 132 /******/ return getter;
  133. 133 /******/ };
  134. 134 /******/
  135. 135 /******/ // Object.prototype.hasOwnProperty.call
  136. 136 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  137. 137 /******/
  138. 138 /******/ // __webpack_public_path__
  139. 139 /******/ __webpack_require__.p = "./dist/";
  140. 140 /******/
  141. 141 /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
  142. 142 /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  143. 143 /******/ jsonpArray.push = webpackJsonpCallback;
  144. 144 /******/ jsonpArray = jsonpArray.slice();
  145. 145 /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  146. 146 /******/ var parentJsonpFunction = oldJsonpFunction;
  147. 147 /******/
  148. 148 /******/
  149. 149 /******/ // add entry module to deferred list
  150. 150 /******/ deferredModules.push([3,1]);
  151. 151 /******/ // run deferred modules when ready
  152. 152 /******/ return checkDeferredModules();
  153. 153 /******/ })
  154. 154 /************************************************************************/
  155. 155 /******/ ({
  156. 156
  157. 157 /***/ 3:
  158. 158 /***/ (function(module, __webpack_exports__, __webpack_require__) {
  159. 159
  160. 160 "use strict";
  161. 161 __webpack_require__.r(__webpack_exports__);
  162. 162 /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
  163. 163 /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_chunk__WEBPACK_IMPORTED_MODULE_0__);
  164. 164
  165. 165
  166. 166
  167. 167 console.log('test');
  168. 168
  169. 169
  170. 170
  171. 171 /***/ })
  172. 172
  173. 173 /******/ });

先看150行,初始不再马上加载入口模块,而是先将入口模块和其依赖的公共模块保存起来,再进行处理加载

  1. /******/ // add entry module to deferred list
  2. /******/ deferredModules.push([3,1]); // 这里的3为test模块,1为chunk公共模块
  3. /******/ // run deferred modules when ready
  4. /******/ return checkDeferredModules();
  1. /******/ function checkDeferredModules() {
  2. /******/ var result;
  3. // deferredModules的结构长这样 [[3,1]],对每一项进行处理
  4. /******/ for(var i = 0; i < deferredModules.length; i++) {
  5. /******/ var deferredModule = deferredModules[i];
  6. /******/ var fulfilled = true;
  7. // 从第二项开始,为依赖的模块
  8. /******/ for(var j = 1; j < deferredModule.length; j++) {
  9. /******/ var depId = deferredModule[j];
  10. // 依赖的模块未加载
  11. /******/ if(installedChunks[depId] !== 0) fulfilled = false;
  12. /******/ }
  13. // 已经加载,则清除,并开始加载入口模块,deferredModule的第一项即为这里的test入口模块
  14. /******/ if(fulfilled) {
  15. /******/ deferredModules.splice(i--, 1);
  16. /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
  17. /******/ }
  18. /******/ }
  19. /******/ return result;
  20. /******/ }

 

注意到这里也有 webpackJsonpCallback 函数,不过它的参数数组中有三项,第三项 var executeModules = data[2]; 暂时还没用到,先略过

  1. /******/ // add entry modules from loaded chunk to deferred list
  2. /******/ deferredModules.push.apply(deferredModules, executeModules || []);
  3. /******/
  4. /******/ // run deferred modules when all chunks ready
  5. /******/ return checkDeferredModules();

上面这个,主要是为了兼容公共模块和入口模块的兼容顺序,什么意思呢?

假如没有这段代码,那么这样是可行的

  1. <script type="text/javascript" src="./dist/common.js"></script>
  2. <script type="text/javascript" src="./dist/main.js"></script>

但common放后面就不行

  1. <script type="text/javascript" src="./dist/main.js"></script>
  2. <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

  1. optimization: {
  2. // 提取runtime代码到common.js文件中
  3. runtimeChunk: {
  4. name: 'common'
  5. },
  6. // 提取公共部分为common.js,使劲地提取吧..
  7. splitChunks: {
  8. name: 'common',
  9. chunks: 'all',
  10. minSize: 1
  11. }
  12. },

 

编译后,看看 ./dist/test.js 干净了许多

  1. (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[2],{
  2. /***/ 3:
  3. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  4. "use strict";
  5. __webpack_require__.r(__webpack_exports__);
  6. /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
  7. /* harmony import */ var _chunk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_chunk__WEBPACK_IMPORTED_MODULE_0__);
  8. console.log('test');
  9. /***/ })
  10. },[[3,1]]]);

不过,注意到这里push的参数多了第三项 [[3,1]],根据上面的分析,这个1应该就是公共模块了

来看看 ./dist/common.js

  1. 1 /******/ (function(modules) { // webpackBootstrap
  2. 2 /******/ // install a JSONP callback for chunk loading
  3. 3 /******/ function webpackJsonpCallback(data) {
  4. 4 /******/ var chunkIds = data[0];
  5. 5 /******/ var moreModules = data[1];
  6. 6 /******/ var executeModules = data[2];
  7. 7 /******/
  8. 8 /******/ // add "moreModules" to the modules object,
  9. 9 /******/ // then flag all "chunkIds" as loaded and fire callback
  10. 10 /******/ var moduleId, chunkId, i = 0, resolves = [];
  11. 11 /******/ for(;i < chunkIds.length; i++) {
  12. 12 /******/ chunkId = chunkIds[i];
  13. 13 /******/ if(installedChunks[chunkId]) {
  14. 14 /******/ resolves.push(installedChunks[chunkId][0]);
  15. 15 /******/ }
  16. 16 /******/ installedChunks[chunkId] = 0;
  17. 17 /******/ }
  18. 18 /******/ for(moduleId in moreModules) {
  19. 19 /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  20. 20 /******/ modules[moduleId] = moreModules[moduleId];
  21. 21 /******/ }
  22. 22 /******/ }
  23. 23 /******/ if(parentJsonpFunction) parentJsonpFunction(data);
  24. 24 /******/
  25. 25 /******/ while(resolves.length) {
  26. 26 /******/ resolves.shift()();
  27. 27 /******/ }
  28. 28 /******/
  29. 29 /******/ // add entry modules from loaded chunk to deferred list
  30. 30 /******/ deferredModules.push.apply(deferredModules, executeModules || []);
  31. 31 /******/
  32. 32 /******/ // run deferred modules when all chunks ready
  33. 33 /******/ return checkDeferredModules();
  34. 34 /******/ };
  35. 35 /******/ function checkDeferredModules() {
  36. 36 /******/ var result;
  37. 37 /******/ for(var i = 0; i < deferredModules.length; i++) {
  38. 38 /******/ var deferredModule = deferredModules[i];
  39. 39 /******/ var fulfilled = true;
  40. 40 /******/ for(var j = 1; j < deferredModule.length; j++) {
  41. 41 /******/ var depId = deferredModule[j];
  42. 42 /******/ if(installedChunks[depId] !== 0) fulfilled = false;
  43. 43 /******/ }
  44. 44 /******/ if(fulfilled) {
  45. 45 /******/ deferredModules.splice(i--, 1);
  46. 46 /******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
  47. 47 /******/ }
  48. 48 /******/ }
  49. 49 /******/ return result;
  50. 50 /******/ }
  51. 51 /******/
  52. 52 /******/ // The module cache
  53. 53 /******/ var installedModules = {};
  54. 54 /******/
  55. 55 /******/ // object to store loaded and loading chunks
  56. 56 /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
  57. 57 /******/ // Promise = chunk loading, 0 = chunk loaded
  58. 58 /******/ var installedChunks = {
  59. 59 /******/ 1: 0
  60. 60 /******/ };
  61. 61 /******/
  62. 62 /******/ var deferredModules = [];
  63. 63 /******/
  64. 64 /******/ // The require function
  65. 65 /******/ function __webpack_require__(moduleId) {
  66. 66 /******/
  67. 67 /******/ // Check if module is in cache
  68. 68 /******/ if(installedModules[moduleId]) {
  69. 69 /******/ return installedModules[moduleId].exports;
  70. 70 /******/ }
  71. 71 /******/ // Create a new module (and put it into the cache)
  72. 72 /******/ var module = installedModules[moduleId] = {
  73. 73 /******/ i: moduleId,
  74. 74 /******/ l: false,
  75. 75 /******/ exports: {}
  76. 76 /******/ };
  77. 77 /******/
  78. 78 /******/ // Execute the module function
  79. 79 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  80. 80 /******/
  81. 81 /******/ // Flag the module as loaded
  82. 82 /******/ module.l = true;
  83. 83 /******/
  84. 84 /******/ // Return the exports of the module
  85. 85 /******/ return module.exports;
  86. 86 /******/ }
  87. 87 /******/
  88. 88 /******/
  89. 89 /******/ // expose the modules object (__webpack_modules__)
  90. 90 /******/ __webpack_require__.m = modules;
  91. 91 /******/
  92. 92 /******/ // expose the module cache
  93. 93 /******/ __webpack_require__.c = installedModules;
  94. 94 /******/
  95. 95 /******/ // define getter function for harmony exports
  96. 96 /******/ __webpack_require__.d = function(exports, name, getter) {
  97. 97 /******/ if(!__webpack_require__.o(exports, name)) {
  98. 98 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  99. 99 /******/ }
  100. 100 /******/ };
  101. 101 /******/
  102. 102 /******/ // define __esModule on exports
  103. 103 /******/ __webpack_require__.r = function(exports) {
  104. 104 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  105. 105 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  106. 106 /******/ }
  107. 107 /******/ Object.defineProperty(exports, '__esModule', { value: true });
  108. 108 /******/ };
  109. 109 /******/
  110. 110 /******/ // create a fake namespace object
  111. 111 /******/ // mode & 1: value is a module id, require it
  112. 112 /******/ // mode & 2: merge all properties of value into the ns
  113. 113 /******/ // mode & 4: return value when already ns object
  114. 114 /******/ // mode & 8|1: behave like require
  115. 115 /******/ __webpack_require__.t = function(value, mode) {
  116. 116 /******/ if(mode & 1) value = __webpack_require__(value);
  117. 117 /******/ if(mode & 8) return value;
  118. 118 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  119. 119 /******/ var ns = Object.create(null);
  120. 120 /******/ __webpack_require__.r(ns);
  121. 121 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  122. 122 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  123. 123 /******/ return ns;
  124. 124 /******/ };
  125. 125 /******/
  126. 126 /******/ // getDefaultExport function for compatibility with non-harmony modules
  127. 127 /******/ __webpack_require__.n = function(module) {
  128. 128 /******/ var getter = module && module.__esModule ?
  129. 129 /******/ function getDefault() { return module['default']; } :
  130. 130 /******/ function getModuleExports() { return module; };
  131. 131 /******/ __webpack_require__.d(getter, 'a', getter);
  132. 132 /******/ return getter;
  133. 133 /******/ };
  134. 134 /******/
  135. 135 /******/ // Object.prototype.hasOwnProperty.call
  136. 136 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  137. 137 /******/
  138. 138 /******/ // __webpack_public_path__
  139. 139 /******/ __webpack_require__.p = "./dist/";
  140. 140 /******/
  141. 141 /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
  142. 142 /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  143. 143 /******/ jsonpArray.push = webpackJsonpCallback;
  144. 144 /******/ jsonpArray = jsonpArray.slice();
  145. 145 /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  146. 146 /******/ var parentJsonpFunction = oldJsonpFunction;
  147. 147 /******/
  148. 148 /******/
  149. 149 /******/ // run deferred modules from other chunks
  150. 150 /******/ checkDeferredModules();
  151. 151 /******/ })
  152. 152 /************************************************************************/
  153. 153 /******/ ([
  154. 154 /* 0 */,
  155. 155 /* 1 */
  156. 156 /***/ (function(module, exports) {
  157. 157
  158. 158 console.log('chunk');
  159. 159
  160. 160
  161. 161 /***/ })
  162. 162 /******/ ]);

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

(window["webpackJsonp"] = window["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;

编译后

在生成的文件中,并没有看到踪迹

当然了,也不能由此就得出结论插件不会影响到生成的文件,只是看起来如此

编译结果就分析到这里了,说实话,非常乱 .......

具体到底是由源码里面哪段代码控制的,就不得而知了,源码实在是庞大,目前定位到两个比较关键的文件,脑壳不疼的时候再看吧

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号