上一篇文章webpack详解中介绍了webpack基于事件流编程,是个高度的插件集合,总体介绍了webpack 的编译流程。本文将单独聊一聊最核心的部分,编译&构建。javascript
webpack的构建中总会经历以下几个事件节点。html
其中make
是整个构建中最核心的部分编译,经过模块工厂函数建立模块,而后对模块进行编译。java
*ModuleFactory
是指模块工厂函数,之因此会有模块工厂这样的函数,还要从webpack中
entry
的配置提及,在webpack的配置项中
entry
支持以下类型:
string
[string]
object { <key>: string | [string] }
(function: () => string | [string] | object { <key>: string | [string] })
为了处理之后不一样类型的入口模块,因此就须要个模块工厂来处理不一样的入口模块类型。webpack
string|object { <key>: string }
[string]|object { <key>: [string] }
(function: () => string | [string] | object { <key>: string | [string] })
上图中为了简单说明构建的流程,就以最直接的singleEntry
类型提及,对于此类入口模块,webpack均使用NormalModuleFactory
来建立模块,这个建立的模块的类型叫NormalModule
,在NormalModule
中实现了模块的构建方法build
,使用runLoaders
对模块进行加载,而后利用进行解析,分析模块依赖,递归构建。git
到构建封装阶段时候,代码构建已经完毕,可是如何将这些代码按照依赖引用逻辑组织起来,当浏览器将你构建出来的代码加载到浏览器的时候,仍然可以正确执行。在webpack中经过Manifest
记录各个模块的详细要点,经过Runtime
来引导,加载执行模块代码,特别是异步加载。github
###Runtime 如上所述,咱们这里只简略地介绍一下。runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行时,webpack 用来链接模块化的应用程序的全部代码。runtime 包含:在模块交互时,链接模块所需的加载和解析逻辑。包括浏览器中的已加载模块的链接,以及懒加载模块的执行逻辑。web
###Manifest 那么,一旦你的应用程序中,形如 index.html 文件、一些 bundle 和各类资源加载到浏览器中,会发生什么?你精心安排的 /src 目录的文件结构如今已经不存在,因此 webpack 如何管理全部模块之间的交互呢?这就是 manifest 数据用途的由来…… 当编译器(compiler)开始执行、解析和映射应用程序时,它会保留全部模块的详细要点。这个数据集合称为 "Manifest",当完成打包并发送到浏览器时,会在运行时经过 Manifest 来解析和加载模块。不管你选择哪一种模块语法,那些 import 或 require 语句如今都已经转换为 webpack_require 方法,此方法指向模块标识符(module identifier)。经过使用 manifest 中的数据,runtime 将可以查询模块标识符,检索出背后对应的模块。编程
定义了一个当即执行函数,声明了__webpack_require__
,对各类模块进行加载。数组
(function(modules) { // webpackBootstrap
var installedModules = {}; // cache module
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, {
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 = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
})([/**modules*/])
复制代码
上面提到的代码片断即是webpack构建后在浏览器中执行的引导代码。也就是上面提到的runtime
。它是个当即执行函数,那么入参modules
即是上面的Manifest
,组织各个模块的依赖逻辑。浏览器
(function(modules){
// ...
// runtime
function __webpack_require__(moduleId) {
// 加载逻辑
}
// ...
})([function (module, exports, __webpack_require__) {
var chunk1 = __webpack_require__(1);
var chunk2 = __webpack_require__(2);
}, function (module, exports, __webpack_require__) {
__webpack_require__(2);
var chunk1 = 1;
exports.chunk1 = chunk1;
}, function (module, exports) {
var chunk2 = 1;
exports.chunk2 = chunk2;
}])
复制代码
上面说到了runtime
和manifest
就是在seal
阶段注入的
class Compilation extends Tapable {
seal(callback) {
this.hooks.seal.call();
// ...
if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
this.hooks.beforeChunkAssets.call();
this.createChunkAssets();
}
// ...
}
createChunkAssets() {
// ...
for (let i = 0; i < this.chunks.length; i++) {
const chunk = this.chunks[i];
// ...
const template = chunk.hasRuntime()
? this.mainTemplate
: this.chunkTemplate; // 根据是否有runTime选择模块,入口文件是true, 须要异步加载的文件则没有
const manifest = template.getRenderManifest({
// 生成manifest
chunk,
hash: this.hash,
fullHash: this.fullHash,
outputOptions,
moduleTemplates: this.moduleTemplates,
dependencyTemplates: this.dependencyTemplates
}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
// ...
}
}
复制代码
经过template
最后将代码组织起来,上面看到的构建后的代码就是mainTemplate
生成的。
经过template
生成最后代码,构建已经完成,接下来就是将代码输出到dist
目录。
腾讯IVWEB团队的工程化解决方案feflow已经开源:Github主页:https://github.com/feflow/feflow