三咲智子 Kevin Deng

三咲智子 Kevin Deng

Gen Z | Full Stack Developer 🏳️‍🌈
github
twitter
twitter

踩坑異聞錄——Windows 前端工具鏈之痛

前言#

昨天發了一條推特,是因為做工具鏈又碰到了 Windows 相關的兼容性問題,所以有感而發的一句吐槽。但似乎以推特的氛圍,只有這種「暴論」輸出才能引起莫名其妙的關注。引發了部分人的不滿,甚至引發了 Windows 與 macOS 之間的爭論。

那我們就來站在 Unix-like 系統的視角下,聊聊關於 Windows 在前端工具鏈(Node.js)中的「坑」。

路徑煩惱#

詭異的反斜杠#

在 Windows 中,一個常見的路徑看起來像 D:\path\to\file.txt;而在 Unix-like 系統中,則為 /home/username/file.txt。顯而易見,Windows 使用反斜杠 \ 作為路徑層級之間的分隔符。而在絕大多數的編程語言中,正斜杠通常不需要特殊處理,而反斜杠則用於轉義字符。舉個例子:

console.log('\\') // 實際上只輸出一個反斜杠
console.log('\n') // 輸出換行符
console.log('/') // 沒有意外,輸出一個正斜杠

這意味著,如果我們只考慮在 Unix-like 系統中運行,基本可以忽略對路徑的特殊處理。

: 分隔符#

不僅如此,在 Windows 中還有盤符的概念;而在 Linux 則使用掛載(mount)來管理不同的物理硬碟和分區。這也是一處需要考慮兼容性的地方。

🌰 舉個例子#

說再多不如一個例子來得貼切。可以在 GitHub 中搜索到我被 Windows 折磨的記錄

test: try fix path for windows#

這個 commit 旨在解決單元測試在 Windows 上的兼容性問題。我們需要傳遞一個 glob 表達式entry 選項。這在 Unix-like 系統中,可以直接傳遞文件的路徑 /path/to/file 來直接匹配確切的文件,這很好。

而 glob 是起源於 UNIX 的功能,在 Windows 中,使用反斜杠並不能在 fast-glob 中像在 Unix-like 系統中那樣正常工作。這就造成了一致性問題。雖然 fast-glob 提供了 convertPathToPattern 函數幫你轉義 Windows 的反斜杠,但仍有部分情況未能解決。

在這個 commit 中更坑的是,我在代碼使用了 4 個反斜杠,這是什麼呢?因為這段代碼被反引號 ` 與 單引號 ' 雙重包裹。因此 4 個反斜杠最終只會表示為一個真正的反斜杠。

fix: watch ignored option for windows#

這也是一個常見的例子,我們用正則表達式來檢查某個路徑是否在 node_modules 文件夾內。為了兼容 Windows 我們需要寫 /[\\/]node_modules[\\/]/,同樣這裡的雙重反斜杠最終只會匹配一個反斜杠。但更麻煩的是,對於第三方依賴,你似乎無法確定在 Windows 下它會傳遞反斜杠的路徑,還是會自動替換成正斜杠的路徑。在這個例子中,chokidar 的處理規則頗為奇怪,對於傳遞 function,它會傳遞正斜杠的路徑,對於靜態的數組,則會特殊處理。因此有了這個 commit。

fix: support Node 22/23 strip types feature#

還記得剛剛提到的 : 分隔符嗎?之前我也以為它似乎沒有兼容性問題。這樣想就太天真了!如果我們要在 Node.js 中導入一個絕對路徑,這在 Unix-like 系統很簡單。

require('/path/to/test.cjs')

平平無奇。然而,在 Windows 就會有大問題!

require('C:\\path\\test.cjs') // ✅
import('C:\\path\\test.cjs') // ❌
// Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'

這是因為 require API 似乎只接受路徑 (path) 作為參數,而 import() 不僅支持路徑,還支持 File URL。所以 Node.js 會把 C: 當作一個 URL,它的 protocolc:。這與 http://path/test.cjs 本質上沒有太大的區別。File URL、路徑傻傻分不清 🤷

import('file://C:/path/test.cjs') // ✅
console.log(new URL('C:\\path\\test.cjs')) // protocol = 'c:'

import 語句#

import mod from 'a/b/n'
import mod from 'a\b\n'

在 ESM 中,必須使用正斜杠作為分隔符。直接使用反斜杠可能導致意外的轉義。在代碼生成相關庫需要額外注意。

🤨 怎麼辦?#

那麼如何解決呢?使用 pathe 或許是個不錯的選擇!即使在 Windows 環境下,它也會直接把路徑轉換為正斜杠。而正斜杠在 Windows 也會被視為反斜杠。所以對於大部分的情況,都能直接幫你解決一致性問題。但邊緣情況呢?還是有挺多的。

開源社區#

據我所知,我周圍開發和貢獻開源項目的大部分人都在使用 Unix-like 系統。而用戶可能大部分使用的是 Windows。我明白這與市佔率可能有一定關係。但對於作者與維護者來說,也是一件無可奈何的事情。

以我舉例,我的開源項目會借助 GitHub Actions,在 Windows 下跑單元測試。每次我 push 完代碼,收到 GitHub Action 的錯誤通知,一看又是 Windows 下跑不過,總會很苦惱。因為手頭上並沒有一台 Windows 電腦,還裝了開發測試環境。以至於被逼無奈,我現在在使用虛擬機調試 Windows 上的 bug。

而且單元測試不總是能覆蓋方方面面,總會有纰漏。這個就需要實際用戶來發現並提 issue 了!

兼容性問題一覽#

  • 路徑
    • 反斜杠
      • 可能需要轉義
      • 可能需要轉為正斜杠
    • 盤符 :
      • 可能需要轉為 File URL
  • 無法使用 KEY=VALUE command 設置環境變量
  • 沒有 rm -fr 命令
    • 使用 fs.rmSync(path, { recursive: true, force: true })
    • 使用 rimraf

種種情況可謂是數不勝數。小編也沒有辦法列舉出所有的情況,這些只是我印象中遇到的一些的坑。

後記#

你遇到了十個路徑問題,並不意味著只有十個,可能還有更多在路上 🛣️…… 恕無法接受「連路徑都處理不對」這樣的說法。

如果有注意到的話,本篇文章通篇都在使用 Unix-like 這樣的表述。這是因為世界上除了 Windows,還有許多其他操作系統。

本文與推文都無意引起 Windows 與 Linux、macOS 的「論戰」。Windows 在很多方面仍在發揮重要的作用(比如說打遊戲)。但就事論事來講,在跨平台領域確實造成許多的不便。

我的庫#

對於我自己的開源庫,我仍然會盡力兼容 Windows,添加 Windows 到 CI 運行。但對於一些邊緣情況,可能會有所忽略。如果你在使用我的庫時遇到了問題,歡迎提 issue 和 PR,我會盡力解決。

替代品#

為了珍惜寶貴生命,建議可以嘗試使用 Unix-like 系統做前端開發(包括 WSL 與第三方虛擬機)。當然,你也可以繼續使用 Windows,但遇到兼容性問題時,請更少抱怨並付出額外的時間、更積極地向開源社區反饋問題與貢獻。

碎碎念#

如果看完文章後,仍然認為這是「傲慢」或「無知」,請自便。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。