昨天追到 createServer
中的 Chokidar
這段,今天來繼續往下看,看能學到什麼新東西!
在 createServer 這個五百多行的 function 中,先大致看一下這方法中的結構方便分析:
這裡我們先來查查 Chokidar 這個套件的用途並做一下筆記:
- 主要用途是可以拿來做跨平台的檔案異動監聽器
- 唸法用中文的話可唸做「丘奇打」,原文是印度語中的守望者、監看者的意思
- 提供具體的
add
、change
、unlink
等檔案異動事件,比起 Node.js 原生 fs.watch / fs.watchFile 原生只有 rename
更好 - 廣泛被用在
webpack-dev-server
、tailwindcss
、nodemon
、Rsbuild
等 3000 多萬個套件中
冰與火之歌中的 Night's Watch 是不是也算是一種 Chokidar? 🤔 (圖片來源)
實際來啟一個專案玩玩看,程式碼也放在 GitHub 上:
新增 chokidar.js
與 src
資料夾,會像是這樣的檔案結構:
新增檔案內容:
此時可以用 node ./src/chokidar.js
執行檔案,並嘗試在 watch_dir
底下去新增、修改、刪除檔案或資料夾,可以看到對應的 log 被印出來,有趣的是可以觀察到改名時,行為會被判定為刪除 (unlink)並新增:
看過簡單範例後,我們可以理解 Chokidar 的用途,這裡獨立將「用 watcher 做檔案變化監聽」的部份單獨拉出來看:
先來看看這個 onFileAddUnlink
:
從新增、刪除、變更檔案時,看到都會觸發 onHMRUpdate
這個方法,追進去會看到 handleHMRUpdate
裡面有兩百多行,可能之後另外寫一篇直接順便研究一下 HMR 的原理。這裡還有另一個比較不懂的東西是 moduleGraph
,研究了下應該可以理解為這個模組圖會用來描述所有模組間的依賴關係,而在使用者在瀏覽器上訪問不同路徑頁面時,在前面提過的按需編譯有可能就與這個有關。
發現這整段應該都是在處理 HMR 的部分,因此統一都後面回頭再來看,這裡先專心找到 pre-bundling 的進入點。
循著 createServer
繼續往下找後會看到一堆的 middlewares.use
,會看到其中有一段 initingServer
,顧名思義這裡就是啟動 dev server 的邏輯所在,其中會到有一個 depsOptimizer
應該就是我們的主角:
這邊有區分 environments 往定義追進去會是在這個位置,在猜這應該是為了支援 SSR 的用途,我們這邊就關注 createDepsOptimizer
就好:
追到 createDepsOptimizer 後,會找到以下的位置與內容,裡面有 700 多行,這個有點硬但可以先從下面這樣關鍵的結構下手,以下有簡化過原始碼內容方便閱讀:
這裡可以看到比較關鍵的地方在 init
這個方法,這就是前面 environment.depsOptimizer?.init()
的初始化函式,從裡面會看到這一段:
從 init
這個初始化函式可以看到有三個關鍵函式:
loadCachedDepOptimizationMetadata
:這裡面會根據目前的 metadata 與 hash 來判斷快取資料是否需要重新 pre-bundlediscoverProjectDependencies
:當前面的快取不存在時,進到這段邏輯來開始做 pre-bundling,而這個函式看起來就是在掃描整個專案中用到的依賴套件runOptimizeDeps
:可能就是 esbuild 的執行點
今天先追到這,下一篇會繼續把 pre-bundling 的這三個函式追完應該就差不多能看見其中的運作原理了,明天會再針對目前追過的原始碼做個總整理。
從昨天到今天一路從啟 dev server,追到實際偵測檔案異動的 Chokidar,並實際嘗試了一下這個套件怎麼用;最後再一路追到 pre-bundling 的三個關鍵函式,文字版的整理可以先這樣理解:
- 啟 dev server
cli.alias('dev')
- →
createServer
- → 用
connect
套件建立 middlewares - → 建立 WebSocket server
- → 用
Chokidar
套件來做為 watcher 監聽檔案變化 (HMR 主要邏輯所在)
- pre-bundling 的進入點
createServer
→ initServer
- →
environment.depsOptimizer?.init()
- 其中的
depsOptimizer
中有兩種環境與設定條件:- 一個是
createExplicitDepsOptimizer
- 一個是
createDepsOptimizer
- 前置作業
- →
createDepsOptimizer
- →
loadCachedDepOptimizationMetadata
- →
discoverProjectDependencies
- →
scanImports
- 專案中用了哪些第三方套件,有的話會補上 flattenId
- →
prepareEsbuildOptimizerRun
先整理如上,明天全部追完後再來改成圖片版,若有看不懂或錯誤的地方再麻煩留言一起討論,感謝你的閱讀,那我們就明天見了!