13 多語言版本管理
不同專案可能需要不同版本的程式語言。版本管理工具讓你在它們之間無縫切換。
13.1 工具選擇
| 工具 | 特點 |
|---|---|
| asdf | 通用,支援多語言 |
| mise | asdf 的 Rust 重寫,更快 |
| fnm | 只管理 Node.js,極快 |
| pyenv | 只管理 Python |
| rbenv | 只管理 Ruby |
13.2 mise:推薦選擇
mise(前身 rtx)是 asdf 的現代替代品:
13.2.1 背景(問題發現)
開發時需要在不同專案間切換不同版本的程式語言。例如專案 A 需要 Node 18,專案 B 需要 Node 20。傳統做法是手動安裝多個版本並切換,容易出錯且麻煩。
13.2.2 方法
使用 mise 統一管理所有語言版本。mise 會: 1. 讀取專案設定檔(.mise.toml 或 .tool-versions) 2. 自動切換到對應版本 3. 在 shell 初始化時載入必要的環境變數
13.2.3 結果(程式碼)
13.2.4 討論/延伸
- mise 使用 Rust 編寫,比 asdf 快 2-3 倍
- 相容 asdf 的
.tool-versions格式,可無痛移轉 - 支援 lazy loading,不會拖慢 shell 啟動速度
- 可同時管理 Node、Python、Ruby、Go 等多種語言
13.2.5 基本使用
13.2.5.1 背景(問題發現)
需要在系統層級設定預設的程式語言版本,確保在任何目錄下都能使用特定版本。
13.2.5.2 方法
使用 mise install 下載特定版本,mise use 設定為全域預設版本。mise 會將版本資訊寫入 ~/.config/mise/config.toml。
13.2.5.3 結果(程式碼)
13.2.5.4 討論/延伸
mise use預設是全域設定,加上--local可設定專案層級- 可使用
mise use -g node@20明確指定全域 mise list顯示所有已安裝版本及其使用狀態- 版本號可使用
@latest、@lts等別名
13.2.6 專案設定
13.2.6.1 背景(問題發現)
不同專案需要不同版本的語言環境。當切換專案時,希望能自動使用對應版本,而不需手動切換。
13.2.6.2 方法
在專案根目錄建立版本設定檔。當進入該目錄時,mise 會自動讀取設定並切換版本。有兩種格式可選: 1. .mise.toml:mise 原生格式,功能更豐富 2. .tool-versions:asdf 相容格式,適合團隊已使用 asdf
13.2.6.3 結果(程式碼)
在專案目錄建立 .mise.toml:
或使用 .tool-versions(與 asdf 相容):
node 20.10.0
python 3.12.0
13.2.6.4 討論/延伸
.mise.toml支援更多功能,如環境變數、任務定義等.tool-versions適合與使用 asdf 的團隊協作- 可用
mise use node@20 --local自動產生設定檔 - 版本號建議使用主版本(如
20)而非完整版本(20.10.0),讓 mise 自動選擇最新的次版本 - 專案設定優先於全域設定
13.3 fnm:Node.js 版本管理
如果只需要管理 Node.js,fnm 是最快的選擇:
13.3.1 背景(問題發現)
純前端開發者可能只需要管理 Node.js 版本,使用 mise 這類通用工具會有額外開銷。需要一個專注於 Node.js、啟動速度極快的工具。
13.3.2 方法
fnm(Fast Node Manager)專注於 Node.js 版本管理,使用 Rust 編寫,啟動時間僅需 1-2ms。它會: 1. 自動偵測 .node-version 或 .nvmrc 檔案 2. 進入目錄時自動切換版本 3. 支援跨平台(macOS、Linux、Windows)
13.3.3 結果(程式碼)
13.3.4 討論/延伸
- fnm 比 nvm 快 40 倍以上
- 自動偵測多種設定檔格式:
.node-version、.nvmrc、package.json的engines欄位 - 可用
fnm install --lts安裝最新 LTS 版本 fnm default設定全域預設版本- 建議在
.zshrc加入eval "$(fnm env --use-on-cd)"啟用自動切換 - 若使用 mise,則不需要額外安裝 fnm
13.4 Python 環境管理
13.4.1 uv:現代 Python 工具
uv 是 Rust 寫的超快 Python 套件管理器:
13.4.1.1 背景(問題發現)
Python 套件管理一直是痛點:pip 速度慢、依賴解析不可靠、虛擬環境管理繁瑣。大型專案安裝套件可能需要數分鐘,嚴重影響開發效率。
13.4.1.2 方法
uv 重新設計了 Python 套件管理流程: 1. 使用 Rust 實作,比 pip 快 10-100 倍 2. 先進的依賴解析演算法,避免衝突 3. 全域快取機制,避免重複下載 4. 統一管理虛擬環境與套件
13.4.1.3 結果(程式碼)
13.4.1.4 討論/延伸
- uv 與 pip 完全相容,可直接替換
pip指令 - 使用全域快取,多個專案共用相同套件時不會重複下載
- 支援
pyproject.toml格式的現代專案結構 - 可用
uv pip compile產生 lock 檔案,確保依賴版本一致 - 大型專案(如 pandas)安裝時間從 2 分鐘降到 10 秒
- 由 Astral 團隊開發(Ruff 的作者),持續活躍維護
13.4.2 自動啟用虛擬環境
13.4.2.1 背景(問題發現)
每次進入 Python 專案目錄都需要手動執行 source .venv/bin/activate 啟用虛擬環境,容易忘記且重複勞動。
13.4.2.2 方法
利用 zsh 的 chpwd 函數,在切換目錄時自動偵測 .venv 目錄並啟用虛擬環境。
13.4.2.3 結果(程式碼)
13.4.2.4 討論/延伸
- 這段程式碼應放在
chpwd()函數內(見下方「進入目錄時自動處理」範例) - 僅當目錄包含
.venv/bin/activate時才啟用,不會影響其他目錄 - 離開專案目錄時需要手動
deactivate,或使用更進階的自動管理方案 - 可搭配 direnv 使用,實現更完整的環境變數管理
- 注意:同時開啟多個專案時,可能會互相干擾虛擬環境
13.4.3 pipx:全域工具
用 pipx 安裝全域 Python 工具:
13.4.3.1 背景(問題發現)
某些 Python 工具需要全域使用(如 ruff、black、pytest),但直接用 pip 安裝會污染系統 Python 環境,且不同工具的依賴可能衝突。
13.4.3.2 方法
使用 uv tool 為每個工具建立獨立的虛擬環境,並將執行檔連結到 PATH。這樣每個工具都有自己的依賴,互不干擾。
13.4.3.3 結果(程式碼)
13.4.3.4 討論/延伸
13.5 實用設定
13.5.1 自動初始化 Python 專案
13.5.1.1 背景(問題發現)
開始新 Python 專案時,總是需要重複執行兩個指令:建立虛擬環境、啟用虛擬環境。希望簡化為單一指令。
13.5.1.2 方法
建立 shell alias,將兩個指令組合成一個。使用 && 確保第一個指令成功後才執行第二個。
13.5.1.3 結果(程式碼)
13.5.1.4 討論/延伸
使用方式:在專案目錄執行
uvinit即可可擴充為函數,加入更多初始化步驟:
也可考慮用
uv init建立完整專案結構(包含 pyproject.toml)若需要指定 Python 版本:
uv venv --python 3.12
13.5.2 進入目錄時自動處理
13.5.2.1 背景(問題發現)
開發時有兩個常見痛點: 1. 忘記啟用虛擬環境就開始工作,導致套件安裝到系統 Python 2. Dropbox 同步 .venv 和 node_modules 造成大量上傳、浪費空間
13.5.2.2 方法
利用 zsh 的 chpwd hook 函數,在每次切換目錄時自動執行: 1. 偵測並啟用 Python 虛擬環境 2. 設定檔案屬性,告訴 Dropbox 忽略特定目錄
xattr -w 'com.apple.fileprovider.ignore#P' 1 是 macOS 的特殊指令,標記目錄不要同步到雲端。
13.5.2.3 結果(程式碼)
13.5.2.4 討論/延伸
chpwd是 zsh 內建的 hook,每次cd時自動執行- 若使用 bash,需改用
PROMPT_COMMAND - 這段程式碼應加入
~/.zshrc或 dotfiles 管理 - 虛擬環境啟用是單向的(進入時啟用),若需要離開時自動停用,需要更複雜的邏輯
- xattr 指令對其他雲端服務(Google Drive、OneDrive)也有類似效果
- 可擴充到其他需要忽略的目錄:
.git、build/、dist/等 - 效能考量:大量目錄切換時會有輕微延遲(約 10-50ms)
13.6 實作練習
- 安裝 mise 並設定不同版本的 Node.js
- 用 uv 建立 Python 虛擬環境
- 在專案中建立
.mise.toml
uv 比 pip 快 10 到 100 倍。在大型專案中差異非常明顯。