21

看完Webpack源码,我学到了这些(苏溪云的博客)

 4 years ago
source link: https://terry-su.github.io/cn/the-tour-of-travelling-webpack-internals/?
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.
neoserver,ios ssh client

看完Webpack源码,我学到了这些(苏溪云的博客) 看完Webpack源码,我学到了这些

72396026-56ca7980-3776-11ea-8711-abf38176d1fe.png

继React,Vue,这是第三个着重阅读源码的前端项目-Webpack。本文主要以:

  • WHY: 为何要看Webpack源码
  • HOW: 如何阅读Webpack源码
  • WHAT: 看完源码后学到了什么

三个方向展开。

诚然Webpack这是一个前端工程化工具,理解容易, 使用简单,似乎没有深入研究的必要。那为什么还要费心费力阅读其源码?这,把正在写此篇文章的我也问住了。理提纲时,认为WHY最好写,几句话就可带过,但事实证明真要较真这一块还值得一说。
擅自揣测下会阅读Webpack源码伙伴可能的动机:
1. 丰富工作经验
2. 技术真爱粉,知其然亦须知其所以然,同时学习方法
3. 与工作或个人项目相关,参考学习
4. 看有人写相关文章,也看看了解下

作者最先是原因是4,然后是1,2。当然,1,2应该是大多数人看项目源码的动机。

搭建源码调试环境

要阅读源码,首先拿到源码,然后最后能边调试边阅读。当然,如果智力和推理能力惊人,大可以直接在Github上在线阅读。 有2种方法下载源码。一种是最常见的git clone,将Github上webpack项目clone到本地,pull后与webpack官方最新代码保持一致,一劳永逸。不过作者尝试第一种方法时,总是clone不下来,很大可能是由于webpack源文件过大且github服务器clone一直很慢。 于是退而求其次,使用第二种方法:下载Webpack源码release版本。选择一个打算阅读的webpack源码版本,直接下载"Source code(zip)"即可。速度非常快,因为不包含.git。

image

IDE作者使用VSCode,调试node很方便。拿到源码后,在目录新建一个文件夹,写一个简单的webpack案例,然后使用VSCode进行调试。
不过,在实际操作中,直接使用下载源码中的webpack.js调试可能会出现报错Cannot find module 'json-parse-better-errors'Cannot find module 'webpack/lib/RequestShortener',只需运行npm install webpack webpack-cli --save-dev,即可解决报错,且不影响调试源码。

image

此附作者在调试时使用版本参考,下载后使用VSCode打开webpack-4.41.4(modified),安装依赖,安装webpack和webpack-cli,按F5即可启动调试。

调试,理清大致脉路走向

Webpack源码量庞大,把每一行代码都读懂确实没有必要,但是我们至少要知道它的整体运行流程,知道它反复用到的核心代码,以及各个模块的生命周期如何运转。

找核心功能源码

代码量大,想要在走整体流程时恰好找核心功能的源码,困难重重,至少对于webpack源码是这样,因为其独特的插件和回调结构。 不过,我们可以根据每一个想要了解的核心功能,单独去寻找和阅读相关源码。比如,如果我们想看webpack如何打包生成bundle.js,可通过webpack一定会调用NodeJS文件系统输出文件方法,全局搜索"writeFile"找到相关代码,或通过bundle.js中的关键字"// Check if module is in cache"进行搜索。

image

通过边调试边阅读代码,了解代码整体走向以及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运行流程

4

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从而生成打包代码。

  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的主要运转流程以及了解较为感兴趣的几个模块。所以其他模块原理以后有机再加入此文。对相应模块模块感兴趣的伙伴可网上先自行搜索相关内容。

阅读源码资源推荐

感谢你的阅读。欢迎通过微信(扫描下方二维码)或Github订阅我的博客。

微信公众号:苏溪云的博客
发布时间: 1/15/2020
分类: Technology/FrontEnd/Webpack
作者版权所有,转载请注明出处,禁止商业转载

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK