WEB前端逆向在nodejs环境中复用webpack代码
介绍如何将Webpack打包后的Browser环境代码复用到Node.js环境中的方法和步骤。 2025-5-13 10:4:12 Author: blog.nsfocus.net(查看原文) 阅读量:14 收藏

阅读: 7

目录:

    ☆ 背景介绍

    ☆ nodejs环境完整实验用例

    ☆ 安装webpack开发环境

    ☆ 用webpack打包成browser可用代码

        1) hello.js

        2) webpack配置文件

        3) webpack打包

        4) hello.html

    ☆ 用AI辅助理解webpack打包结果框架流程

    ☆ webpack动态导出

    ☆ 在nodejs环境中使用browser环境代码

        1) 全局导出__webpack_require__函数

        2) run_webpack_code_1.js

        3) 生产模式webpack打包结果复用

            3.1) 全局导出__webpack_require__函数

            3.2) run_webpack_code_1.js

            3.3) 半自动收集245模块所有依赖模块

    ☆ 小结

————————————————————————–

☆ 背景介绍

WEB前端逆向中会遭遇webpack处理过的js代码,这种代码不是给人类阅读的,所以没

什么可读性。有些生成关键字段的函数位于其中,不想调试分析算法逻辑,想视之为

黑盒函数,给in,返回out;想在nodejs环境中复用本来在browser环境中运行的

webpack代码;此过程俗称「webpack代码抠取」。

简单点说,webpack是一种工具,可将本来在nodejs环境中运行的js处理一下,生成

可在browser环境中运行的js。对WEB前端逆向人员,没必要往更复杂理解。学习思路

如下

a. 写一套nodejs中可用的测试用例

b. 正向应用webpack技术,从a中js得到新的带符号信息的js

c. 编写HTML,在browser中使用b中js

d. F12调试,了解webpack框架流程

e. 正向应用webpack技术,从a中js得到新的strip过的js,接近现实世界案例

f. 针对e中js进行逆向工程,从中抠取webpack代码,得到新的js

g. 在nodejs中加载f中js,调用其中感兴趣的函数,得到返回值

按此思路学习,可从原理上真正理解「webpack代码抠取」。

☆ nodejs环境完整实验用例

假设目录结构如下

————————————————————————–

/path/hello/

|

\—src/

        foo_0.js

        foo_1.js

        foo_2.js

        foo_3.js

        bar_0.js

        bar_1.js

        bar_2.js

        bar_3.js

        hello_node.js

        crypto-js.min.js

————————————————————————–

a中js位于”hello/src”子目录,这是一组nodejs环境完整实验用例。

————————————————————————–

// foo_0.js

function func_foo_0 ( sth ) {

    console.log( sth );

}

module.exports  = {

    func_foo_0  : func_foo_0,

};

————————————————————————–

// foo_1.js

function func_foo_1 ( a, b ) {

    return a + b;

}

module.exports  = {

    func_foo_1  : func_foo_1,

};

————————————————————————–

// foo_2.js

let CryptoJS    = require( ‘./crypto-js.min.js’ );

function func_foo_2 ( sth ) {

    let hash    = CryptoJS.MD5( sth );

    return hash.toString( CryptoJS.enc.Hex );

}

module.exports  = {

    func_foo_2  : func_foo_2,

};

————————————————————————–

// foo_3.js

let CryptoJS    = require( ‘./crypto-js.min.js’ );

function func_foo_3 ( sth ) {

    let hash    = CryptoJS.SHA256( sth );

    return hash.toString( CryptoJS.enc.Hex );

}

module.exports  = {

    func_foo_3  : func_foo_3,

};

————————————————————————–

// bar_0.js

let foo_0   = require( ‘./foo_0.js’ );

function func_bar_0 ( sth ) {

    foo_0.func_foo_0( sth );

}

module.exports  = {

    func_bar_0  : func_bar_0,

};

————————————————————————–

// bar_1.js

let foo_1   = require( ‘./foo_1.js’ );

function func_bar_1 ( a, b ) {

    return foo_1.func_foo_1( a, b );

}

module.exports  = {

    func_bar_1  : func_bar_1,

};

————————————————————————–

// bar_2.js

let foo_2   = require( ‘./foo_2.js’ );

function func_bar_2 ( sth ) {

    return foo_2.func_foo_2( sth );

}

