📋 目錄
這篇會帶你把 Vue 3.6 beta.9 的 changelog 轉成可執行的 SSR 回歸測試與升級決策,避免「升了版本才知道哪裡壞掉」。
前言
多數團隊看到 v3.6.0-beta.9 第一個反應通常是:「beta 先等等,等 RC 或 stable 再說。」這個策略沒有錯,但如果你在維護 SSR 專案(尤其是 Nuxt 或自架 createSSRApp),你還是需要提早做一件事:把這次修補整理成你的回歸測試矩陣。
原因很實際。Vue 3.6 beta.9 不是在推大量新 API,而是在 hydration、teleport、keep-alive 和 runtime-vapor 相關路徑修了很多 edge case。這類修補的特性是「你平常感受不到,一踩到就很痛」:例如 hydration anchor 對不上、Teleport target 變動後殘留節點、KeepAlive 分支切換後快取或 scope 行為異常。
截至 2026-04-02,npm dist-tags 仍是 latest: 3.5.31、beta: 3.6.0-beta.9。也就是說,這一版不是所有團隊都要立刻上,但所有 SSR 團隊都該先知道「上了會影響哪裡」。
為什麼 Vue 3.6 beta.9 值得關注
先講結論:這版最值得關注的是 runtime 穩定性,不是語法糖。
從 minor 分支 changelog 可以看到三個明顯訊號:
hydration針對 Teleport null target、disabled teleport、nested anchor 定位連續修補。teleport修正 target 變更時舊 anchor 清理、missing slots、deferred mount/unmount 等邊界。keep-alive與runtime-vapor出現大量 cache key、scope cleanup、async component reactivation 相關修補。
這代表什麼?代表你就算不是在玩實驗性能力,仍可能受影響,因為 SSR 專案裡最容易爆雷的本來就不是「畫面有沒有渲染」,而是「hydration 後 DOM 與 reactive state 還有沒有對齊」。
三類高風險修補點:Hydration、Teleport、KeepAlive
下面不是逐行翻譯 changelog,而是把它轉成工程上可判斷風險的分類。
Hydration:先看 target/anchor 定位是否穩定
beta.9 這波 hydration 修補重點是 Teleport 在 null target、disabled 狀態、巢狀情境下的 anchor 處理一致性。
如果你的頁面有以下模式,建議列入高風險清單:
- SSR 階段 target 還不存在,client mount 後才動態插入。
<Teleport :disabled="...">在 client 會切換啟用/停用。- 巢狀 Teleport 或和 Transition / Suspense 一起使用。
這類問題在 production 常見症狀:
- console 出現 hydration mismatch warning。
- 目標容器內出現重複節點或 ghost node。
- 第一次切頁正常,第二次切頁才壞(因為 anchor 殘留)。
Teleport:target 變更與延遲掛載是主要地雷
beta.9 對 Teleport 的 patch 很集中在「target 改變後的清理與重掛」:
- target 切換時舊 anchor 清理。
- root replacement 後 css vars 重套。
- deferred teleport 在已 unmount 情境下不該繼續 mount。
工程上要特別注意兩種寫法:
- 依 route/state 動態改
to。 - target 容器由外層 layout 非同步建立(例如 portal root 晚於 app mount)。
如果你的 UI 有 Drawer、Modal、Toast、Global Dropdown,幾乎都踩在這條路徑上。
KeepAlive:最常被忽略,但最容易造成「第二次才壞」
KeepAlive 在 beta.9 的修補包含 keyed branch scope、teardown、composite cache key、async component resolve 後的快取一致性。
這些詞看起來很底層,但實際影響非常「產品面」:
- 表單頁切換回來狀態不一致。
- 非同步頁面元件解完後覆蓋了已卸載分支。
- 記憶體緩慢上升(快取未正確 prune)。
如果你的路由層有 KeepAlive + defineAsyncComponent,或有 tab-based 多分支切換,這類回歸測試必做。
SSR 專案升級前的回歸測試矩陣(可直接落地)
下面給一套「先小後大」的測法,重點是你可以先在 app shell 做 smoke test,再決定要不要上 beta。
1. 先鎖版本並建立測試分支
# 建議在獨立分支做 beta 驗證
npm i vue@3.6.0-beta.9 @vue/server-renderer@3.6.0-beta.9
# 若是 Nuxt 專案,先確認 nuxt 與 vue peer 相容再升
npm ls vue
2. 建立 Hydration + Teleport 的最小驗證
// tests/teleport-hydration.spec.ts
import { describe, expect, it } from 'vitest'
import { createSSRApp, h, ref, Teleport } from 'vue'
import { renderToString } from '@vue/server-renderer'
describe('SSR hydration: Teleport target timing', () => {
it('target 晚到時,不應遺留重複節點', async () => {
const open = ref(true)
const App = {
setup() {
return () =>
h('div', [
h('div', { id: 'app-content' }, 'page'),
// 中文註解:故意把 Teleport 指向可能晚於 hydration 才出現的 target
h(Teleport, { to: '#modal-root' }, open.value ? h('p', 'modal-body') : null),
])
},
}
const html = await renderToString(createSSRApp(App))
// 中文註解:先模擬 SSR 輸出時 target 尚未存在
document.body.innerHTML = `<div id="app">${html}</div>`
// 之後才插入 target
const target = document.createElement('div')
target.id = 'modal-root'
document.body.appendChild(target)
const app = createSSRApp(App)
app.mount('#app')
expect(document.querySelectorAll('#modal-root p').length).toBeLessThanOrEqual(1)
app.unmount()
})
})
3. 建立 KeepAlive + async component 切換測試
// tests/keepalive-async.spec.ts
import { defineAsyncComponent, h, KeepAlive, createApp, nextTick, ref } from 'vue'
import { describe, expect, it } from 'vitest'
describe('KeepAlive with async branches', () => {
it('切換分支後不應殘留舊快取狀態', async () => {
const page = ref<'A' | 'B'>('A')
const A = defineAsyncComponent(async () => ({
name: 'PageA',
setup: () => () => h('div', { 'data-page': 'A' }, 'A'),
}))
const B = defineAsyncComponent(async () => ({
name: 'PageB',
setup: () => () => h('div', { 'data-page': 'B' }, 'B'),
}))
const App = {
setup() {
return () =>
h(KeepAlive, null, [
// 中文註解:刻意給 key,驗證 keyed branch 在反覆切換時是否穩定
h(page.value === 'A' ? A : B, { key: page.value }),
])
},
}
document.body.innerHTML = '<div id="app"></div>'
const app = createApp(App)
app.mount('#app')
await nextTick()
page.value = 'B'
await nextTick()
page.value = 'A'
await nextTick()
const node = document.querySelector('[data-page]')
expect(node?.getAttribute('data-page')).toBe('A')
app.unmount()
})
})
4. 實際專案測試優先順序
- 全域 Teleport 元件(Modal/Drawer/Toast)。
KeepAlive + RouterView或 tab container。- 首頁與高流量落地頁的 SSR hydration。
- 有 async component 的流程頁(登入、結帳、儀表板)。
Nuxt / 純 Vue 專案的升級策略
Nuxt 專案:先「驗證」再「切換預設」
對 Nuxt 專案來說,建議採兩階段:
- 建立
beta verification branch,只做 Vue 與關鍵相依的兼容驗證。 - 先把回歸測試補齊,過了再評估是否進入 staging 或灰度。
要點不是「全站立刻上 beta」,而是避免你未來升級時沒有 baseline 可比對。
純 Vue SSR 專案:可以更早吃 beta,但要有退出路徑
如果你是自管 SSR(Vite + @vue/server-renderer),通常能更快驗證。但請務必保留:
- 一鍵回退到 3.5.x 的 lockfile。
- PR 級別的 benchmark 與 memory snapshot。
- 針對 Teleport/KeepAlive 的 smoke test 常駐 CI。
不建議立刻升的情境
- 專案沒有自動化回歸測試。
- 近期正好在改 Router、Layout、Portal 架構。
- 核心頁面 heavily 依賴 KeepAlive,但目前沒有 state consistency 測試。
這三種情況建議先補測試,再談升級。
升級決策 Checklist:本週可做 / 下版本再做
本週可做
- 盤點所有 Teleport 使用點與 target 建立時機。
- 列出
KeepAlive + async component的頁面路徑。 - 建立至少 3 個最小回歸測試(Hydration、Teleport、KeepAlive 各一)。
- 在 CI 新增一個 beta 驗證 job(不影響主線部署)。
下版本再做
- 若沒有足夠測試覆蓋,先維持
latest(3.5.x)。 - 等 RC 或 Nuxt 生態相容性訊號更清楚後,再切換生產依賴。
- 將 beta 驗證結果整理成團隊內升級 playbook,避免每次重頭踩雷。
常見問題 / 注意事項
這次是不是只影響 Vapor?
不是。雖然 changelog 裡有很多 runtime-vapor 條目,但 hydration / teleport / keep-alive 本身也有直接修補。你需要用「實際元件路徑」判斷風險,而不是只看功能標籤。
Nuxt 團隊一定要等 stable 嗎?
不一定。更準確的做法是「先驗證、再決策」:先在隔離分支跑回歸測試,用結果決定是否灰度,而不是用情緒判斷 beta。
Teleport 問題為什麼常在第二次操作才出現?
因為很多 bug 和 anchor 清理、目標切換、deferred mount/unmount 有關,第一次互動可能正常,第二次才暴露狀態殘留。
只靠手動測試可不可以?
可以做初步判斷,但不夠。這波修補多數是 edge case,最怕「偶發且不可重現」。至少要有可重跑的自動化測試才能做版本決策。
總結
Vue 3.6 beta.9 的價值,不在於讓你多寫幾行新語法,而是讓 SSR 專案在 Hydration、Teleport、KeepAlive 這三條最常出事故的路徑更穩。對台灣前端團隊來說,最務實的策略不是「馬上升」或「完全不看」,而是先把 changelog 轉成自己的測試矩陣。
你真正要帶回團隊的,不是一句「beta 先不要」,而是這三件事:
- 我們哪些頁面最容易被這波修補影響。
- 我們已經有沒有可重跑的回歸測試。
- 我們什麼條件下才會把 beta 送進 staging。
把這三件事做完,你就算今天不升,也是在替下一次升級省下真正的事故成本。
參考資料
- Vue Core Release
v3.6.0-beta.9: https://github.com/vuejs/core/releases/tag/v3.6.0-beta.9 - Vue Core
minorCHANGELOG: https://github.com/vuejs/core/blob/minor/CHANGELOG.md - npm
vuedownloads (last-week): https://api.npmjs.org/downloads/point/last-week/vue - npm
piniadownloads (last-week): https://api.npmjs.org/downloads/point/last-week/pinia - npm
nuxtdownloads (last-week): https://api.npmjs.org/downloads/point/last-week/nuxt