Webpack學習-工作原理(下)
繼上篇介紹了Webpack的基本概念完整流程以及打包過程中廣播的一些事件的作用這篇文章主要講生成的chunk檔案如何輸出成具體的檔案。分同步和非同步兩種情況來分析輸出的檔案使用的webpack版本3.8.0
。
模組檔案show.js
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');
生成的bundle檔案
// webpackBootstrap啟動函式
// modules存放的是所有模組的陣列每個模組都是一個函式
(function(modules) {
var installedModules = {}; // 快取安裝過的模組提升效能
//去傳入的modules陣列中載入對應moduleIdindex的模組與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以非同步載入形式引入這也是分離程式碼達到減少檔案體積的最佳化方法兩個檔案分析如下。
0.bundle.js
// 載入本檔案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;
/***/ })
]);
bundle.js
(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陣列中載入對應moduleIdindex的模組與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函式用於從非同步載入的檔案中去安裝模組。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4328/viewspace-2822294/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Webpack學習-工作原理(上)Web
- 深度學習的工作原理深度學習
- webpack 學習筆記:引入 CSS(下)Web筆記CSS
- webpack 學習筆記:核心概念(下)Web筆記
- webpack 學習筆記:使用 babel(下)Web筆記Babel
- 揭祕webpack外掛的工作原理Web
- 學習webpack(二)Web
- webpack打包學習Web
- 用三張圖理解深度學習的工作原理深度學習
- webpack學習筆記Web筆記
- Webpack學習 – Webpack安裝及安裝Web
- webpack原理Web
- 學習筆記-React的簡單介紹&工作原理筆記React
- AngularJS學習筆記3——AngularJS的工作原理AngularJS筆記
- webpack學習(一)專案中安裝webpackWeb
- 如何學習配置webpack(一)Web
- 《webpack文件》學習筆記Web筆記
- 說說webpack proxy工作原理?為什麼能解決跨域?Web跨域
- webpack4配置學習(一)Web
- webpack學習(四)配置pluginsWebPlugin
- Webpack4學習筆記Web筆記
- webpack1學習筆記Web筆記
- webpack模組化學習記錄Web
- 共同學習Vue.js ---webpackVue.jsWeb
- webpack 學習筆記:使用 lodashWeb筆記
- 工作學習整理
- webpack系列--淺析webpack的原理Web
- Webpack 原理淺析Web
- Webpack 模組打包原理Web
- webpack原理解析Web
- webpack 熱更新原理Web
- webpack學習(四) -- css tree shakingWebCSS
- WebPack持久快取學習小結Web快取
- webpack學習(二)初識打包配置Web
- 從0開始學習Webpack(一)Web
- webpack入門學習手記(四)Web
- webpack入門學習手記(二)Web
- webpack入門學習手記(一)Web