module.exports  = {

    func_bar_2  : func_bar_2,

};

————————————————————————–

// bar_3.js

async function func_bar_3 ( sth ) {

    let foo_3   = await import( ‘./foo_3.js’ );

    return foo_3.func_foo_3( sth );

}

module.exports  = {

    func_bar_3  : func_bar_3,

};

————————————————————————–

// hello_node.js

(async() => {

let bar_1   = require( ‘./bar_1.js’ );

let bar_3   = require( ‘./bar_3.js’ );

let foo_3   = require( ‘./foo_3.js’ );

async function dosth ( sth, a, b ) {

    let bar_0   = await import( ‘./bar_0.js’ );

    let bar_2   = await import( ‘./bar_2.js’ );

    let foo_2   = await import( ‘./foo_2.js’ );

    sth         = sth + ‘ ‘ + bar_1.func_bar_1( a, b );

    bar_0.func_bar_0( sth );

    let ret_2   = bar_2.func_bar_2( sth );

    bar_0.func_bar_0( ret_2 );

    ret_2       = foo_2.func_foo_2( sth );

    bar_0.func_bar_0( ret_2 );

    let ret_3   = await bar_3.func_bar_3( sth );

    bar_0.func_bar_0( ret_3 );

    ret_3       = foo_3.func_foo_3( sth );

    bar_0.func_bar_0( ret_3 );

}

await dosth( ‘Hello World’, 5120, 1314 );

await dosth( ‘Webpack Test’, 1234, 5678 );

})();

————————————————————————–

上例涉及静态导入、动态导入、静态导出,但未涉及动态导出,求MD5、SHA256。

cd /path/hello/src

node hello_node.js

Hello World 6434

2ab71d221778bf89844546711dab751d

2ab71d221778bf89844546711dab751d

f9b5771e0c341ceec6546e44b4d4212930413f185396542628d544618a45149a

f9b5771e0c341ceec6546e44b4d4212930413f185396542628d544618a45149a

Webpack Test 6912

1c4a039a5443cdee3fc425220a46a576

1c4a039a5443cdee3fc425220a46a576

f00ac1bc3e8d90718658228e0c5657c24f55537583fee7527a5e10933657ff27

f00ac1bc3e8d90718658228e0c5657c24f55537583fee7527a5e10933657ff27

☆ 安装webpack开发环境

cd /path/hello

npm init -y (将在当前目录生成package.json)

npm install –save-dev webpack webpack-cli (不要指定-g,将使用当前目录)

npm list

npm uninstall webpack webpack-cli

当前测试版本如下

Node.js v20.11.1

☆ 用webpack打包成browser可用代码

1) hello.js

————————————————————————–

// /path/hello/src/hello.js

(async() => {

let bar_1   = require( ‘./bar_1.js’ );

let bar_3   = require( ‘./bar_3.js’ );

let foo_3   = require( ‘./foo_3.js’ );

async function dosth ( sth, a, b ) {

    let bar_0   = await import( ‘./bar_0.js’ );

    let bar_2   = await import( ‘./bar_2.js’ );

    let foo_2   = await import( ‘./foo_2.js’ );

    sth         = sth + ‘ ‘ + bar_1.func_bar_1( a, b );

    bar_0.func_bar_0( sth );

    let ret_2   = bar_2.func_bar_2( sth );

    bar_0.func_bar_0( ret_2 );

    ret_2       = foo_2.func_foo_2( sth );

    bar_0.func_bar_0( ret_2 );

    let ret_3   = await bar_3.func_bar_3( sth );

    bar_0.func_bar_0( ret_3 );

    ret_3       = foo_3.func_foo_3( sth );

    bar_0.func_bar_0( ret_3 );

}

window.dosth = dosth;

})();

————————————————————————–

从hello_node.js修改出hello.js,主要区别是,全局导出dosth函数,不在IIFE (

Immediately Invoked Function Expression/立即执行的函数表达式)中直接调用

dosth函数,留待hello.html交互式调用,见后。

2) webpack配置文件

webpack打包时,需要原始js,还需要配置文件,一般名为webpack.config.js

————————————————————————–

// /path/hello/webpack.config.js

let path        = require( ‘path’ );

