📋 目錄
這篇文章會帶你把
Transformers.js 4.0+Vite 8的升級風險,落成可上線的 fallback 實作與檢查清單。
前言
前端團隊導入瀏覽器端 AI 時,最容易低估的不是模型效果,而是升級風險:套件版本對了,型別卻壞掉;本機跑得動,上線後 WASM 路徑 404;某些裝置 WebGPU 初始化失敗,整個功能直接掛掉。
這篇不是泛用 AI 入門,而是聚焦你在升級到 @huggingface/transformers@4.0.0 與 vite@8.0.3(截至 2026-04-02)時,最常踩到的三個坑:TypeScript 型別、推論執行緒隔離、部署資產路徑。
目標是讓你今天就能把「WebGPU 優先、WASM 保底」這套流程落地,並保留可回滾空間。
核心主題 1:4.0 升級時先處理型別與初始化邊界
這次最常見的破壞點,是把舊範例直接搬到 4.0。實務上至少要先檢查兩件事:
- 不要假設存在通用
Pipeline型別;改用實際 task 對應的型別,例如TextClassificationPipeline。 - fallback 不要只靠
navigator.gpu判斷,因為「有 API」不代表 runtime 初始化一定成功。
下面是可直接放在 Worker 的最小可執行範例。
// src/ai/worker.ts
import {
env,
LogLevel,
pipeline,
type TextClassificationPipeline,
} from '@huggingface/transformers'
type InferRequest = { id: string; text: string }
type InferResponse = {
id: string
ok: boolean
backend: 'webgpu' | 'wasm'
output?: unknown
error?: string
}
const MODEL_ID = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english'
let classifierPromise: Promise<TextClassificationPipeline> | null = null
let backend: 'webgpu' | 'wasm' = 'wasm'
env.logLevel = LogLevel.WARNING
async function createClassifier(): Promise<TextClassificationPipeline> {
try {
const webgpuClassifier = await pipeline('sentiment-analysis', MODEL_ID, {
device: 'webgpu',
dtype: 'fp32',
})
backend = 'webgpu'
return webgpuClassifier as TextClassificationPipeline
} catch {
// 中文註解:WebGPU 失敗時,改走預設 WASM runtime
const wasmClassifier = await pipeline('sentiment-analysis', MODEL_ID, {
dtype: 'q8',
})
backend = 'wasm'
return wasmClassifier as TextClassificationPipeline
}
}
function getClassifier() {
if (!classifierPromise) classifierPromise = createClassifier()
return classifierPromise
}
self.onmessage = async (event: MessageEvent<InferRequest>) => {
const { id, text } = event.data
try {
const classifier = await getClassifier()
const output = await classifier(text)
const response: InferResponse = { id, ok: true, backend, output }
self.postMessage(response)
} catch (error) {
const response: InferResponse = {
id,
ok: false,
backend,
error: error instanceof Error ? error.message : 'unknown error',
}
self.postMessage(response)
}
}
核心主題 2:Vite 8 要把推論放進 Worker,避免 UI 掉幀
Vite 8 本身不會幫你解決主執行緒卡頓。若推論直接跑在 UI thread,首載模型時很容易造成互動延遲。最穩的方式是固定用 Worker + 訊息協定,讓 UI 只負責發 request 與顯示結果。
// src/ai/client.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' })
export async function classify(text: string) {
const id = crypto.randomUUID()
return new Promise<{ label: string; score: number; backend: 'webgpu' | 'wasm' }>(
(resolve, reject) => {
const onMessage = (event: MessageEvent) => {
const data = event.data
if (!data || data.id !== id) return
worker.removeEventListener('message', onMessage)
if (!data.ok) {
reject(new Error(data.error ?? 'inference failed'))
return
}
// 中文註解:sentiment-analysis 預設回傳陣列,取第一筆即可
const first = Array.isArray(data.output) ? data.output[0] : data.output
resolve({ label: first.label, score: first.score, backend: data.backend })
}
worker.addEventListener('message', onMessage)
worker.postMessage({ id, text })
},
)
}
工程上建議加一條規則:同一頁面若連續失敗達門檻(例如 3 次),直接關閉端側推論並切回伺服器 API,避免使用者一直重試。
核心主題 3:wasmPaths 是部署設定,不是每個專案都要硬改
許多文章會直接寫 env.backends.onnx.wasm.wasmPaths = '/wasm/',但在 TypeScript 專案常遇到型別不穩。更重要的是,這個設定本質上是「資產部署路徑」問題,只有在你要自管 ONNX Runtime WASM 檔案時才需要設定。
// src/ai/runtime.ts
import { env } from '@huggingface/transformers'
type OnnxEnvWithWasm = typeof env.backends.onnx & {
wasm?: {
wasmPaths?: string | Record<string, string>
}
}
const onnxEnv = env.backends.onnx as OnnxEnvWithWasm
// 中文註解:只有你要把 wasm 檔放在 /public/wasm 或 CDN 時才需要覆寫
if (onnxEnv.wasm) {
onnxEnv.wasm.wasmPaths = '/wasm/'
}
env.useWasmCache = true
搭配 Vite 8,建議你把部署策略固定成以下其中一種:
- 交給套件預設下載(簡單,但首次載入較慢)。
- 自家 CDN 託管 WASM(可控性高,需維護版本映射)。
- 站內
/public/wasm託管(最直覺,需確認 cache header 與路徑一致)。
核心主題 4:把升級風險寫成可驗證清單
如果你的團隊要在 Sprint 內交付功能,建議不要只靠「能跑起來」做驗收,而是把風險拆成可以打勾的項目。下面這份清單,是我在前端專案導入端側推論時最常用的最小版本。
4.0 / Vite 8 風險對照
- 型別風險:
Pipeline泛型假設過度,導致 CI 在tsc --noEmit才爆。解法是改用 task 專屬型別,或在邊界層做明確as轉型並集中管理。 - 執行期風險: 裝置宣告有 WebGPU,但 runtime 初始化失敗。解法是 Worker 內 fallback,不把判斷放在 UI 層就結束。
- 資產風險:
WASM 路徑在 dev 正常、部署後 404。解法是把
wasmPaths與 CDN/public 路徑納入 deploy checklist,並在 staging 先驗證。 - 觀測風險: 推論失敗只在 console 出現,沒有監控事件。解法是上報 backend、model id、error type、裝置資訊,才能做事故切分。
建議的 CI / QA 驗證順序
TypeScriptgate:每次 PR 必跑tsc --noEmit,先擋掉型別退化。- fallback 測試:模擬 WebGPU 不可用,確認仍能回傳可用結果。
- 資產 smoke test:用 staging 網域實際載入 WASM,確認路徑與快取標頭。
- 壓力測試:同時發 10-20 次請求,確認 Worker 不會造成主執行緒長時間卡頓。
- 回滾演練:透過 feature flag 關閉端側推論,驗證 API fallback 可即時生效。
這段流程的重點只有一個:你要有能力在「版本升級」和「產品穩定」之間快速切換,而不是每次都靠人工猜測。
分階段遷移路線(建議 2 週)
- 第 1-2 天:先把 Worker 與 fallback 跑通,只接一個低風險任務(例如 sentiment 或關鍵字分類)。
- 第 3-5 天:補齊監控欄位與錯誤分級,至少能區分「模型下載失敗」與「推論失敗」。
- 第 6-8 天:在 staging 做裝置分層驗證,蒐集桌機/中階手機/低階手機的延遲差異。
- 第 9-10 天:灰度 5%-10% 流量,觀察錯誤率、P95 latency、回滾頻率,再決定是否擴大。
很多團隊導入失敗,不是技術做不到,而是直接一步到位推全流量。端側推論應該像基礎設施升級一樣,先小範圍、可觀測、可回退,再逐步放大。
常見問題 / 注意事項
WebGPU 可用就一定要強制走 WebGPU 嗎?
不建議強制。某些裝置初始化成本很高,短文本任務不一定比 WASM 快。建議用真實流量觀察 P95 latency 再決定預設路徑。
Vite 8 升級時,最優先該補哪種測試?
先補「初始化 + fallback」整合測試。你要驗證的是:WebGPU 失敗時,功能仍能在 WASM 下回傳結果。
什麼情況不建議現在導入端側推論?
如果你沒有錯誤監控、沒有回滾開關、也沒有裝置分層策略,先不要上線。先把觀測與 fallback 打好,風險會小很多。
總結
Transformers.js 4.0 搭配 Vite 8 已經能做出可上線的瀏覽器端推論,但前提是你把升級風險當成工程問題來處理,不是只看 demo 能不能跑。
這篇的落地順序可以這樣做:
- 先修正型別與 Worker 初始化,確保最小可執行。
- 補上 WebGPU 失敗自動 fallback 到 WASM。
- 依部署策略決定是否設定
wasmPaths,並加上回滾開關。
把這三步做好,你的端側 AI 功能才不會在真實裝置上變成隨機故障源。