写点什么

webpack | 谈谈 webpack 的本质

用户头像
梁龙先森
关注
发布于: 2021 年 01 月 30 日
webpack | 谈谈webpack的本质

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中的事件机制const EventEmitter = require('events');const emitter = new EventEmitter();// 监听事件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.jsconst {SyncHook} = require("tapable");
module.exports = class Compiler{ constructor(){ this.hooks = { // 1. 注册同步钩子 init:new SyncHook(['start']), } } run(){ // 3. 触发钩子函数 this.hooks.init.call() }}
//模拟 plugin.jsclass Plugin{ constructor(){} apply(compiler){ // 2. 插件内监听钩子函数 compiler.hooks.init.tap('start',()=>{ console.log('compiler start') }) }}

// 模拟webpack.jsconst options = { plugins:[new Plugin()]}
const compiler = new Compiler()for(const plugin of options.plugins){ if(typeof plugin==='function'){ plugin.call(compiler,compiler) }else{ plugin.apply(compiler) }}
compiler.run()
复制代码

6. 总结

至此学习了 webpack 的本质。

附:

浅谈Webpack的本质

Loader处理非JavaScript模块详解

Plugin事件流机制详解

浅谈Webpack原理,以及loader和plugin实现

wepback进阶用法1:多入口构建/资源内联/脚本分离等

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

webpack进阶用法2:代码分割和动态引入的实现方式

webpack进阶用法3:如果将代码打包成一个通用JS库

webpack进阶用法4:如何进行构建速度和体积分析

webpack如何提示构建速度,进行体积优化?

webpack提升构建速度和体积优化的N种方式

使用Travis CI为工程搭建一个持续集成服务


发布于: 2021 年 01 月 30 日阅读数: 2552
用户头像

梁龙先森

关注

脚踏V8引擎的无情写作机器 2018.03.17 加入

还未添加个人简介

评论

发布
暂无评论
webpack | 谈谈webpack的本质