module.exports  = {

    mode: ‘development’,

    entry: ‘./src/hello.js’,

    output: {

        filename: ‘hello.bundle.js’,

        path: path.resolve( __dirname, ‘dist’ ),

        publicPath: ‘dist/’,

        chunkFilename: ‘[name].chunk.js’,

    },

    resolve: {

        fallback: {

            “crypto”: false,

        },

    },

    experiments: {

        topLevelAwait: false,

    },

    optimization: {

        clean: true,

        minimize: false,

    },

    devtool: ‘source-map’,

};

————————————————————————–

// /path/hello/webpack.config_1.js

let path        = require( ‘path’ );

module.exports  = {

    mode: ‘production’,

    entry: ‘./src/hello.js’,

    output: {

        filename: ‘hello.bundle.js’,

        path: path.resolve( __dirname, ‘dist_1’ ),

        publicPath: ‘dist_1/’,

        chunkFilename: ‘[name].chunk.js’,

    },

    resolve: {

        fallback: {

            “crypto”: false,

        },

    },

    experiments: {

        topLevelAwait: false,

    },

    optimization: {

        clean: true,

        minimize: true,

    },

    devtool: false,

};

————————————————————————–

上面给了两个配置文件;webpack.config.js用于开发者模式,打包结果包含丰富的

原始信息,函数名人类可读;webpack.config_1.js用于生产模式,打包结果相当于

strip过。大部分选项都是自解释的,还可用AI辅助理解。entry指定入口js,output

指定打包结果所在。

“resolve fallback”非通用配置。测试用例用到crypto-js.min.js,webpack打包该

文件时有个错,对本例无影响,通过指定”resolve fallback”规避之。

3) webpack打包

假设目录结构如下

————————————————————————–

/path/hello/

|   webpack.config.js       // development mode

|   webpack.config_1.js     // production mode

|   package.json            // npm init -y

|

+—node_modules/           // npm install –save-dev webpack webpack-cli

|

+—dist/                   // 手工创建,存放development mode打包结果

|

+—dist_1/                 // 手工创建,存放production mode打包结果

|

\—src/                    // 原始js所在

        hello.js            // 与webpack.config.js中entry相符

        …

————————————————————————–

cd /path/hello

npx webpack

npx webpack -c webpack.config_1.js

第一条命令默认使用webpack.config.js,第二条命令指定任意配置文件。之后dist、

dist_1分别出现不同模式的打包结果,重点查看其中的hello.bundle.js。

————————————————————————–

/path/hello/

|

+—dist                    // development mode

|       hello.bundle.js     // webpack打包生成

|       hello.bundle.js.map

|       src_bar_0_js.chunk.js

|       src_bar_0_js.chunk.js.map

|       src_bar_2_js.chunk.js

|       src_bar_2_js.chunk.js.map

|       src_foo_2_js.chunk.js

|       src_foo_2_js.chunk.js.map

|

\—dist_1                  // production mode

        245.chunk.js

        808.chunk.js

        954.chunk.js

        hello.bundle.js     // webpack打包生成

————————————————————————–

4) hello.html

webpack打包结果应该在浏览器中测试,配套的hello.html、hello_1.html如下

————————————————————————–

<!DOCTYPE html>

<html>

    <head>

        <meta charset=”utf-8″>

        <title>Hello World</title>

    </head>

    <body>

        <h1>Hello World</h1>

        <button onclick=”(async() => { await dosth( ‘Hello World’, 5120, 1314 ); })();”>Test 0</button>

        <p>

        <button onclick=”(async() => { await dosth( ‘Webpack Test’, 1234, 5678 ); })();”>Test 1</button>

        <script src=”./dist/hello.bundle.js”></script>

    </body>

</html>

————————————————————————–

<!DOCTYPE html>

<html>

    <head>

        <meta charset=”utf-8″>

        <title>Hello World</title>

    </head>

    <body>

        <h1>Hello World</h1>

        <button onclick=”(async() => { await dosth( ‘Hello World’, 5120, 1314 ); })();”>Test 0</button>

        <p>

        <button onclick=”(async() => { await dosth( ‘Webpack Test’, 1234, 5678 ); })();”>Test 1</button>

        <script src=”./dist_1/hello.bundle.js”></script>

    </body>

</html>

————————————————————————–

cd /path/hello

python3 -m http.server -b 192.168.x.x 8080

http://192.168.x.x:8080/hello.html

http://192.168.x.x:8080/hello_1.html

