這系列命名為探索網頁前端工具的前世今生,在歷經前面的「JS 模組化歷史」、「Webpack 誕生」、「ESM 標準統一」、「Vite 如何實現更好的開發體驗」這些主題後,今天也終於要進到重頭戲的 Rust 的戰國時代,來學習這些新生代的 Rust 工具該怎麼用以及探索它們為什麼可以這麼快。
進到了 Rust 的章節,這幾天反覆思考對於一個完全沒寫過 Rust 的網頁開發者如我來說,怎麼樣才是適合的學習方式。稍微整理了下覺得可以先問問自己這些問題。
就像學習 JavaScript 一樣,一定也是先學會剪剪貼貼做出一個網頁,才回頭理解基礎語法,再逐漸擴大到大型專案並去理解一些語言的眉角,真的對某個工具的問題有興趣才去鑽原始碼,或許這個「先知道怎麼用再學原理」是個不錯的方式。
響應上面越級打怪的學習方式,今天我就試著以終為始先試著了解自己的目標在哪裡吧,直接先在不會 Rust 的狀況下來追追看 Rolldown 的原始碼。
以成熟度來說 Rspack 應該更完整,而且現在也能開箱即用,而選擇 Rolldown 的原因是因為目前開發階段相對早期,在想可能程式碼看起來更單純。況且前面都在研究 Vite,直接一脈相承繼續研究下來可能更有發 PR 的機會。
不免俗地一樣先把 Rolldown 給載下來:
$ git clone https://github.com/rolldown/rolldown.git --depth=3
有了前面追 Vite 原始碼的經驗,現在可以更熟悉地知道直接往其中的 packages/rolldown 追進去,稍微瀏覽了這個資料夾底下會發現幾乎都是 TypeScript 檔案,跟前面在看 Vite 沒兩樣,但看 GitHub 上說明的原始碼組成這個 codebase 裡應該有很多 Rust 才對,所以代表這個地方只是最上層的應用層:
這時試著在 VS Code 去搜尋檔案,會看到 Rust 檔案幾乎都放在一個叫做 crates 的資料夾下:
這裡我就感到困惑了,如果說 package/rolldown
裡放的是上層應用的邏輯,而這個 crates
裡可能指的是用 Rust 寫出來的一些模組或 plugin 讓上層去引用,但上層又是怎麼去引用的呢?
沒有頭緒下稍微問一下 Cursor 的 inline AI chat:
第一個 prompt 下得不好,它有提到 crates
底下的結構,但沒有解答我的疑惑,再試一次:
看起來這就是我們要的了!
💡Cursor 是一套基於 VS Code 的 AI editor,對細節有興趣歡迎參考我之前寫過的文章 —— 《Cursor:下一代開發者的 AI 武器》
回到原本的 packages/rolldown/package.json 裡可以看到一些端倪:
"scripts": {
"# Scrips for binding #": "_",
"build-binding": "napi build -o=./src --manifest-path ../../crates/rolldown_binding/Cargo.toml --platform -p rolldown_binding --js binding.js --dts binding.d.ts --no-const-enum",
"...": "..."
}
"devDependencies": {
"@napi-rs/cli": "^3.0.0-alpha.60",
"@napi-rs/wasm-runtime": "^0.2.4",
"@rolldown/testing": "workspace:*",
"rolldown": "workspace:*",
"...": "..."
},
"optionalDependencies": {
"@rolldown/binding-darwin-arm64": "workspace:*",
"@rolldown/binding-darwin-x64": "workspace:*",
"@rolldown/binding-freebsd-x64": "workspace:*",
"@rolldown/binding-linux-arm-gnueabihf": "workspace:*",
"@rolldown/binding-linux-arm64-gnu": "workspace:*",
"@rolldown/binding-linux-arm64-musl": "workspace:*",
"@rolldown/binding-linux-x64-gnu": "workspace:*",
"@rolldown/binding-linux-x64-musl": "workspace:*",
"@rolldown/binding-wasm32-wasi": "workspace:*",
"@rolldown/binding-win32-arm64-msvc": "workspace:*",
"@rolldown/binding-win32-ia32-msvc": "workspace:*",
"@rolldown/binding-win32-x64-msvc": "workspace:*"
}
這裡看到有個叫 napi
的工具在幫忙在執行指令 build-bindling
後做一些 Node.js 與 Rust 之間綁定的工作。另外在其中安裝的套件中有許多是參考自 workspace:*
裡,這應該是利用 pnpm workspace 在做 monorepo 管理的方式,也就是說它會直接參考到目前這個 codebase 底下其他 package 中打包出來的內容。
接著讓我們來看看 napi 這個東西是什麼。
這裡如果去搜尋 napi
的話,可以找到它的用途看起來是可以把原本 Node.js 裡的邏輯抽成模組,並用 Rust 來改寫,最後可以透過這個套件來做橋接,達到將原本 Node.js 專案漸進式提升效能的作用。如果去看到前面在 AI chat 中有提到的 crates/rolldown_binding,可以看到其中有用上 napi
來做 build code 的操作:
fn main() {
use napi_build::setup;
setup();
}
找到 Rust 的原始碼位置與橋接方式後,雖然還沒能看懂 Rust 但稍微去瀏覽下像是 crates/rolldown/src/bundler.rs 或其他比較複雜的檔案,可以看到一些熟悉的語法像是 let
用來宣告變數、fn
用來宣告函式、use
應該是載入模組的方式、也有 async/await
、if
、for
,和一些類似 TypeScript 的型別定義與泛型等等。
但其中也看到許多不熟悉的語法像是 mut
、pub
、impl
、&mut
、match
、vec
等,而且怎麼好像看到了許多類似 reference 的 &
語法,看來可以預期這些東西大概就是學習曲線會拉高的一個坎,截取其中一小段看個感覺:
期待這系列結束時可以看懂一部份這在寫什麼。
今天開始進入了 Rust 的章節,在搜集了許多資料並實際嘗試後,也全盤地規劃出後續章節的學習地圖,並嘗試一改以往學習新語言時從基本語法開始練起的方式,直接越級打怪追 Rolldown 的原始碼,先看看要打的怪是什麼,之後要學習時才知道哪邊要多注意去弄懂。其中也示範了下我自己平常如何搭配著 AI 工具來學習,對於學全新不熟悉的內容用這些工具可以說是如虎添翼。
下一篇我們將回到階段一從入門的方式,先學會這些 Rust 工具怎麼用,再來探討其中的原理,我們明天見!