不知不覺,webpack文章寫道第三篇了,上篇文章寫了webpack的配置分離,這裏講一下webpack的代碼分離,webpack chunk和一些webpack的調試吧。
代碼分割不單單是提取業務的公共代碼,更應該關注的是實現代碼的按需加載。 經過在模塊中定義 split point ,webpack 會在打包的時候自動的分割代碼塊。定義 split point 的方式有兩種:html
require.ensure 方法:react
/** * @param dependencies [Array] 模塊依賴的數組 * @param callback [Function] */
require.ensure(dependencies, callback)
eg:
// 模塊 index.jswebpack
/** * [description] * @param {[type]} ) { const async [description] * @return {[type]} [description] */ require.ensure(['./async'], function() { const async = require('./async'); console.log(async.default) });
// 模塊 async.jsweb
export default { a: 1 }
webpack 打包事後會生成三個文件json
index.js 1.1.js vendor.common.js
數組
index.js 的內容爲:服務器
webpackJsonp([0],[ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; __webpack_require__.e/* nsure */(1, function () { var async = __webpack_require__(1); console.log(async.default); }); /***/ } ]); //# sourceMappingURL=home.js.map
1.1.js 的內容爲app
webpackJsonp([1],[ /* 0 */, /* 1 */ /***/ function(module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * async module */ exports.default = { a: 1 }; /***/ } ]); //# sourceMappingURL=1.1.js.map
webpackJsonp 方法定義在 vendor.bundle.js 中:dom
window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) { /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, callbacks = []; /******/ for(;i < chunkIds.length; i++) { /******/ chunkId = chunkIds[i]; /******/ if(installedChunks[chunkId]) /******/ callbacks.push.apply(callbacks, installedChunks[chunkId]); /******/ installedChunks[chunkId] = 0; /******/ } /******/ for(moduleId in moreModules) { /******/ modules[moduleId] = moreModules[moduleId]; /******/ } /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules); /******/ while(callbacks.length) /******/ callbacks.shift().call(null, __webpack_require__); /******/ if(moreModules[0]) { /******/ installedModules[0] = 0; /******/ return __webpack_require__(0); /******/ } /******/ };
因此在 html 中應該先加載 vendor.bundle.js異步
<script src="js/vendor.bundle.js"></script> <script src="js/index.js"></script>
在使用異步加載的時候須要注意一下兩點:
AMD 的方式也能夠實現異步加載,這和使用 require.js 的使用方式基本相同,在定義模塊的時候須要按照 AMD 的規範來定義
/** * @param dependencies [Array] 模塊依賴 * @param callback [Function] */ require(dependencies, callback)
eg:
// 定義模塊 define('modula-a', ['module-c'], function(c) { // return ... })
// 依賴模塊 require(["module-a", "module-b"], function(a, b) { console.log(a, b); });
這時候的 require 實現是異步的方式,只有依賴的模塊加載完成並執行回調,纔會執行模塊的 callback,依賴模塊的回調結果會做爲參數傳入 a, b 中。
webpack 中 Chunk 實際上就是輸出的 .js 文件,可能包含多個模塊,主要的做用是爲了優化異步加載。
Entry Chunk
入口代碼塊包含了 webpack 運行時須要的一些函數,如 webpackJsonp, webpack_require 等以及依賴的一系列模塊。
Normal Chunk
普通代碼塊沒有包含運行時須要的代碼,只包含模塊代碼,其結構有加載方式決定,如基於 CommonJs 異步的方式可能會包含 webpackJsonp 的調用。 The chunk also contains a list of chunk id that it fulfills.
Initial chunk
與入口代碼塊對應的一個概念是入口模塊(module 0),若是入口代碼塊中包含了入口模塊 webpack 會當即執行這個模塊,不然會等待包含入口模塊的代碼塊,包含入口模塊的代碼塊其實就是 initial chunk。 以上面的 CommonJs 異步加載爲例:
<!-- 入口 Chunk, 未包含入口模塊 --> <script src="js/vendor.bundle.js"></script> <!-- 包含入口模塊的 Initial Chunk,執行入口模塊 --> <script src="js/index.js"></script>
以前咱們已經利用 CommonsChunkPlugin 來分割公共代碼如 react, react-dom 到 vendor.bundle.js 中,這裏介紹相關的原理。
如下面的配置爲例
var webpack = require("webpack"); module.exports = { entry: { a: "./a", b: "./b" }, output: { filename: "[name].js" }, plugins: [ new webpack.optimize.CommonsChunkPlugin("init.js") ] }
當有多個入口的時候,CommonsChunkPlugin 會把 a,b 模塊公共依賴的模塊抽離出來,並加上 webpack 運行時代碼,造成一個新的代碼塊,這個代碼塊類型爲 entry chunk。a,b 兩個入口會造成兩個單獨的代碼塊,這兩個代碼塊爲 initial chunk。
在 html 中,能夠以下加載:
<!-- entry chunk --> <script src="init.js"></script> <!-- inital chunk a --> <script src="a.js"></script> <!-- initial chunk b --> <script src="b.js"></script>
require.include
可使用 require.include 方法,直接引入模塊,以下例子:
require.ensure(["./file"], function(require) { require("./file2"); }); // is equal to require.ensure([], function(require) { require.include("./file"); require("./file2"); });
這個方法能夠實現一些分塊的優化,當一個 chunk-parent 可能會異步引用多個 chunk-child 而這些 chunk-child 可能都包含了 moduleA, 那麼能夠在 chunk-parent 中 require.include('moduleA') 就能夠避免重複加載 moduleA。
給異步 Chunk 命名
根據 split point 生成出來的 chunk 名稱都是數字,能夠在 split point 上定義 chunk 名稱:
/** * @param chunkName [String] chunk 名稱 */ require.ensure(dependencies, callBack, chunkName)
也能夠在 webpack.config.js 中配置修改 output.chunkName 來修改 chunk 名稱
在一些特殊的場景能夠利用以下這些插件來完成 Chunk 的優化,
在配置 webpack 的過程當中,能夠利用 webpack 提供的一些工具和參數來調試。
$ webpack [--params,...]
1.--progress: 可以看到打包進度
2.--json: 能夠將打包結果輸出爲 json
3.--display-chunks: 能夠看到打包出來的 chunk 信息
能夠經過 analyse 網站分析 webpack 的編譯結果,以下圖,能夠分析 chunks, modules, Assets 等。