可用F12调试,辅助理解webpack打包结果的框架流程。

☆ 用AI辅助理解webpack打包结果框架流程

参看

《让AI协助阅读代码》

https://scz.617.cn/misc/202503101024.txt

python3 MergeFile.py <path>/dist dist.merge.txt js

写作本节时用的是”Gemini 2.5 Flash Preview 04-17″,上传dist.merge.txt,依次

提问

「这是webpack结果,请介绍每个文件的主要功能,请详细解释hello.bundle.js」

「chunk与module的区别」

「想从webpack生成的js中抽取代码,在nodejs环境中直接调用func_foo_2()」

「解释webpack异步chunk加载机制」

这些问题都得到AI的良好回答,节省篇幅,略。了解webpack框架流程后,从逆向工

程角度看,全局导出__webpack_require__是关键;只需关心moduleId,无需关心

chunkId。

此节内容应亲自实践,不要跳过,后续内容假设已了解webpack框架流程。

☆ webpack动态导出

在hello.bundle.js中有

————————————————————————–

/* webpack/runtime/define property getters */

(() => {

// define getter functions for harmony exports

__webpack_require__.d = (exports, definition) => {

for(var key in definition) {

if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {

Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });

}

}

};

})();

/* webpack/runtime/hasOwnProperty shorthand */

(() => {

__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))

})();

————————————————————————–

__webpack_require__.d用于动态导出。src_foo_2_js.chunk.js中有

————————————————————————–

module.exports  = {

    func_foo_2  : func_foo_2,

};

————————————————————————–

这种姑且称为静态导出,与之对应的动态导出形如

————————————————————————–

__webpack_require__.d(

    __unused_webpack_exports,

    {

        “func_foo_2”: (

            function () {

                return func_foo_2;

            }

        ),

    }

);

————————————————————————–

__webpack_require__.d()的第一形参需要对应模块函数的第二形参,本例模块函数

形参如下

(module, __unused_webpack_exports, __webpack_require__)

本例webpack结果保持静态导出,未使用动态导出,所以模块函数第二形参名字中出

现”unused”,但这不影响”静态导出”转”动态导出”。

现实世界中多次遭遇动态导出。

☆ 在nodejs环境中使用browser环境代码

假设目录结构如下

————————————————————————–

/path/hello/

|

+—browser2node            // development mode

|       hello.bundle_1.js   // 从”dist/hello.bundle.js”修改而来

|       src_foo_2_js.chunk.js

|                           // 从”dist/src_foo_2_js.chunk.js”复制而来

|       run_webpack_code_1.js

|                           // 在nodejs环境中使用browser环境代码

|

\—browser2node_1          // production mode

        hello.bundle_1.js   // 从”dist_1/hello.bundle.js”修改而来

        808.chunk.js        // 从”dist_1/808.chunk.js”复制而来

                            // 此即strip过的src_foo_2_js.chunk.js

        run_webpack_code_1.js

                            // 在nodejs环境中使用browser环境代码

————————————————————————–

1) 全局导出__webpack_require__函数

从dist目录复制hello.bundle.js、src_foo_2_js.chunk.js到browser2node目录,对

hello.bundle_1.js进行改造。

————————————————————————–

function __webpack_require__(moduleId) {

    // Check if module is in cache

    var cachedModule = __webpack_module_cache__[moduleId];

    if (cachedModule !== undefined) {

        return cachedModule.exports;

    }

    // Create a new module (and put it into the cache)

    var module = __webpack_module_cache__[moduleId] = {

        exports: {}

    };

    // Execute the module function

    __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);

    // Return the exports of the module

    return module.exports;

}

//

// 在__webpack_require__函数体后增加如下代码,全局导出该函数

//

globalThis.__exposedWebpackRequire  = __webpack_require__;

————————————————————————–

主要就是全局导出__webpack_require__函数,熟悉框架后可精简hello.bundle_1.js,

初学者不建议精简。

2) run_webpack_code_1.js

————————————————————————–

// /path/hello/browser2node/run_webpack_code_1.js

global.self     = global;

require( ‘./hello.bundle_1.js’ );

require( ‘./src_foo_2_js.chunk.js’ );

let __webpack_require__

                = globalThis.__exposedWebpackRequire;

let foo_2       = __webpack_require__( “./src/foo_2.js” );

