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

2021-03-02 SegmentFault
繼 React,Vue,這是第三個著重閱讀源碼的前端項目-Webpack。本文主要以:
誠然 Webpack 這是一個前端工程化工具,理解容易, 使用簡單,似乎沒有深入研究的必要。那為什麼還要費心費力閱讀其源碼?這,把正在寫此篇文章的我也問住了。理提綱時,認為 WHY 最好寫,幾句話就可帶過,但事實證明真要較真這一塊還值得一說。
擅自揣測下會閱讀 Webpack 源碼夥伴可能的動機:作者最先是原因是 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。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,即可解決報錯,且不影響調試源碼。此附作者在調試時使用版本參考,下載後使用 VSCode 打開 webpack-4.41.4(modified),安裝依賴,安裝 webpack 和 webpack-cli,按 F5 即可啟動調試。https://github.com/Terry-Su/learn-webpack/archive/0.0.1.zipWebpack 源碼量龐大,把每一行代碼都讀懂確實沒有必要,但是我們至少要知道它的整體運行流程,知道它反覆用到的核心代碼,以及各個模塊的生命周期如何運轉。找核心功能源碼代碼量大,想要在走整體流程時恰好找核心功能的源碼,困難重重,至少對於 webpack 源碼是這樣,因為其獨特的插件和回調結構。
不過,我們可以根據每一個想要了解的核心功能,單獨去尋找和閱讀相關源碼。比如,如果我們想看 webpack 如何打包生成 bundle.js,可通過 webpack 一定會調用 NodeJS 文件系統輸出文件方法,全局搜索"writeFile"找到相關代碼,或通過 bundle.js 中的關鍵字"// Check if module is in cache"進行搜索。
通過邊調試邊閱讀代碼,了解代碼整體走向以及webpack如何打包生成 bundle.js,作者學到了以下內容:
TapableTapable 在源碼中應用隨處可見,要了解源碼,首先得學習 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 運行流程
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 對於內容分兩步處理,第一步先通過 loader(默認為 babel-loader)生成組合 JS 代碼。第二步將組合 JS 代碼放入 webpack 默認函數中,從而避免變量洩露。

export const foo = () => 'hello foo!'

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 從而生成打包代碼。不管在 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 的主要運轉流程以及了解較為感興趣的幾個模塊。所以其他模塊原理以後有機再加入此文。對相應模塊模塊感興趣的夥伴可網上先自行搜索相關內容。閱讀源碼資源推薦https://raw.githubusercontent.com/sokra/slides/master/data/how-webpack-works.pdfhttps://webpack.wuhaolin.cn/https://github.com/TheLarkInn/artsy-webpack-tourhttps://www.youtube.com/watch?v=Gc9-7PBqOC8點擊左下角閱讀原文,歡迎到 SegmentFault 思否社區 和文章作者展開更多互動和交流。

