webpack 打包后代码分析
简单情况分析
前言
打包前各个脚本:
index.js
//
import { log } from './util';
log('abc');
util.js
export function log(v) { console.log(v); }
打包后代码:
(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;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// 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 });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if (mode & 1) value = __webpack_require__(value);
if (mode & 8) return value;
if (mode & 4 && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if (mode & 2 && typeof value != 'string')
for (var key in value)
__webpack_require__.d(
ns,
key,
function(key) {
return value[key];
}.bind(null, key),
);
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__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;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
// __webpack_public_path__
__webpack_require__.p = '';
// Load entry module and return exports
return __webpack_require__((__webpack_require__.s = './webpack-sourcecode/index.js'));
})({
'./webpack-sourcecode/index.js':
/*! no exports provided */
function(module, __webpack_exports__, __webpack_require__) {
'use strict';
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ './webpack-sourcecode/util.js');
Object(_util__WEBPACK_IMPORTED_MODULE_0__['log'])('abc');
},
'./webpack-sourcecode/util.js':
/*! exports provided: log */
function(module, __webpack_exports__, __webpack_require__) {
'use strict';
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, 'log', function() {
return log;
});
function log(v) {
console.log(v);
}
},
});
概述
以js脚本路径为key,一个函数为值去调用最外层的这个IFFE函数(IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数)。对于作为entry
的js脚本来说,这个函数为 “main” 函数,对于它所依赖的脚本,这个函数则会返回utils脚本export
出的成员。
代码逐行分析
以entry
脚本作为参数执行__webpack_require__函数,同时以__webpack_require__.s记录entry
脚本路径。
JS赋值操作也有返回值,即等号右边的值。
// Load entry module and return exports
return __webpack_require__((__webpack_require__.s = './webpack-sourcecode/index.js'));
核心逻辑:webpack_require 函数
// 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;
}
流程图:

// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
该行函数会去调用index
脚本对应的函数(即初始时传入IIFE函数的参数:以脚本路径为key,函数为值),这里会去调用此函数
function(module, __webpack_exports__, __webpack_require__) {
'use strict';
__webpack_require__.r(__webpack_exports__);
/* harmony import -- 用于tree-shaking的一种标记 */
// 使用__webpack_require__加载依赖脚本返回其中的exports成员,这里是utils脚本
var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ './webpack-sourcecode/util.js');
// 调用utils脚本导出的log函数执行最终的逻辑
Object(_util__WEBPACK_IMPORTED_MODULE_0__['log'])('abc');
}
此时,会在新创建出来的module
对象上调用上述函数,各参数对应的值如下:
参数名 | 值 |
---|---|
module | 新建出来出来的module对象:{ i: moduleId, l: false, exports: {}} |
__webpack_exports__ | 新建出来出来的module对象的exports属性,初始为空 |
__webpack_require__ | __webpack_require__函数 |
__webpack_require__.r(__webpack_exports__);
调用__webpack_require__.r
函数,主要是对exports对象新定义一个属性。
// define __esModule on exports
__webpack_require__.r = function(exports) {
//判断是否支持Symbol,支持的话则直接将exports对象的对象描述设置为 Module
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
关于Symbol
,在JavaScript中,Symbol
是一种基本数据类型,它用于创建唯一的标识符, 引入自ES6.Symbol
类型的值是通过调用Symbol()
函数生成的,每次调用都会生成一个独一无二的Symbol
值,即使你传入相同的参数。Symbol
通常用作对象属性的键,以确保属性名的唯一性,避免属性名的冲突。
关于Symbol.toStringTag
,这是一个内置的Symbol,当调用 Object.prototype.toString()
方法时,如果对象有 Symbol.toStringTag
属性,这个属性的值会出现在 toString()
方法返回的字符串中,表示对象的类型。这允许开发者自定义对象的字符串描述,而不是使用默认的 “[object Object]”
使用__webpack_require__
函数加载utils
脚本并返回其中的exports成员。
var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ './webpack-sourcecode/util.js');
同上述index脚本一样,__webpack_require__
函数会去执行utils脚本对应的函数:
function(module, __webpack_exports__, __webpack_require__) {
'use strict';
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, 'log', function() {
return log;
});
function log(v) {
console.log(v);
}
}
此处调用__webpack_require__.d
函数
__webpack_require__.d(__webpack_exports__, 'log', function() {
return log;
});
// 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 });
}
};
__webpack_require__.o
函数用来检测exports对象是否有指定属性。
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
这里则是检查utils是否已经导出过log
函数,如果没有则给exports新增一个log
属性,值为
function log(v) {
console.log(v);
}
即打包前utils脚本中定义的log
函数。
// util.js
export function log(v) {
console.log(v);
}
_util__WEBPACK_IMPORTED_MODULE_0__
即为utils脚本对应模块的exports属性,
此处传入abc
参数调用log函数,至此所有逻辑完成了。
// 调用utils脚本导出的log函数执行最终的逻辑
Object(_util__WEBPACK_IMPORTED_MODULE_0__['log'])('abc');
转载自:https://juejin.cn/post/7349940323026108457