1. 概述
Webpack作為現代前端開發中最流行的模塊打包工具,其模塊加載機制值得深入理解。本文將解析Webpack的幾種模塊加載方式,包括數組形式、鍵值對形式和JSONP動態加載。只有理解了它的相關加載機制,我們才可以進行逆向工作。
2. 數組形式的模塊加載
2.1 基本結構
!function (e) {var c = {}; // 用于存儲已加載的模塊function n(t) {if (c[t]) // 如果模塊已加載,則直接返回return c[t].exports;var a = c[t] = { // 創建新模塊對象i: t, // 模塊IDl: !1, // 是否加載完成exports: {} // 模塊導出對象};e[t].call(a.exports, a, a.exports, n); // 執行模塊函數a.l = !0; // 標記模塊已加載return a.exports;}n.m = e; // 存儲所有模塊// 入口執行模塊2n(2);
}([function () {console.log('模塊1')},function () {console.log('模塊2')},function () {console.log('模塊3')}
]);
基本結構建議大家去跟蹤調試一遍,就明白原理了。
2.2 執行流程
初始化階段:創建空對象c用于緩存已加載模塊
模塊加載函數n(t):
檢查模塊是否已緩存,是則直接返回
創建新模塊對象,包含ID、加載狀態和導出對象
執行模塊代碼,將模塊標記為已加載
入口執行:從模塊ID=2開始執行
3. 鍵值對形式的模塊加載
3.1 結構變化
!function (e) {// ...相同的主體代碼...
}({0: function () {console.log('模塊1')},1: function () {console.log('模塊2')},2: function () {console.log('模塊3')}
});
3.2 與數組形式的區別
使用對象代替數組存儲模塊
模塊ID作為鍵名,可以是數字或字符串
更靈活地管理模塊,支持非連續ID
4. JSONP動態加載機制
4.1 Webpack的JSONP實現
// JSONP回調函數
function webpackJsonpCallback(data) {var chunkId = data[0]; // 模塊IDvar modules = data[1]; // 模塊代碼var runtime = data[2]; // 運行時回調(可選)// 將新加載的模塊加入Webpack內部模塊管理for (var moduleId in modules) {//Object.prototype.hasOwnProperty.call(modules, moduleId) 檢測對象中是否包含這個屬性if (Object.prototype.hasOwnProperty.call(modules, moduleId)) {__webpack_modules__[moduleId] = modules[moduleId];}}// 執行回調(如果存在)if (runtime) runtime();
}// 重寫push方法
window["webpackJsonp"].push = webpackJsonpCallback;
4.2 動態模塊加載示例
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1], // 模塊ID(chunkId){"./src/moduleA.js": function(module, exports) {console.log("模塊A加載成功");},"./src/moduleB.js": function(module, exports) {console.log("模塊B加載成功");}},[[1]] // 可選的執行入口(runtime callback)
]);
4.3 執行流程
初始化:創建或獲取window[“webpackJsonp”]數組
推送數據:調用push方法傳入模塊數據
回調處理:
提取模塊ID、模塊代碼和運行時回調
將新模塊注冊到__webpack_modules__中
執行運行時回調函數(如果存在)
總結
把上面的代碼都要跑一邊,一定要調試跟蹤一遍,那個是基礎,基礎打牢才可以進行逆向。
5. xhs進行逆向
有了前4節的基礎后,我們知道,其實就是用JS實現了一個加載器,然后通過加載器分步去調用對應的函數。那么第一步我們就是找到加載器的入口。
通過調試分析可以找到他的入口加載是在這個js中,并且是通過__webpack_require__
去進行模塊加載。那么我們要如何修改加載器,讓他可以暴露出去,讓我們進行調用了?
通過對比前4節所學,我們可以知道__webpack_require__
其實就是函數n
通過觀察,我們可以稍微對代碼進行調整,我們把需要傳遞的模塊全部傳遞給eeee
并且發現調用模塊是通過s
,那么我們把eeee
賦值給s
就可以。那么eeee是怎么被傳遞的了,通過前4節我們很清楚的可以知道 在加載器自加載的時候,把模塊全部當成一個參數對象進行傳遞即可,如下圖:
最后我們把參數入口掛載到全局對象上,就可以實現模塊的調用了
window.WEBPACKET=__webpack_require__
有些模塊中的方法是私有的,我們需要如何進行暴露了?
通過觀察我們發現是通過r.d進行對外暴露。如果我想多暴露一個私有方法generateTraceId直接再后面加就行
這樣就可以拿到暴露的方法了。其他方法同理,直接加載模塊然后調用。