快速Webpack学习记之《__webpack_require__》

准备

为了看到Webpack是怎么把模块进行加载和打包,这里创建了两个文件,分别是a.js和index.js,index.js调用了a.js的funcA。

/* src/a.js */

export function funcA() {

console.log(‘in funcA’);

}

/* src/index.js */

import { funcA } from ‘./a’;

export function funcB() {

funcA();

console.log(‘in funcB’);

}

webpack处理结果分析

以下webpack打包出来的文件(我做了一些删减和eval的转换)。
接下来我们一步一步看看模块是怎么加载的。

var WebpackTest =

(function(modules) { // webpackBootstrap

// The module cache

var installedModules = {};

// The require function

function webpack_require(moduleId) {

// Check if module is in cache

if(installedModules[moduleId]) {

return installedModules[moduleId].exports;

}

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

var module = installedModules[moduleId] = {

i: moduleId,

l: false,

exports: {}

};

// Execute the module function

modules[moduleId].call(module.exports, module, module.exports,
webpack_require);

// Flag the module as loaded

module.l = true;

// Return the exports of the module

return module.exports;

}

// define getter function for harmony exports

webpack_require.d = function(exports, name, getter) {

if(!webpack_require.o(exports, name)) {

Object.defineProperty(exports, name, { enumerable: true, get: getter });

}

};

// define __esModule on exports

webpack_require.r = function(exports) {

if(typeof Symbol !== ‘undefined’ && Symbol.toStringTag) {

Object.defineProperty(exports, Symbol.toStringTag, { value: ‘Module’
});

}

Object.defineProperty(exports, ‘__esModule’, { value: true });

};

// Object.prototype.hasOwnProperty.call

webpack_require.o = function(object, property) { return
Object.prototype.hasOwnProperty.call(object, property); };

// Load entry module and return exports

return webpack_require(webpack_require.s =
“./src/index.js”);

})

/************************************************************************/

({

“./src/a.js”:

(function(module, webpack_exports, webpack_require) {

webpack_require.r(webpack_exports);

webpack_require.d(webpack_exports, “funcA”, function()
{ return funcA; });

function funcA() { console.log(‘in funcA’);}

}),

“./src/index.js”:

(function(module, webpack_exports, webpack_require) {

webpack_require.r(webpack_exports);

webpack_require.d(webpack_exports, “funcB”, function()
{ return funcB; });

var a__WEBPACK_IMPORTED_MODULE_0_ =
webpack_require(“./src/a.js”);

function funcB() {

Object(a__WEBPACK_IMPORTED_MODULE_0_[“funcA”])();

console.log(‘in funcB’);

}

})

});

step 1

我们看到整个立即执行函数的真正执行的地方。
./src/index.js就是在webpack.config.js中定义的entry。

return webpack_require(webpack_require.s =
“./src/index.js”);

step 2

接下来轮到我们的主角__webpack_require__。

var installedModules = {};

// The require function

function webpack_require(moduleId) {

// Check if module is in cache

// 如果所需加载模块存在缓存中,则直接从缓存取用

if(installedModules[moduleId]) {

return installedModules[moduleId].exports;

}

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

var module = installedModules[moduleId] = {

i: moduleId,

l: false,

exports: {}

};

// Execute the module function

modules[moduleId].call(module.exports, module, module.exports,
webpack_require);

// Flag the module as loaded

module.l = true;

// Return the exports of the module

return module.exports;

}

__webpack_require__函数可以类比CommonJS的require,都是加载模块代码。和NodeJS的设计很类似,都是先从缓存取用,否则加载模块并放入缓存。

__webpack_require__所在的闭包能访问外层变量modules和缓存installedModules。这个很关键,因为modules是webpack打包后立即执行函数传入的参数。modules是一个object,key是string类型,value是function类型。

step3

我们开始加载模块moduleId(‘./src/index.js’),执行模块方法

modules[moduleId].call(module.exports, module, module.exports,
webpack_require);

复制代码

这里我们看到了两个函数,分别是__webpack_require__.r和__webpack_require__.d。

webpack_require.r方法主要是标识该模块为es模块。

webpack_require.d方法是提供Getter给导出的方法、变量。

“./src/index.js”:

(function(module, webpack_exports, webpack_require) {

// 标识模块为es模块

webpack_require.r(webpack_exports);

// 提供funcB的getter

webpack_require.d(webpack_exports, “funcB”, function()
{ return funcB; });

// 加载module a

var a__WEBPACK_IMPORTED_MODULE_0_ =
webpack_require(“./src/a.js”);

function funcB() {

//调用module a的funcA方法

Object(a__WEBPACK_IMPORTED_MODULE_0_[“funcA”])();

console.log(‘in funcB’);

}

})

我们对比看看未经webpack处理的代码,就能明白上面的webpack处理后的代码啦。

/* 以下是源代码,未经webpack处理 */

/* src/index.js */

import { funcA } from ‘./a’;

export function funcB() {

funcA();

console.log(‘in funcB’);

}

step 4

想在浏览器中使用funcB,只需要调用WebpackTest.funcB()即可。
下面为对应的webpack配置

//webpack.config.js

output: {

filename: ‘[name].bundle.js’,

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

library: ‘WebpackTest’,

libraryTarget: ‘var’,

}

结尾

webpack是前端日常使用的工具,也是有必要了解webpack打包处理出来的文件。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!