相關焦點

  • webpack的幾個常見loader源碼淺析,以及動手實現一個md2html-loader
    前言本文會帶你簡單的認識一下webpack的loader,動手實現一個利用md轉成抽象語法樹,再轉成html字符串的loader。順便簡單的了解一下幾個style-loader,vue-loader,babel-loader的源碼以及工作流程。
  • 程式設計師是如何閱讀源碼的
    但是這種大型項目的構建流程較為複雜,如果只是想簡單了解源碼,不需要去了解這些複雜的東西。這裡教大家一個簡單的方案,直接到 CDN 上下載官方編譯好了的開發版源碼(https://cdn.jsdelivr.net/npm/react@17.0.1/umd/react.development.js),中間的版本號可以替換成任何想看的版本。
  • 【Webpack】654- 了不起的 Webpack Scope Hoisting 學習指南
    在 JavaScript 中,還有「變量提升」和「函數提升」,JavaScript 會將變量和函數的聲明提升到當前作用域頂部,而「作用域提升」也類似,webpack 將引入到 JS 文件「提升到」它的引入者的頂部。
  • 記 webpack 中文文檔的一次優化
    ❝webpack 主文檔的翻譯和遷移工作基本接近尾聲,感謝社區小夥伴的參與,才能讓文檔的翻譯工作進行得如此迅速。這裡重點感謝 馮博 和 黃錦華 的積極參與與付出。❞最近,有位細心的小夥伴在瀏覽文檔時發現了錨點跳轉異常的問題,這段時間我針對此問題著手進行了調研和解決。
  • Webpack 3 從入門到放棄
    再次,即使是基本的功能,也內容繁多,我儘可能地解釋通俗易懂,將我學習過程中的疑惑和坑一一解釋,如有紕漏,敬請雅正。再次,為了清晰有效地講解,我會演示從零編寫 demo,只要一步步跟著做,就會清晰許多。最後,官方文檔也是個坑爹貨!Webpack,何許人也?借用官方的說法:webpack is a module bundler.
  • 【webpack】webpack 中最易混淆的 5 個知識點
    但是最近看了一下 webpack4 的文檔,發現 webpack官網的 指南[1] 寫的還不錯,跟著這份指南學會 webpack4 基礎配置完全不是問題,想系統學習 webpack 的朋友可以看一下。今天我主要分享的是一些 webpack 中的易混淆知識點,也是面試的常見內容。我把這些分散在文檔和教程裡的內容總結起來,目前看是全網獨一份,大家可以加個收藏,方便以後檢索和學習。
  • Webpack打包全世界
    核心和概念本質上,webpack 是一個現代 JavaScript 應用程式的靜態模塊打包器(module bundler)。當 webpack 處理應用程式時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程式需要的每個模塊,然後將所有這些模塊打包成一個或多個 bundle。
  • webpack系列---loader
    這個處理結果應該是 String 或者 Buffer(被轉換為一個 string),代表了模塊的 JavaScript 源碼。另外還可以傳遞一個可選的 SourceMap 結果(格式為 JSON 對象)。如果是單個處理結果,可以在同步模式中直接返回。如果有多個處理結果,則必須調用 this.callback()。
  • Webpack的使用指南-Webpack的常用解決方案
    而use數組代表用哪些loader去處理這些匹配到的文件。此時再運行webpack,打包後的文件bundle.js就包含了css代碼。其中css-loader負責加載css,打包css到js中。而style-loader負責生成:在js運行時,將css代碼通過style標籤注入到dom中。
  • 重學webpack4之基礎篇
    前言重學Webpack4專題來了,本文有一種代碼片段的既視感。本專題有字節跳動@xfz投稿分享。
  • 如何編寫一個 Webpack Plugin
    最後執行 Compiler 的 emitAssets 方法把生成的文件輸出到 output 的目錄中。Plugin 作用按我的理解,Webpack 插件的作用就是在 webpack 運行到某個時刻的時候,幫我們做一些事情。
  • 擼一個webpack插件!!
    而將這些插件控制在webapck事件流上的運行的就是webpack自己寫的基礎類Tapable。Tapable暴露出掛載plugin的方法,使我們能將插件控制在webapack事件流上運行(如下圖)。後面我們將看到核心的對象 Compiler,Compilation等都是繼承於Tabable類。Tabable是什麼?
  • Webpack 多入口配置,看這篇,足夠了
    由於不同入口下的路由頁面有一些是重複的,因此我考慮使用 Webpack 多入口配置來解決這個需求。再一次,在網上找的不少文章都不合我的需求,很多文章都是只簡單介紹了生產環境下配置,沒有介紹開發環境下的配置,有的也沒有將多入口結合 vue-router、 vuex、 ElementUI 等進行配置,因此在下通過不斷探坑,然後將思路和配置過程記錄下來,留給自己作為筆記,同時也分享給大家,希望可以幫助到有同樣需求的同學們~1.
  • Webpack vs Rollup
    先別急,等你看完這篇文章,你就知道為什麼了。一、Webpack誕生於2012年,目前Javascript社區使用得比較多的構建工具。它的出現,解決了當時的構建工具不能處理的問題——構建複雜的單頁面應用(SPA)。它是一個強力的模塊打包器。
  • 前端必備技能 webpack - 4. webpack處理CSS資源
    每篇文章純屬個人經驗觀點,如有錯誤疏漏歡迎指正  因為 webpack 本身只具有識別 JS 的能力,所以涉及到其他資源
  • webpack教程:如何從頭開始設置 webpack 5
    webpack 對我來說曾經是一個怪物般存在一樣,因為它有太多太多的配置項,相反,使用像create-react-app腳手架可以很輕鬆創建項目,所以有一段時間內,我會儘量避免使用 webpack,因為它看起來既複雜又望而卻步 😊如果你們不習慣從頭開始設置 webpack
  • 從Webpack打包後的文件分析導入的原理
    installedModules:緩存已經加載過的 module,簡單理解就是已經運行了源碼中 import a from 'xxx' 這樣的語句。installedModules 是一個 object, key 為 module id,value 為對應 module 導出的變量。
  • webpack基本配置有哪些?如何搭建webpack?
    主要內容webpack學習目標第一節 webpack介紹1.webpack介紹webpack 是一個模塊打包器。webpack 的主要目標是將 JavaScript 文件打包在一起,打包後的文件用於在瀏覽器中使用,但它也能夠勝任轉換(transform)、打包(bundle)。
  • 為什麼我說你應該學React源碼?
    但如果把公司範圍縮小到大廠,或者把範圍擴展到全球,那React無疑獨佔鰲頭。↑上圖來自《2019年度JavaScript現狀調查報告》↑可以說,如果你想進大廠,必須搞定 React,並且是從原理層面搞定 React。以下是我從阿里,字節,滴滴等大廠面經中摘取的React相關面試題:React 中的 setState 是同步還是異步React、Vue /Angular 的區別?
  • 你必須知道的 webpack 插件原理分析
    webpack 自身只支持 js 和 json 這兩種格式的文件,對於其他文件需要通過 loader 將其轉換為 commonJS 規範的文件後,webpack 才能解析到。plugin 是一個擴展器,它豐富了 webpack 本身,針對是 loader 結束後,webpack 打包的整個過程,它並不直接操作文件,而是基於事件機制工作,會監聽 webpack 打包過程中的某些節點,執行廣泛的任務。