let sth         = “Webpack Test 6912”;

console.log( sth );

let ret         = foo_2.func_foo_2( sth );

console.log( ret );

————————————————————————–

为什么要补个全局self?本例加载src_foo_2_js.chunk.js时,第一句就是

(self[“webpackChunkhello”] = self[“webpackChunkhello”] || []).push([[“src_foo_2_js”],{

nodejs环境缺省没有self变量。

本例比较简单,复用webpack打包结果时,补环境时只需补全局self变量;实战时缺

啥补啥。

$ node run_webpack_code_1_test.js

Webpack Test 6912

1c4a039a5443cdee3fc425220a46a576

3) 生产模式webpack打包结果复用

前面是开发者模式webpack打包结果复用,但逆向工程遇上的是生产模式webpack打包

结果复用。实战时,假设F12调试定位到某函数,需要抠取到nodejs环境中调用之。

F12调试hello_1.html时,注意到808.chunk.js中含有MD5相关代码。从dist_1目录复

制hello.bundle.js、808.chunk.js到browser2node_1目录,改成hello.bundle_1.js。

3.1) 全局导出__webpack_require__函数

生产模式strip过,hello.bundle_1.js中没有__webpack_require__这个符号,已变

成某个短名字,可F12动态调试定位。

比如808.chunk.js内容如下:

————————————————————————–

(self.webpackChunkhello = self.webpackChunkhello || []).push([[245, 808], {

    //

    // 245模块入口,第三形参o即__webpack_require__

    //

    245: (e, n, o) => {

        //

        // F12 Console中查看o,即可定位__webpack_require__函数

        //

        let t = o(633);

        e.exports = {

            func_foo_2: function(e) {

                return t.MD5(e).toString(t.enc.Hex)

            }

        }

    //

    // 245模块出口

    //

    }

    ,

    808: (e, n, o) => {

        let t = o(245);

        e.exports = {

            func_bar_2: function(e) {

                return t.func_foo_2(e)

            }

        }

    }

}]);

————————————————————————–

参看注释,可断在245模块入口,查看模块函数第三形参o,跳过去。

本例__webpack_require__函数实际是这个:

————————————————————————–

//

// 此即__webpack_require__函数,只是本例中名为s,t即moduleId

//

function s(t) {

    //

    // o[t]实际是__webpack_module_cache__[moduleId]

    //

    var e = o[t];

    if (void 0 !== e)

        return e.exports;

    var r = o[t] = {

        exports: {}

    };

    //

    // n[t]实际是__webpack_modules__[moduleId]

    //

    return n[t].call(r.exports, r, r.exports, s),

    r.exports

}

//

// 在__webpack_require__函数体后增加如下代码,全局导出该函数

//

globalThis.__exposedWebpackRequire  = s;

————————————————————————–

无论strip与否,__webpack_require__函数框架样子差不多都这样,熟悉后亦可静态

定位之。同样对hello.bundle_1.js进行改造,全局导出__webpack_require__函数。

全局导出时用什么名字无所谓,上面只是我的习惯用法。

3.2) run_webpack_code_1.js

————————————————————————–

// /path/hello/browser2node_1/run_webpack_code_1.js

global.self     = global;

require( ‘./hello.bundle_1.js’ );

require( ‘./808.chunk.js’ );

let __webpack_require__

                = globalThis.__exposedWebpackRequire;

let module_245  = __webpack_require__( 245 );

let module_808  = __webpack_require__( 808 );

let module_54   = __webpack_require__( 54 );

let sth         = “Webpack Test 6912”;

console.log( sth );

let ret;

ret             = module_245.func_foo_2( sth );

console.log( ret );

ret             = module_808.func_bar_2( sth );

console.log( ret );

ret             = module_54.func_foo_3( sth );

console.log( ret );

————————————————————————–

$ node run_webpack_code_1.js

Webpack Test 6912

1c4a039a5443cdee3fc425220a46a576

1c4a039a5443cdee3fc425220a46a576

f00ac1bc3e8d90718658228e0c5657c24f55537583fee7527a5e10933657ff27

3.3) 半自动收集245模块所有依赖模块

假设245模块内部依赖很多其他模块,分散在不同chunk文件中,想收集这些被依赖的

模块,本小节介绍如何达此目的。

在245模块入口、出口分别设断点。待245模块入口断点命中后,Console中查看第三

