webpack3之Scope Hoisting
介紹
在webpack2發布半年不到,webpack3又發布了,這次的發布版本雖然跨度比較大,但是不同於webpack2的升級,它對於向下的兼容性還是要好很多的,但是由於內部的突破性變化導致某些插件的使用,根據官方的數據表明,目前為止,至少98%的用戶都沒有遇到升級過程中不兼容的問題。
在這次升級中,有以下特性:
1. 作用域的提升(Scope Hoisting)
2. 魔法注釋(Magic Comments)
我們在這篇文章中,主要的對象是作用域的提升scope hoisting
如果對於webpack前兩個版本了解的同學,應該知道,在打包後的文件裡面,每個模塊都會被包裝在一個單獨的函數閉包中,這些閉包會導致你JS在瀏覽器中的執行速度變慢,並且會導致內存佔用率增加,而另外的打包工具rollup則是將所有的模塊包裝在一個大的閉包中
所以在webpack3中增加了這個功能,我們可以在配置項中添加對應的配置來開啟該功能
module.exports = {n // ...n plugins: [n new webpack.optimize.ModuleConcatenationPlugin()n ]n}n // ...n
並且該功能也會讓代碼打包的體積變得更小,加快運行的速度
Scope Hoisting 和 Code splitting
我們都知道webpack有一個核心功能是Code-splitting, 並且在webpack2也開始支持了原生ES6的模塊載入方案,那麼對於按需載入的模塊,肯定是在打包過程中,形成另外的打包文件的,那麼webpack 是怎麼做到 code splitting的呢?
所以在webpack中不能將所有所有的模塊直接放在同一個作用域下,有以下幾個原因:
1. 按需載入的模塊
2. 使用commonjs規範的模塊
3. 被不能在同一作用域的模塊所共享的模塊
可能說起來比較抽象,我們舉個例子,來對比下webpack2和webpack3再打包同樣的代碼後生成的代碼
webpack2 VS webpack3
我們都統一建立如下關係的文件,分別用webpack2和webpack3打包,觀察下生成的打包文件的不同
上圖中實現表示的是同步的依賴關係,大箭頭表示的是非同步的依賴關係
根據我們上文中所說的規則,webpack會使用一種稱為 「局部範圍提升」的方法,該方法會選擇可以展開的最大的ES模塊進行變數提升,會將它們和webpack的默認原函數相連接
所以上述的模塊之間的關係會變成下圖:
**index.js**nn```nimport { a, x, y } from "./a";nimport * as b from "./b";nnimport("./lazy").then(function(lazy) {ntconsole.log(a, b.a(), x, y, lazy.c, lazy.d.a, lazy.x, lazy.y);n});n```nn**lazy.js**nn```nexport * from "./c";nimport * as d from "./d";nexport { d };n```nn**a.js**nn```n// module anexport var a = "a";nexport * from "./shared";n```nn**b.js**nn```n// module bnexport function a() {ntreturn "b";n};n```nn**c.js**nn```n// module cnimport { c as e } from "./cjs";nnexport var c = String.fromCharCode(e.charCodeAt(0) - 2);nnexport { x, y } from "./shared";n```nn**d.js**nn```n// module dnexport var a = "d";n```nn**cjs.js**nn```n// module cjs (commonjs)nexports.c = "e";n```nn**shared.js**nn```n// shared modulenexport var x = "x";nexport * from "./shared2";n```nn**shared2.js**nn```n// shared2 modulenexport var y = "y";n```nn**webopack2打包的代碼**nn> 只保留了模塊的代碼nn```n// bundle.jsn/* 0 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared__ = __webpack_require__(2);n/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_0__shared__["a"]; });n/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_0__shared__["b"]; });n// module anvar a = "a";nnn/***/ }),n/* 1 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";n/* harmony export (immutable) */ __webpack_exports__["a"] = a;n// module bnfunction a() {ntreturn "b";n};nn/***/ }),n/* 2 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return x; });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared2__ = __webpack_require__(5);n/* harmony namespace reexport (by used) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_0__shared2__["a"]; });n// shared modulenvar x = "x";nnn/***/ }),n/* 3 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__a__ = __webpack_require__(0);n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__b__ = __webpack_require__(1);nnnn__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 4)).then(function(lazy) {ntconsole.log(__WEBPACK_IMPORTED_MODULE_0__a__["a"], __WEBPACK_IMPORTED_MODULE_1__b__["a"](), __WEBPACK_IMPORTED_MODULE_0__a__["b" /* x */], __WEBPACK_IMPORTED_MODULE_0__a__["c" /* y */], lazy.c, lazy.d.a, lazy.x, lazy.y);n});nn/***/ }),n/* 4 */,n/* 5 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return y; });n// shared2 modulenvar y = "y";nn/***/ })n/******/ ]);n```nn```n// 0.bundle.jsnwebpackJsonp([0],[n/* 0 */,n/* 1 */,n/* 2 */,n/* 3 */,n/* 4 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__c__ = __webpack_require__(6);n/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["a"]; });n/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "x", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["b"]; });n/* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "y", function() { return __WEBPACK_IMPORTED_MODULE_0__c__["c"]; });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__d__ = __webpack_require__(8);n/* harmony reexport (module object) */ __webpack_require__.d(__webpack_exports__, "d", function() { return __WEBPACK_IMPORTED_MODULE_1__d__; });nnnnn/***/ }),n/* 5 */,n/* 6 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return c; });n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs__ = __webpack_require__(7);n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__cjs__);n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__shared__ = __webpack_require__(2);n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["a"]; });n/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["b"]; });n// module cnnnvar c = String.fromCharCode(__WEBPACK_IMPORTED_MODULE_0__cjs__["c"].charCodeAt(0) - 2);nnnn/***/ }),n/* 7 */n/***/ (function(module, exports) {nn// module cjs (commonjs)nexports.c = "e";nn/***/ }),n/* 8 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });n// module dnvar a = "d";nn/***/ })n]);n
上述代碼可以發現,每個文件都被視為一個module,並且使用了閉包函數把它們分別進行了包裹
**webpack3打包代碼**nn> 只保留模塊的代碼nn```n// bundle.jsn/* 0 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });nn// CONCATENATED MODULE: ./shared2.jsn// shared2 modulenvar y = "y";n// CONCATENATED MODULE: ./shared.jsn/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return x; });n/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "b", function() { return y; });n// shared modulenvar x = "x";nnn/***/ }),n/* 1 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });nn// CONCATENATED MODULE: ./a.jsn/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__shared__ = __webpack_require__(0);n// module anvar a = "a";nn// CONCATENATED MODULE: ./b.jsn// module bnfunction b_a() {ntreturn "b";n};n// CONCATENATED MODULE: ./index.jsnnnn__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 3)).then(function(lazy) {ntconsole.log(a, b_a(), __WEBPACK_IMPORTED_MODULE_0__shared__["a"], __WEBPACK_IMPORTED_MODULE_0__shared__["b"], lazy.c, lazy.d.a, lazy.x, lazy.y);n});n```nn```n// 0.bundle.jsnwebpackJsonp([0],[n/* 0 */,n/* 1 */,n/* 2 */n/***/ (function(module, exports) {nn// module cjs (commonjs)nexports.c = "e";nn/***/ }),n/* 3 */n/***/ (function(module, __webpack_exports__, __webpack_require__) {nn"use strict";nObject.defineProperty(__webpack_exports__, "__esModule", { value: true });nn// CONCATENATED MODULE: ./c.jsn/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs__ = __webpack_require__(2);n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__cjs___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__cjs__);n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__shared__ = __webpack_require__(0);n// module cnnnvar c = String.fromCharCode(__WEBPACK_IMPORTED_MODULE_0__cjs__["c"].charCodeAt(0) - 2);nnn// CONCATENATED MODULE: ./d.jsnvar d_namespaceObject = {};n__webpack_require__.d(d_namespaceObject, "a", function() { return a; });n// module dnvar a = "d";n// CONCATENATED MODULE: ./lazy.jsn/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "c", function() { return c; });n/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "x", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["a"]; });n/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "y", function() { return __WEBPACK_IMPORTED_MODULE_1__shared__["b"]; });n/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "d", function() { return d_namespaceObject; });nnnnn/***/ })n]);n
結論
通過webpack2和webpack3的所形成的打包文件的對比來看,在代碼的體積上,和性能上是有所提升的,對於一個很大型且複雜的項目來說,webpack3的性能速度有所提升和打包後的代碼體積更小。
推薦閱讀:
※[webpack] 如何把代碼內聯進html中?
※你的Tree-Shaking並沒什麼卵用
※webpack 多頁面設置
※webpack真的適合SPA么?
TAG:webpack |