繼上篇文章介紹了Webpack的基本概念,完整流程,以及打包過程當中廣播的一些事件的做用,這篇文章主要講生成的chunk文件如何輸出成具體的文件。分同步和異步兩種狀況來分析輸出的文件使用的webpack版本:3.8.0
。 javascript
模塊文件show.jshtml
function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content; } // 經過 CommonJS 規範導出 show 函數 module.exports = show;
// main.js import show from './show'; show('TWENTY-FOUR K');
// webpackBootstrap啓動函數 // modules存放的是全部模塊的數組,每一個模塊都是一個函數 (function(modules) { var installedModules = {}; // 緩存安裝過的模塊,提高性能 //去傳入的modules數組中加載對應moduleId(index)的模塊,與node的require語句類似 function __webpack_require__(moduleId) { // 檢查是否已經加載過,若是有的話直接從緩存installedModules中取出 if(installedModules[moduleId]) { return installedModules[moduleId].exports; } // 若是沒有的話,就直接新建一個模塊,而且存進緩存installedModules var module = installedModules[moduleId] = { i: moduleId, // 對應modules的索引index,也就是moduleId l: false, // 標誌模塊是否已經加載 exports: {} }; // 執行對應模塊函數,而且傳入須要的參數 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 將標誌設置爲true module.l = true; // 返回這個模塊的導出值 return module.exports; } // 儲存modules數組 __webpack_require__.m = modules; // 儲存緩存installedModules __webpack_require__.c = installedModules; // 爲Harmony導出定義getter函數 __webpack_require__.d = function(exports, name, getter) { if(!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { configurable: false, enumerable: true, get: getter }); } }; // 用於與非協調模塊兼容的getdefaultexport函數,將module.default或非module聲明成getter函數的a屬性上 __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; }; // 工具函數,hasOwnProperty __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // webpack配置中的publicPath,用於加載被分割出去的異步代碼 __webpack_require__.p = ""; // 使用__webpack_require__函數去加載index爲0的模塊,而且返回index爲0的模塊也就是主入口文件的main.js的對應文件,__webpack_require__.s的含義是啓動模塊對應的index return __webpack_require__(__webpack_require__.s = 0); }) /************************************************************************/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // 設置__esModule爲true,影響__webpack_require__.n函數的返回值 Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); // 同步加載index爲1的依賴模塊 /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__show__ = __webpack_require__(1); // 獲取index爲1的依賴模塊的export /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__show___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__show__); // 同步加載 __WEBPACK_IMPORTED_MODULE_0__show___default()('wushaobin'); /***/ }), /* 1 */ /***/ (function(module, exports) { function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content; } // 經過 CommonJS 規範導出 show 函數 module.exports = show; /***/ }) ]);
// main.js // 異步加載 show.js import('./show').then((show) => { // 執行 show 函數 show('TWENTY-FOUR K'); });
經webpack打包會生成兩個文件0.bundle.js和bundle.js,怎麼作的緣由,是能夠吧show.js以異步加載形式引入,這也是分離代碼,達到減小文件體積的優化方法,兩個文件分析以下。java
// 加載本文件(0.bundle.js)包含的模塊, webpackJsonp用於從異步加載的文件中安裝模塊,掛載至全局(bundle.js)供其餘文件使用 webpackJsonp( // 在其餘文件中存放的模塊id [0],[ // 本文件所包含的模塊 /***/ (function(module, exports) { function show(content) { window.document.getElementById('app').innerText = 'Hello,' + content; } // 經過 CommonJS 規範導出 show 函數 module.exports = show; /***/ }) ]);
(function(modules) { // webpackBootstrap啓動函數 // 安裝用於塊加載的JSONP回調 var parentJsonpFunction = window["webpackJsonp"]; // chunkIds 異步加載文件(0.bundle.js)中存放的須要安裝的模塊對應的chunkId // moreModules 異步加載文件(0.bundle.js)中存放須要安裝的模塊列表 // executeModules 異步加載文件(0.bundle.js)中存放須要安裝的模塊安裝後須要執行的模塊對應的index window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { // 將 "moreModules" 添加到modules對象中, // 將全部chunkIds對應的模塊都標記成已經加載成功 var moduleId, chunkId, i = 0, resolves = [], result; for(;i < chunkIds.length; i++) { chunkId = chunkIds[i]; if(installedChunks[chunkId]) { resolves.push(installedChunks[chunkId][0]); } installedChunks[chunkId] = 0; } for(moduleId in moreModules) { if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { modules[moduleId] = moreModules[moduleId]; } } if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); // 執行 while(resolves.length) { resolves.shift()(); } }; // 緩存已經安裝的模塊 var installedModules = {}; // 儲存每一個chunk的加載狀態 // 鍵爲chunk的id,值爲0表明加載成功 var installedChunks = { 1: 0 }; // 去傳入的modules數組中加載對應moduleId(index)的模塊,與node的require語句類似,同上,此處省略 function __webpack_require__(moduleId) { ... } // 用於加載被分割出去的須要異步加載的chunk對應的文件, // chunkId須要異步加載的chunk對應的id,返回的是一個promise __webpack_require__.e = function requireEnsure(chunkId) { // 從installedChunks中獲取chunkId對應的chunk文件的加載狀態 var installedChunkData = installedChunks[chunkId]; // 若是加載狀態爲0,則表示該chunk已經加載成功,直接返回promise resolve if(installedChunkData === 0) { return new Promise(function(resolve) { resolve(); }); } // installedChunkData不爲空且不爲0時表示chunk正在網絡加載中 if(installedChunkData) { return installedChunkData[2]; } // installedChunkData爲空,表示該chunk尚未加載過,去加載該chunk對應的文件 var promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); installedChunkData[2] = promise; // 經過dom操做,向html head中插入一個script標籤去異步加載chunk對應的javascript文件 var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.type = "text/javascript"; script.charset = 'utf-8'; script.async = true; script.timeout = 120000; // HTMLElement 接口的 nonce 屬性返回只使用一次的加密數字,被內容安全政策用來決定此次請求是否被容許處理。 if (__webpack_require__.nc) { script.setAttribute("nonce", __webpack_require__.nc); } // 文件的路徑由配置的publicPath、chunkid拼接而成 script.src = __webpack_require__.p + "" + chunkId + ".bundle.js"; // 設置異步加載的最長超時時間 var timeout = setTimeout(onScriptComplete, 120000); script.onerror = script.onload = onScriptComplete; // 在script加載和執行完成時回調 function onScriptComplete() { // 防止內存泄漏 script.onerror = script.onload = null; clearTimeout(timeout); var chunk = installedChunks[chunkId]; // 判斷chunkid對應chunk是否安裝成功 if(chunk !== 0) { if(chunk) { chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); } installedChunks[chunkId] = undefined; } }; head.appendChild(script); return promise; }; // 這裏會給__webpack_require__設置多個屬性和方法,同上,此處省略 // 異步加載的出錯函數 __webpack_require__.oe = function(err) { console.error(err); throw err; }; // Load entry module and return exports return __webpack_require__(__webpack_require__.s = 0); }) /************************************************************************/ ([ // 存放沒有通過異步加載的,隨着執行入口文件加載的模塊,也就是同步的模塊 /* 0 */ /***/ (function(module, exports, __webpack_require__) { // 經過__webpack_require__.e異步加載show.js對應的chunk // 異步加載 show.js __webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 1)).then((show) => { // 執行 show 函數 show('Webpack'); }); /***/ }) ]);
這裏咱們經過對比同步加載和異步加載的簡單應用,去分析兩種狀況webpack打包出來文件的差別性,咱們發現,對於異步加載的bundle.js與同步的bundle.js基本類似,都是模擬node.js的require語句去導入模塊,有所不一樣的是多了一個__webpack_require__.e函數,用來加載分割出的須要異步加載的chunk對應的文件,以及一個wepackJsonp函數,用於從異步加載的文件中去安裝模塊。node