形参(本例为o),此即__webpack_require__函数;点击跳转,即hello.bundle_1.js

中”function s(t) {“所在。

————————————————————————–

//

// 此即__webpack_require__函数,只是本例中名为s,t即moduleId

//

function s(t) {

    //

    // o[t]实际是__webpack_module_cache__[moduleId]

    //

    var e = o[t];

    if (void 0 !== e)

        return e.exports;

    var r = o[t] = {

        exports: {}

    };

    //

    // n[t]实际是__webpack_modules__[moduleId]

    //

    return n[t].call(r.exports, r, r.exports, s),

    r.exports

}

————————————————————————–

在__webpack_require__()入口设断点,F8继续,断点命中后立即删除该断点。先在

Console中清空__webpack_module_cache__,如下

__webpack_module_cache__ = {}   // 原理

o = {}                          // 本例

这步是为了后面的__webpack_modules__[moduleId].call(本例对应n[t].call)触发

更多的依赖。考虑这种情况,245模块依赖633模块,633模块在245模块之前因为其他

原因已加载过,__webpack_module_cache__中有633模块对应项;633模块依赖某个假

想中的xxx模块;现在加载245模块,n[t].call不会再次触发对633、xxx模块的加载,

很可能导致收集不到xxx模块信息。预清空__webpack_module_cache__,可收集245模

块更多的依赖。

即使这样,也不能保证一次性收集245模块所有依赖。比如加载245模块时触发的依赖

已收集到位,但调用245模块中某函数时触发的依赖在加载245模块时收集不到,只能

等报错后用同样技术继续补充模块,此时可能出现模块重复;现实世界遭遇过。

假设正断在__webpack_require__()入口,立即在Console中定义一个全局变量

window.module_array = ”

后面靠它收集结果。

__webpack_require__()中必有__webpack_modules__[moduleId].call()。即使strip

过,也能找到,只要找唯一的call()即可,本例中是n[t].call(),此n[t]即

__webpack_modules__[moduleId]。

在call所在行设日志断点,比如

window.module_array += moduleId + ‘:’ + __webpack_modules__[moduleId] + ‘,\n’

window.module_array += ‘”‘ + moduleId + ‘”:’ + __webpack_modules__[moduleId] + ‘,\n’

本例应为

window.module_array += t + ‘:’ + n[t] + ‘,\n’

日志断点中作为key出现的moduleId两侧加双引号与否,视当前case而定,两种情况

都在现实世界遭遇过。

为什么在call所在行设日志断点,而非在__webpack_require__()入口设日志断点?

即使前面已经预清空__webpack_module_cache__,但在加载245模块过程中,仍可能

多次命中__webpack_module_cache__,cache的目的就是这个。若在入口设前述日志

断点,可能导致module_array重复收集模块,体积不必要地增大。

测试观察到意外现象,dict的key可以重复,不会报错。若真碰上模块重复,可不消

重,有洁癖时再说。

F8继续执行,会断在245模块出口断点,Console中查看window.module_array,即245

模块所有依赖模块。右键”Copy string contents”,用美化网站查看,比如:

https://beautifier.io/

理论上可将window.module_array的内容全部放入one_bundle.js,本例过于简单,看

不出效果。此法在超级复杂的现实webpack中半自动抠取成功,呈深度优先遍历。

为什么在245模块入口、出口同时设断点?因为只想收集245模块的依赖模块,出口断

点可避免污染window.module_array。

前述操作初看较繁琐,熟悉后并不复杂。亦可考虑Local Overrides,而非日志断点。

☆ 小结

「webpack代码抠取」基本过程

a. F12调试跟踪至目标模块中某函数,欲抠取

b. 识别出目标模块的模块函数,识别模块入口、出口

c. 通过模块入口第三形参定位__webpack_require__()

d. 下载webpack运行时所在js,修改,全局导出__webpack_require__()

e. 下载目标模块所在chunk,精简,只留目标模块

f. 半自动抠取目标模块的依赖模块,补充到e中chunk

g. 编写run_webpack_code.js,加载webpack运行时,加载f中chunk,调用目标函数

步骤d、e可能是同一个js。步骤g可能需要二次补依赖模块。


文章来源: https://blog.nsfocus.net/nodejs/
如有侵权请联系:admin#unsafe.sh