前言
通过前两篇文章
我们可以知道 webpack 的安装与使用,以及一些基本配置,最终可以实现一个自动构建过程,那么构建之后的文件又是怎样的 ? 它又是如何解析的,今天我们来探讨一下
打包后的 bundle.js
文件
这里呢,我引用上述文章的示例,拿到直接打包后的 bundle.js
文件来分析,先来看源代码 我加了注释
(function(modules) {
// 创建一个缓存,用于存储已加载过的模块
var installedModules = {};
// 自定义 require 函数,用于加载模块
function __webpack_require__(moduleId) {
// 判断模块是否已加载过,在缓存
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 创建一个新的模块
var module = installedModules[moduleId] = {
i: moduleId, // 模块 id
l: false, // 是否倍加载过
exports: {} // 模块实体
};
// 执行对应模块实体,对应的自执行函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 标识模块已被加载
module.l = true;
// 返回模块实体
return module.exports;
}
// __webpack_require__ 函数自定义属性及方法 start
// 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, {
configurable: false,
enumerable: true,
get: getter
});
}
};
// 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 = "";
// __webpack_require__ 函数自定义属性及方法 end
// 加载入口文件模块
return __webpack_require__(__webpack_require__.s = 0);
})
/***************************** 参数如下 *******************************************/
([
/* 0 */ 下标
(function(module, exports, __webpack_require__) {
var show = __webpack_require__(1);
console.log("我是入口文件main.js")
show('Webpack');
}),
/* 1 */ 下标
(function(module, exports) {
console.log("我是utils.js")
function show (content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
}
module.exports = show;
})
]);
我们分析一下上边源码的结构,仔细看一下,其实是一个自执行函数,传入了模块参数,大概是这个样子
(
function (modules) { /* 实体 */ }
)
(
[
( function(module, exports, __webpack_require__) { /* 内容 */ } ),
( function(module, exports) { /* 内容 */ } )
]
)
解析过程
了解一下前提,webpack 采用的是 common.js 规范,所以默认可以识别 js 文件,在 common.js 模块规范里, 一个 Js 文件就是一个模块 。
- common.js 模块规范
- 导入: var m = require("文件路径")
- 导出: module.exports = m
好,了解 common.js 规范后,我们来看一下 webpack 到底是怎么解析的
首先进入自执行函数,看到传入的参数 modules
,modules 是什么 ? modules 是一个数组,数组内容则是各个依赖的 Js 模块(包含初始化模块,也就是webpack entry
配置的入口文件)
然后就是执行 return __webpack_require__(__webpack_require__.s = 0);
,webpack_require 是什么 ? webpack_require 函数是用来替代 require 函数的,主要是创建一个新的模块,放入缓存,并执行对应的Js模块实体; ↓ ↓ ↓
// 自定义 require 函数,用于加载模块
function __webpack_require__(moduleId) {
// 判断模块是否已加载过,在缓存
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 创建一个新的模块
var module = installedModules[moduleId] = {
i: moduleId, // 模块 id
l: false, // 是否倍加载过
exports: {} // 模块实体
};
// 执行对应模块实体,对应的自执行函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 标识模块已被加载
module.l = true;
// 返回模块实体
return module.exports;
}
执行对应的 Js 模块实体又是什么 ? 就是这句代码
// 执行对应模块实体,自执行函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
- modules: 是参数,上边有说,是一个数组,数组内容则是各个依赖的 Js 模块
- moduleId:在这里则是指下标,上述代码可以看出初始化传入的是 0
三个参数
- module:创建新的模块对象
- module.exports: 执行 webpack_require 函数传入的对应 Js 模块实体导出
- webpack_require 函数自身
这里传入了 webpack_require 函数自身,可以看的出来,是为了引用自身,实质就是递归寻找依赖,再次解析其它相关的依赖模块就是webpack的本质,递归寻找依赖的所有模块
最后加载过的模块给予标识,标识自己经被加载过了,返回对应模块的导出 exports , 也就是 return module.exports
,这里的 return module.exports
其实返回的就是参数里自执行函数返回的内容,拿上述例子来看:
([
/* 0 */ 下标
(function(module, exports, __webpack_require__) {
var show = __webpack_require__(1);
console.log("我是入口文件main.js")
show('Webpack');
}),
/* 1 */ 下标
(function(module, exports) {
console.log("我是utils.js")
function show (content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
}
module.exports = show;
})
]);
有两个地方需要说明一下
var show = __webpack_require__(1);
这里webpack在编译的时候将 require 替换为 __webpack_require__函数module.exports = show;
就是导出 show 函数自身了
结语
总体的逻辑,通过一个小小的 demo 做了简要分析,webpack 的核心知识点还有很多,后续会分享一下 loader 和plugin 的原理
小小鼓励,大大成长,欢迎点赞