繼React,Vue,這是第三個着重閱讀源碼的前端項目-Webpack。本文主要以:前端
三個方向展開。node
歡迎Star和訂閱 個人博客。
誠然Webpack這是一個前端工程化工具,理解容易, 使用簡單,彷佛沒有深刻研究的必要。那爲何還要費心費力閱讀其源碼?這,把正在寫此篇文章的我也問住了。理提綱時,認爲WHY最好寫,幾句話就可帶過,但事實證實真要較真這一塊還值得一說。
擅自揣測下會閱讀Webpack源碼夥伴可能的動機:react
做者最早是緣由是4,而後是1,2。固然,1,2應該是大多數人看項目源碼的動機。webpack
要閱讀源碼,首先拿到源碼,而後最後能邊調試邊閱讀。固然,若是智力和推理能力驚人,大能夠直接在Github上在線閱讀。
有2中方法下載源碼。一種是最多見的git clone,將Github上webpack項目clone到本地,pull後與webpack官方最新代碼保持一致,一勞永逸。不過做者嘗試第一種方法時,老是clone不下來,很大多是因爲webpack源文件過大且github服務器clone一直很慢。
因而退而求其次,使用第二種方法:下載Webpack源碼release版本。選擇一個打算閱讀的webpack源碼版本,直接下載"Source code(zip)"便可。速度很是快,由於不包含.git。
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
,便可解決報錯,且不影響調試源碼。github
此附做者在調試時使用版本參考,下載後使用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
經過邊調試邊閱讀代碼,瞭解代碼總體走向以及webpack如何打包生成bundle.js,做者學到了如下內容:前端工程化
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
未壓縮的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和訂閱個人原創前端技術博客。