再見了 Create React App! 從Webpack 到 Snowpack 搬家紀錄

xxlee (Ching Hung Lee)
7 min readMar 18, 2021

過去開發新的 react 專案時,多是使用 create-react-app (CRA) 來快速完成初期建置,而使用 webpack 的 CRA 存在已下缺點:

  • webpack 的啟動時間長
  • webpack Hot Module Replacement (HMR) 需要重新整理網頁,無法保留 state
  • 若要修改 webpack 設定,需靠 react-app-rewiredcraco 等套件來實現

由於過去瀏覽器對 ES module (ESM) 支援度並不好,因此需要透過 webpack 打包成瀏覽器看得懂的語法,但隨著 ESM 逐漸被主流瀏覽器所支持(Can I Use),不需打包的 build tool 如 SnowpackVite 成為越來越多開發者的選擇,更多詳細的比較可以參考:

https://blog.logrocket.com/snowpack-vs-webpack/

本篇文章將告訴你如何將使用 CRA 的舊專案轉移至 Snowpack (原本是想選目前星星數比較多的 Vite,但轉換的過程感覺不是很穩定,也遇到了一些不知道怎麼解決的 error),以及過程中所遇到的問題。

開發環境

  • react-scripts@4.0.1
  • react@17.0.1
  • typescript@3.9.7
  • 使用 cracoeslint

轉換過程

建議可以先將官方的 react template 開著來對照。

1. 安裝 snowpack

先在 terminal 執行:

yarn add -D snowpack

接著改寫 package.json 的 scripts:

"scripts": {
- "start": "react-scripts start",
+ "dev": "snowpack dev",
- "build": "react-scripts build",
+ "build": "snowpack build",
...
},
...

然後執行 yarn dev ,接下來就完成了🎉🎉!感謝大家的收看!

如果是有一定開發規模的專案,這時候應該會被 error 噴個滿臉。

2. 新增 snowpack 所需檔案

  • snowpack.config.js:在和 package.json 的資料夾(根目錄)新增檔名為 snowpack.config.js 之設定檔,內容可以去抄官方的 template
  • index.html:將原本放在 public/index.html 的檔案加入 <script type="module" src="/src/index.js"></script> 。由於路徑會自動 rebase 至專案根目錄,因此可以將所有的 %PUBLIC_URL% 刪除。

3. 排除 error

會遇到的 error 因專案而異,這邊僅紀錄這次所遇到的問題,有沒列到的也歡迎留言補充。

  • polyfillNode:import 的 npm package 並不一定能直接在瀏覽器使用,Snowpack 提供了一個簡單的解決方法,只需將 packageOptions.polyfillNode 設為 true 即可。
  • 路徑:ESM import 的路徑開頭只能是 /./ 、和../ ,所以如果以前有寫 import XX from 'path/to/XX' 這類的程式碼,就會被解讀成為 npm package 而導致 error。可以透過在前面加上 ./ ,或是透過 path alias 來解決。
  • path alias:蠻多開發者會習慣用 @ 代表 src ,在 Snowpack 只需要在 snowpack.config.js 的 alias ,以及 tsconfig.json 的 path 做好相對應的設定即可。
// snowpack.config.js
// https://www.snowpack.dev/reference/configuration#alias
{
alias: {
'@': './src',
},
...
}
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
},
...
}
}
  • 環境變數:原本 node.js 下的環境變數是靠 process.env.XXX 來讀取,在 ESM 下則會揭露於 import.meta.env.XXX 。另外需注意變數名稱需為 SNOWPACK_PUBLIC_* 之格式,若是寫在 .env 檔案內的需要安裝 plugin-dotenv 才能正常讀取,詳細請見官方教學
  • 型別宣告缺失:在使用 import.meta.XXX 時會出現沒有定義 ImportMeta 屬性的 error,這時只需要安裝 @types/snowpack-env 即可將缺少的型別補上。
  • proxy:開發時會需將 api request proxy 至正確的 api server,在 webpack 會以 setupProxy.js 來做設定,Snowpack 則可以參考官方教學
  • static assets:使用 typescript 在 import png、svg 等靜態資源時會出現 Cannot find module 的 error,可能以前 CRA 默默的幫我們處理好了,只需要在 xxx.d.ts (放在自己覺得合適的地方)宣告以下型別即可:
declare module '*.png' { export default '' as string; }
// jpg, svg 依此類推

4. 開啟 Hot Module Replacement (HMR)

終於結束了枯燥乏味的 debug 過程,這時候專案應該可以正常的開起來了,不過搬來 Snowpack 的主要原因就是為了更快的開發速度,這時超快速,可以保留 state,不需要 refresh 的 HMR 正式當場啦!其實官方的教學已經寫得很簡單了,不過為了讓文章看起來更豐富一點,我還是將內容再濃縮一下放上來,不用謝不用謝~

首先先將 HMR 模式打開

// src/index.ts...
if (import.meta.hot) {
import.meta.hot.accept();
}

再來灌需要的 package

yarn add -D @snowpack/plugin-react-refresh

最後 snowpack.config.js 中使用

{
plugins: ['@snowpack/plugin-react-refresh'],
...
}

恭喜!這次真的可以開心的開始開發了🎉!

Production 環境

Snowpack 可以自行選擇打包工具,預設為 esbuild ,可以透過修改 config 中的 optimize 來調整打包的行為:

// snowpack.config.js

module.exports = {
optimize: {
bundle: true,
minify: true,
target: 'es2018',
},
};

但是由於 esbuild 目前還沒非常成熟,官方目前其實比較推薦用 webpack 來做打包,使用方式非常簡單,先安裝好 @snowpack/plugin-webpack,然後在 config 中加入:

// snowpack.config.jsmodule.exports = {
plugins: ['@snowpack/plugin-webpack'],
};

只需一行即設定完成!開發者也可以對 webpack 設定做調整,方法請自行參考套件文件。

至此就是所有我經歷的過程,其實目前 Snowpack 已經十分完善,很多碰到的問題都可以在官方文件得到解答,祝各位都能順利的轉換,享受 ESM 帶來的高速開發體驗!

--

--