avatar
threadsinstagram

來實驗看看 Next.js、Remix 專案遷移到 Rspack 可行嗎?

Table of Contents

前言

剛好前陣子有位讀者在 IG 上私訊詢問「有在 Next.js 中將 bundler 成功替換成 Rspack,但不知道能不能在 Remix 上用上 Rspack?」

雖然我沒用過 Rspack 跟 Remix,但我也有點好奇 Rspack 是可以被用在這兩個 meta-framework 上的嗎,剛好趁這個機會來做個實驗試試看。順便比較一下 Rspack 到底有多快。

TL;DR:實驗沒有成功,Next.js、Remix 都有自己針對 CLI 做客製化,所以要遷移相對較困難,除非社群中有人開發出 plugin 來橋接。

實驗一:將 Next.js 專案遷移到 Rspack 可行嗎?快多少?

建立一個範例專案

這裡參考官網的 template 中挑一個比較偏靜態網站且依賴比較複雜的部落格網站模板,快速 clone 出一個 Next.js 專案:

$ mkdir hello-rspack-nextjs-blog
$ cd hello-rspack-nextjs-blog
$ pnpx degit 'timlrx/tailwind-nextjs-starter-blog'
$ yarn
$ pnpm install

前面沒特別提我習慣使用 pnpm 體系的套件管理工具,算是比較快且省空間版本的 npm,但如果有自己習慣的套件管理工具,也可以將指令換成 npmyarn,甚至更新潮的 bun,但我自己的經驗有時候用 bun 暫時會有一些兼容性問題比較多只能在 dev 開發用。

而剛好這個模板中有限制套件管理工具是用 yarn,但安裝實在太慢所以我有調整成 pnpm,如果照著上面手動安裝的讀者,在執行 pnpm install 前也會需要手動調整一下 package.json 中的這段:

- "packageManager": "yarn@3.6.1"
+ "packageManager": "pnpm@9.12.0"

接著嘗試把專案 serve 起來確認模板能正常使用:

$ pnpm run build
$ pnpm run serve

理想上等待 build 完後再 host 起來應該可以看到這樣的畫面,但難保模板有更新導致未預期錯誤,如果有的話可以再留言回報給我:

nextjs-blog

degit 是什麼

另外小小岔個題,上面在下載專案時,這裡我也好奇模板文件上這個 degit 是什麼,查了一下發現原來是個酷東西。

簡單說的話它可以直接在抓最新版的 codebase 且幫你略過 git 歷史,下載速度更快之外,也直接省掉自己要手動砍 .git 的工。

而原理其實也很單純,就是去找到某個你想下載的 codebase,然後找到它對應的 tar 檔去拉下來放到本地端某個快取資料夾底下,可能類似 ~/.degit/some-user/some-repo/commithash.tar.gz ,再去解壓縮到你指定的當前目錄,也因此解開後的 codebase 可以抓到最新版且不包含 .git

如果有看過前面 Day 12 在下載 Vite 原始碼時,我們用的是 —depth=3 來減少要抓取的 git commit 歷史紀錄,效果也差不多只是要自己手動砍掉 .git

另外看 degit 也有其他幾個好處:

  • 提供一些 API 讓套件開發者可以整合在 Node.js 程式裡
  • 幫你快取 tar 檔,可在離線或快取存在時直接解開

先來加上效能評估的 plugin

回到正題。因為要評估打包速度的差異,這裡安裝可以評估 bundle 時間的 plugin —— speed-measure-webpack-plugin

$ pnpm add -D speed-measure-webpack-plugin

調整一下 next.config.js

// next.config.js
// ...
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
 
// ...
module.exports = () => {
  const plugins = [withContentlayer, withBundleAnalyzer]
  return plugins.reduce((acc, next) => next(acc), {
    // ...
    webpack: (config, { isServer }) => {
      // ...
 
      // 加上 SpeedMeasurePlugin 評估打包時間
      if (!isServer) {
        config.plugins.push(new SpeedMeasurePlugin())
      }
 
      return config
    },
  })
}

記得先清掉上次打包出來的 .next 才會準確,再執行 pnpm run build

$ rm -rf .next
$ pnpm run build
 
 Next.js 14.2.3
 Creating an optimized production build ...
 
 SMP
General output time took 3.19 secs
 
 SMP  Loaders
next took 1.91 secs
  module count = 371
modules with no loaders took 0.201 secs
  module count = 90
@svgr/webpack took 0.069 secs
  module count = 1

這裡可以看到打包大約花了 3.19s

來試著遷移到 Rspack

看目前 Rspack 在遷移的文件並沒有 Next.js 相關的,到 GitHub issues 裡有查到有人詢問過,核心維護者回應看起來結論是沒辦法。因為 Next.js 生態由 Vercel 控制,真的外部工具要做到整合的話會需要另外做一個 hack 版的 plugin,來將打包工作轉給 Rspack,打包完成後再用 hook 方式丟回給 Webpack。

但這個 issue 後來看起來不了了之,可能只能先宣告實驗失敗,突然可以感受到一個框架被企業掌控的小小挫敗感,而也印證了之前看到的這個推可能所言不假,之後 Next.js 就是得跟著 Turbopack 自家生態系了,或等哪天上述這個 hack 版的 plugin 有被補上。

🤔 一些 murmur:只是仍有點好奇為什麼原問題的讀者說有轉換成功,不確定是不是更早版本的 Next.js,待我問問再回頭編輯補充。應該做實驗前要先確認可不可行的,有點失策。 😅 更新:詢問後對方表示後來因為 HMR 有問題,所以測試用專案就砍掉了。

