4

webpack | 谈谈 webpack 的本质

 3 years ago
source link: https://xie.infoq.cn/article/c48403d68f6b75356c02edb69
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1. 概念

webpack  是一个现代 JavaScript 应用程序的 静态模块打包器(module bundler) 。当 webpack 处理应用程序时,它会递归地构建一个 依赖关系图(dependency graph) ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个  bundle。

2. 作用

webpack 通过 loader 可以支持各种语言和预处理器编写模块(通过loader处理非JavaScript模块,并在bundle中引入依赖),模块可以通过以下方式来表达它们的依赖关系:

1. ES2015 import 语句
2. CommonJS require() 语句
3. AMD define 和 require 语句
4. css/sass/less 文件中的@import语句
5. 样式(url(...))和HTML文件(<img src=...>)中的图片链接

3. 本质

webpack可以理解为一种基于事件流的编程范例,一系列的插件运行,而实现这个插件机制的是Tapable。

1. Tapable是什么?

Tapable公开了许多Hook类,可用于为插件创建钩子。

Tapable是一个类似于Node.js的EventEmitter的库,主要是控制钩子函数的发布与订阅,控制着Webpack的插件系统。

npm install --save tapable

Node.js事件机制简单例子:

// nodejs中的事件机制
constEventEmitter =require('events');
constemitter =newEventEmitter();
// 监听事件
emitter.on('start',()=>{
console.log('start')
})
// 触发事件
emitter.emit('start')

2. Tapable Hooks类型

Tapable暴露的为插件提供挂载的Hook类,如下:

const {    
SyncHook,// 同步钩子
SyncBailHook,// 同步熔断钩子
SyncWaterfallHook,// 同步流水钩子
SyncLoopHook,// 同步循环钩子
AsyncParallelHook,// 异步并发钩子
AsyncParallelBailHook,// 异步并发熔断钩子
AsyncSeriesHook,// 异步串行钩子
AsyncSeriesBailHook,// 串行串行熔断钩子
AsyncSeriesWaterfallHook// 异步串行流水钩子
} =require("tapable");

这些Hook可以按以下进行分类:

Hook:所有钩子的后缀
Waterfall:同步方法,但是它会传值给下一个函数
Bail:熔断:当函数有任何返回值,就会在当前执行函数停止
Loop:监听函数返回true表示继续循环,返回undefined表示循环结束
Sync:同步方法
AsyncSeries:异步串行钩子
AsyncParallel:异步并行执行钩子

4. 构建流程

  1. 初始化参数

从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。  

  1. 开始编译

用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。

  1. 确定入口

根据配置中的 entry 找出所有的入口文件。

  1. 编译模块

从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。

  1. 完成模块编译

在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。

  1. 输出资源

根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。

  1. 输出完成

在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。  

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

5. 实现简易webpack

模拟webpack的Compiler与plugin

//tapable使用:模拟webpack的Compiler.js
const{SyncHook} =require("tapable");

module.exports =classCompiler{
constructor(){
this.hooks = {
// 1. 注册同步钩子
init:newSyncHook(['start']),
}
}
run(){
// 3. 触发钩子函数
this.hooks.init.call()
}
}

//模拟 plugin.js
classPlugin{
constructor(){}
apply(compiler){
// 2. 插件内监听钩子函数
compiler.hooks.init.tap('start',()=>{
console.log('compiler start')
})
}
}


// 模拟webpack.js
constoptions = {
plugins:[newPlugin()]
}

constcompiler =newCompiler()
for(constpluginofoptions.plugins){
if(typeofplugin==='function'){
plugin.call(compiler,compiler)
}else{
plugin.apply(compiler)
}
}

compiler.run()

6. 总结

至此学习了webpack的本质。

附:

plugin机制详解

Loader处理非JavaScript模块机制详解

前端知识体系总结输出文章目录汇总


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK