![6b0f6c4c6ccbde2d2e4f021a174b019c.png](https://i-blog.csdnimg.cn/blog_migrate/ca3d3ed59c164546d79dc7c6e89922a9.jpeg)
继 React,Vue,这是第三个着重阅读源码的前端项目-Webpack。本文主要以:
- WHY: 为何要看Webpack源码
- HOW: 如何阅读Webpack源码
- WHAT: 看完源码后学到了什么
No.1
WHY
诚然 Webpack 这是一个前端工程化工具,理解容易, 使用简单,似乎没有深入研究的必要。那为什么还要费心费力阅读其源码?这,把正在写此篇文章的我也问住了。理提纲时,认为 WHY 最好写,几句话就可带过,但事实证明真要较真这一块还值得一说。擅自揣测下会阅读 Webpack 源码伙伴可能的动机:
- 丰富工作经验
- 技术真爱粉,知其然亦须知其所以然,同时学习方法
- 与工作或个人项目相关,参考学习
- 看有人写相关文章,也看看了解下
No.2
HOW
搭建源码调试环境
要阅读源码,首先拿到源码,然后最后能边调试边阅读。当然,如果智力和推理能力惊人,大可以直接在 Github 上在线阅读。有 2 种方法下载源码。一种是最常见的 git clone,将 Github 上 webpack 项目 clone 到本地,pull 后与 webpack 官方最新代码保持一致,一劳永逸。不过作者尝试第一种方法时,总是 clone 不下来,很大可能是由于 webpack 源文件过大且 github 服务器 clone 一直很慢。
于是退而求其次,使用第二种方法:下载 Webpack 源码 release 版本。选择一个打算阅读的 webpack 源码版本,直接下载"Source code (zip) "即可。速度非常快,因为不包含 .git。
![d5c74917bd74b5d8359e4942a585b3d1.png](https://i-blog.csdnimg.cn/blog_migrate/b9055012c5a0c26b236491c58a3266dc.png)
不过,在实际操作中,直接使用下载源码中的 webpack.js 调试可能会出现报错 Cannot find module 'json-parse-better-errors' 或 Cannot find module 'webpack/lib/RequestShortener' ,只需运行 npm install webpack webpack-cli --save-dev ,即可解决报错,且不影响调试源码。
![b8e8f6a78a0aa832e0f372eaa6a3ae72.png](https://i-blog.csdnimg.cn/blog_migrate/d48517d0c5c4fc187a07e905cbb719ee.jpeg)
使用版本:https://github.com/Terry-Su/learn-webpack/archive/0.0.1.zip
No.3
调试,理清大致脉路走向
Webpack 源码量庞大,把每一行代码都读懂确实没有必要,但是我们至少要知道它的整体运行流程,知道它反复用到的核心代码,以及各个模块的生命周期如何运转。找核心功能源码
代码量大,想要在走整体流程时恰好找核心功能的源码,困难重重,至少对于 webpack 源码是这样,因为其独特的插件和回调结构。不过,我们可以根据每一个想要了解的核心功能,单独去寻找和阅读相关源码。比如,如果我们想看 webpack 如何打包生成 bundle.js,可通过 webpack 一定会调用 NodeJS 文件系统输出文件方法,全局搜索"writeFile"找到相关代码,或通过 bundle.js 中的关键字"// Check if module is in cache"进行搜索。
![817981309f99473b0886980bcc67e817.png](https://i-blog.csdnimg.cn/blog_migrate/3e499e4be74e3f06ad7c6c2e8816ce7c.png)
No.4
WHAT
通过边调试边阅读代码,了解代码整体走向以及webpack如何打包生成 bundle.js,作者学到了以下内容:- tapable 插件机制
- 简化版 Webpack 运行流程
- bundle.js 内容如何生成
Tapable
Tapable 在源码中应用随处可见,要了解源码,首先得学习 tapable 机制。其实它并不复杂,并且我们只需要知道它的基本作用和用法即可。Tapable 可理解为一套钩子回调函数机制,每一个钩子可订阅多个函数,发布钩子时会运行该钩子订阅该的多个函数。
用一个简单案例说明。
const { SyncHook } = require('tapable')
class Car {
constructor() {
this.hooks = {
// # 添加一个钩子
start: new SyncHook()
}
}
}
const car = new Car()
// start钩子订阅一个函数
car.hooks.start.tap( 'run slowly', () => console.log('start running slowly') )
// start钩子订阅另一个函数
car.hooks.start.tap( 'run mediumly', () => console.log('start running mediumly') )
// 发布钩子
car.hooks.start.call() // 输出: run slowly run mediumly
简化版 Webpack 运行流程
![1dcb41fb2614ac7a0b93c70811d025ca.png](https://i-blog.csdnimg.cn/blog_migrate/49eb086607404ed63f9268180cdfae19.png)
bundle.js 内容如何生成
未压缩的 bundle.js 文件结构一般如下:
/******/ (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: {}
/******/ };
....
那么 Webpack 如何生成这些内容?
其实 Webpack 对于内容分两步处理,第一步先通过 loader (默认为 babel-loader) 生成组合 JS 代码。第二步将组合 JS 代码放入 webpack 默认函数中,从而避免变量泄露。
如打包前: foo.js
export const foo = () => 'hello foo!'
bar.js
import { foo } from './foo.js'
foo()
console.log( 'hello bar!' )
打包第一步,通过 loader
(默认为 babel-loader)
生成组合 JS 代码:
...
const foo = () => 'hello foo!'
...
\r\n__WEBPACK_MODULE_REFERENCE__0_666f6f_call__()\r\nconsole.log( 'hello bar!' )
...
打包第二步,组合 JS 代码放入 webpack 默认函数中。
/******/ (function(modules) { // webpackBootstrap\n
...
const foo = () => 'hello foo!'
...
foo()
console.log( 'hello bar!' )
...
值得注意的是,核心文件为
ConcatenatedModule.js
, 通过遍历
modulesWithInfo
从而生成打包代码。
No.5
常见问题
1. runtime 是什么? 不管在 webpack 源码,还是 Vue 源码和其他地方,runtime 经常出现。runtime 究竟是什么?经过反复查阅资料和推敲,runtime 代码可以理解为编译后生成的代码。比如,对于 React,runtime 代码就是编译 JSX 代码后生成的 JS 代码。对于 Vue,runtime 代码则是编译 template,script ,style 后生成的 JS 代码。 2. 热更新,Code Splitting, Tree-shaking 等是如何实现? Webpack 内容较多,核心模块原理也不少,比如 loader 如何运转,Code Splitting 如何实现,Tree-Shaking 和热加载又是怎么做到的。但毕竟时间有限,此次阅读源码的目标不是大而全的弄懂所有内容,而是掌握 Webpack 的主要运转流程以及了解较为感兴趣的几个模块。所以其他模块原理以后有机再加入此文。对相应模块模块感兴趣的伙伴可网上先自行搜索相关内容。
阅读源码资源推荐
how-react-works.pdf: https://raw.githubusercontent.com/sokra/slides/master/data/how-webpack-works.pdf 深入浅出 webpack-吴浩麟: https://webpack.wuhaolin.cn/ https://github.com/TheLarkInn/artsy-webpack-tour build your own webpack: https://www.youtube.com/watch?v=Gc9-7PBqOC8
- END -
![5b6c4c0fb4e590642bd8760c6c297dd6.png](https://i-blog.csdnimg.cn/blog_migrate/961bba3714bf8d848c45dd8e9bd366a2.jpeg)