原本的 SMP 可換成 Rsdoctor

另外也看到有一個可以筆記的部份是上面測試時間用的 speed-measure-webpack-pluginRspack 文件上提到也是不兼容的,會需要另外用它們生態系中的一套 Rsdoctor

rsdoctor

只好小試一下 Turbopack

上面的準備工作都做好了不用可惜,雖然 Next.js 官網文件明確寫到目前 Turbopack 還只能用在 dev 不能用在 build,而且從 2022 年發佈後尚在 beta 階段,有點不期不待,但還是來試試看 dev 的 bundle 是否有比 Webpack 快。

這裡可以將 package.json 改成這樣:

"scripts": {
    "dev": "cross-env INIT_CWD=$PWD next dev --turbo",
}

next.config.js 中將原本的 webpack 改成 turbo

module.exports = {
  // ...
  experimental: {
    turbo: {
      rules: {
        '*.svg': {
          loaders: ['@svgr/webpack'],
          as: '*.js',
        },
      },
    },
  },
}

兩種的 dev 啟 server 測測看,原本 Webpack 版本是 4.1s,調整成以上的設定並砍掉 .next 後重測看看:

 Next.js 14.2.3 (turbo)
  - Local:        http://localhost:3000
  - Experiments (use with caution):
    · turbo
 
 Starting...
 Ready in 1902ms

算是有快了將近一半的時間,也算是個好消息,雖然設定中這個 experimental 在正式產品上用我自己也是會覺得有點抖就是。但這個結果也算是可以稍稍扳回一城證明 Rust 工具有比較快。

💡補充:今天剛好看到 Vercel CEO 的推特在討論是否有期待 Next.js 生態系改善的地方,果不其然有人提到 Turbopack,看起來近期可能有機會有一些新進度 (ref):

turbopack

實驗二:將 Remix 專案遷移到 Rspack 可行嗎?

有了前面的經驗後先來查查看是不是可行。從 Remix 官網文件這段上看起來沒特別講什麼,只說到可以將原本舊版用 Webpack 的專案遷移成他們的 CLI,而這個 CLI 看起來應該是 Vite 為基底去客製的 (ref)。

另外有查到 Rspack 核心開發者似乎有發過貼文測試說可以在 Remix 上 build,只有看到他有寫了一個 plugin,但沒找到實際設定的方法。

或許只能等未來 Vite 的工具生態鏈補齊了,剛好昨天晚上的 ViteConf 2024 有提到他們將募資成立一個叫做 void(0) 的未來網頁工具生態鏈的公司,只能說未來可期。

另外也讀了 Rspack 部落格這篇文章中有提到 Remix:

As the ecosystem evolved, full-stack web frameworks such as Next.js, Nuxt, and Remix became popular; Vite was introduced as a lightweight build tool and also gained popularity. However, CRA and Vue CLI gradually stopped being maintained.

因為 Rspack 是 ByteDance 內部 infra 團隊打造的工具,原本的目的是想要改善內部舊有的 CRA 與 Vue CLI 專案,所以在遷移到 Rspack 的情境上可能比較多會是這種。而更現代的 meta frameworks 像是 Next.js、Remix 等都有自己針對 CLI 做客製化,所以要遷移相對比較困難,除非社群中有人開發出 plugin 來橋接,或是未來哪天核心開發團隊有打算合作才有機會吧。或其實上述是可行的只是我沒找到對的方法,也歡迎下方留言告訴我!

補充:awesome-rspack

再補充一個,對於如何遷移舊專案到 Rspack 或遷移到 Rspack 後速度有多快,今天在查資料時找到了在 awesome-rspack 裡有搜集了許多不錯的文章,有需要的讀者可以參考,這裡就不一個一個展開了。

倒是看到這篇文章中有些內容值得筆記一下,裡面提到一個既有的 Webpack 專案在考慮遷移時,也曾想過要用 Vite,但 Vite 有一些硬傷:

  • Esbuild in dev, Rollup in prod 的問題,會造成難以在 production 環境 debug
  • browser network congestion 應該就是《Day 15:來做個實驗 - 壓測 Vite 的效能瓶頸》中有發現的一個問題,當你自己 source code 的模組依賴比較深層時還是會造成 request waterfall,其中有提到怎麼調整設定來改善此問題
  • Vite 原本是 Rollup 體系,設定基本上與 Webpack 不兼容,既有專案會需要重寫打包設定檔與找對應 plugin

也因此,這個文章作者後來是選擇了與 Webpack 更兼容的 Rspack 來遷移。

只能說在考慮遷移這件事在實務上有許多兼容的點需要考慮進去,不能單單只看到工具新潮、效能快就魯莽套上去用,很可能會造成後續系統不穩定的問題,經驗來說更多時候會找可以漸進式改善的方法與策略來執行,像是遷移就語法、將測試補得更齊全、導入監控系統如 Sentry 等,才能確保不會發生非預期錯誤。

小結

今天原本想來嘗試 Rspack 工具有多快,也因為剛好有個疑惑想實驗看看 Next.js、Remix 這兩個框架是否能做遷移,結果研究起來暫時都不可行的樣子,雖然實驗沒有成功,但也學會了 degit 是什麼、speed-measure-webpack-plugin 的用法、路過試試 Turbopack 等等,也不算完全沒有收穫。