看完Webpack源碼,我學到了這些

繼React,Vue,這是第三個着重閱讀源碼的前端項目-Webpack。本文主要以:前端

  • WHY: 爲什麼要看Webpack源碼
  • HOW: 如何閱讀Webpack源碼
  • WHAT: 看完源碼後學到了什麼

三個方向展開。node

歡迎Star和訂閱 個人博客

WHY

誠然Webpack這是一個前端工程化工具,理解容易, 使用簡單,彷佛沒有深刻研究的必要。那爲何還要費心費力閱讀其源碼?這,把正在寫此篇文章的我也問住了。理提綱時,認爲WHY最好寫,幾句話就可帶過,但事實證實真要較真這一塊還值得一說。
擅自揣測下會閱讀Webpack源碼夥伴可能的動機:react

  1. 豐富工做經驗
  2. 技術真愛粉,知其然亦須知其因此然,同時學習方法
  3. 與工做或我的項目相關,參考學習
  4. 看有人寫相關文章,也看看了解下

做者最早是緣由是4,而後是1,2。固然,1,2應該是大多數人看項目源碼的動機。webpack

How

搭建源碼調試環境

要閱讀源碼,首先拿到源碼,而後最後能邊調試邊閱讀。固然,若是智力和推理能力驚人,大能夠直接在Github上在線閱讀。
有2中方法下載源碼。一種是最多見的git clone,將Github上webpack項目clone到本地,pull後與webpack官方最新代碼保持一致,一勞永逸。不過做者嘗試第一種方法時,老是clone不下來,很大多是因爲webpack源文件過大且github服務器clone一直很慢。
因而退而求其次,使用第二種方法:下載Webpack源碼release版本。選擇一個打算閱讀的webpack源碼版本,直接下載"Source code(zip)"便可。速度很是快,由於不包含.git。
imagegit

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,便可解決報錯,且不影響調試源碼。github

image

此附做者在調試時使用版本參考,下載後使用VSCode打開webpack-4.41.4(modified),安裝依賴,安裝webpack和webpack-cli,按F5便可啓動調試。web

調試,理清大體脈路走向

Webpack源碼量龐大,把每一行代碼都讀懂確實沒有必要,可是咱們至少要知道它的總體運行流程,知道它反覆用到的核心代碼,以及各個模塊的生命週期如何運轉。npm

找核心功能源碼

代碼量大,想要在走總體流程時剛好找核心功能的源碼,困難重重,至少對於webpack源碼是這樣,由於其獨特的插件和回調結構。
不過,咱們能夠根據每個想要了解的核心功能,單獨去尋找和閱讀相關源碼。好比,若是咱們想看webpack如何打包生成bundle.js,可經過webpack必定會調用NodeJS文件系統輸出文件方法,全局搜索"writeFile"找到相關代碼,或經過bundle.js中的關鍵字"// Check if module is in cache"進行搜索。json

image

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運行流程

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的主要運轉流程以及瞭解較爲感興趣的幾個模塊。因此其餘模塊原理之後有機再加入此文。對相應模塊模塊感興趣的夥伴可網上先自行搜索相關內容。

閱讀源碼資源推薦

感謝你花時間閱讀這篇文章。若是你喜歡這篇文章,歡迎點贊、收藏和分享,讓更多的人看到這篇文章,這也是對我最大的鼓勵和支持!
歡迎Star和訂閱個人原創前端技術博客

相關文章
相關標籤/搜索