博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Webpack编译结果浅析
阅读量:5880 次
发布时间:2019-06-19

本文共 59414 字,大约阅读时间需要 198 分钟。

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

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

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

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

 

 

 

一、Webpack的运行机制

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

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

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

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

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

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

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

可以来看看这篇文章

 

二、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

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

可以看到首先是一个匿名函数,在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

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

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

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

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

 

看看 ./dist/main.js

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

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

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

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 = window["webpackJsonp"] = window["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 只是用于处理错误

初始会判断是否有window["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 ,该模块加载完成后就开始执行

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

此时的 window["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改变了,差不不大,一样的道理

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

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

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

再看看 ./dist/test.js

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

先看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();

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

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

但common放后面就不行

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 干净了许多

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

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

// 使用SyncHookconst {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;

编译后

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

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

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

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

 

9. Scope Hoisting 作用域提升

Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快,它也可以称作 “作用域提升”。

它是由ModuleConcatenationPlugin这个插件操控的,在webpack4中内置了这个插件,我们只需要用这个属性concatenateModules开启即可(mode为production时默认开启)

./webpack.config.js

optimization: {        concatenateModules: true,    }

./main.js

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

./number.js

let n = 10;export {    n};

./str.js

export function str() {    var ss = 'strstr';};export var str1 = 11;

编译后看看 ./dist/main.js 的后面部分

/******/ ([/* 0 *//***/ (function(module, __webpack_exports__, __webpack_require__) {"use strict";// CONCATENATED MODULE: ./number.jslet n = 10;// CONCATENATED MODULE: ./str.jsfunction str() {    var ss = 'strstr';};var str1 = 11;// CONCATENATED MODULE: ./main.jsconsole.log('main', str1);/***/ })/******/ ]);

非常精简,体积减小了,运行时因为创建的函数作用域也变少了,减小了内存开销

不过,它只能用作ES6的静态模块分析,分析出模块之间的依赖关系,尽可能地把模块放到同一个函数中。接下来使用require的方式修改以下 ./main.js

/******/ ([/* 0 *//***/ (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 *//***/ (function(module, __webpack_exports__, __webpack_require__) {"use strict";// CONCATENATED MODULE: ./str.jsfunction str() {    var ss = 'strstr';};var str1 = 11;// CONCATENATED MODULE: ./main.js__webpack_require__(0);console.log('main', str1);/***/ })/******/ ]);

可以看到仅ES6的import被提升合并了,所以,在开发时尽量使用ES6的模块方式,在使用了babel编译时也要记得设置配置为 module: false ,防止代码被转换成commonjs规范的东西,导致无法提升合并

再来看看不设置concatenateModules的时候的输出结果,对比就明显了

/******/ ([/* 0 *//***/ (function(module, __webpack_exports__, __webpack_require__) {"use strict";__webpack_require__.r(__webpack_exports__);/* harmony import */ var _str__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);// import {n} from './number';__webpack_require__(1);console.log('main', _str__WEBPACK_IMPORTED_MODULE_0__["str1"]);/***/ }),/* 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;/***/ }),/* 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; });/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "str1", function() { return str1; });function str() {    var ss = 'strstr';};var str1 = 11;/***/ })/******/ ]);

10. Tree Shaking 清除无用代码

 可以用来清除无用的代码,同样的,它在production模式下默认开启。

它作用于ES6的模块,同时需要代码优化压缩工具的参与

基于此,设置mode为production,编译一下

./main.js

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

./str.js

export function str() {    var ss = 'strstr';};export var str1 = 11;

 

编译后的压缩文件(部分)

r.p="./dist/",r(r.s=0)}([function(e,t,r){"use strict";r.r(t);console.log("main",11)}]);

可以看到就剩下str1这个有用到的变量,其他东西都被清除

最后,改一下webpack的配置,将压缩工具去掉

optimization: {        minimize: false,

再看看生成的文件,str函数没有被清除

/******/ ([/* 0 *//***/ (function(module, __webpack_exports__, __webpack_require__) {"use strict";__webpack_require__.r(__webpack_exports__);// CONCATENATED MODULE: ./str.jsfunction str() {    var ss = 'strstr';};var str1 = 11;// CONCATENATED MODULE: ./main.jsconsole.log('main', str1);/***/ })/******/ ]);

 

转载地址:http://pkcix.baihongyu.com/

你可能感兴趣的文章
飞秋无法显示局域网好友
查看>>
学员会诊之03:你那惨不忍睹的三层架构
查看>>
vue-04-组件
查看>>
Golang协程与通道整理
查看>>
解决win7远程桌面连接时发生身份验证错误的方法
查看>>
C/C++ 多线程机制
查看>>
js - object.assign 以及浅、深拷贝
查看>>
python mysql Connect Pool mysql连接池 (201
查看>>
Boost在vs2010下的配置
查看>>
一起谈.NET技术,ASP.NET伪静态的实现及伪静态的意义
查看>>
20款绝佳的HTML5应用程序示例
查看>>
string::c_str()、string::c_data()及string与char *的正确转换
查看>>
11G数据的hive初测试
查看>>
如何使用Core Text计算一段文本绘制在屏幕上之后的高度
查看>>
==和equals区别
查看>>
2010技术应用计划
查看>>
XML 节点类型
查看>>
驯服 Tiger: 并发集合 超越 Map、Collection、List 和 Set
查看>>
Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现...
查看>>
Template Method Design Pattern in Java
查看>>