如果 Reactive Hell 只是一種「感覺」,那我們永遠只能說它很慢;只有把它變成可重複的實驗,才有辦法知道到底慢在哪裡。
前一天,我們把大型 Vue 專案裡常見的 Reactive Chain 拆開來看:
User Input
↓
State Update
↓
Computed
↓
Watch
↓
Derived State
↓
Component Render
↓
DOM Update
真正讓人頭痛的是一次次發生的問題,尤其是 很小的 State Update,最後可能牽動一整條 Reactive Chain。
問題來了,如果今天拿一個 Demo 跑一次 Performance,看到 Vue 3.6 好像快了 15%,這個數字真的能代表 Vue Runtime 變快了嗎?你們買單嗎?
其實我不太敢確定,因為我不知道:
所以今天要 先把「Reactive Hell」變成一個公平的實驗。
一開始只有大腦有很多想法,但是不知道怎麼架構才叫夠乾淨?於是,我和各家 AI 分享自己想法,然後請他們先建立一條 固定 的 Reactive Dependency Graph。
例如:
Form State
↓
Computed #1
↓
Computed #2
↓
Computed #3
↓
Validation
↓
Display State
↓
Component Render
現在我們故意把 Dependency Chain 拉長,才能問一個很單純的問題:
當 Reactive Dependency 變深時,Runtime Update Cost 會怎麼變?
這時候,Reactive Hell 就從一個形容詞變成一個 可以控制的變數。
一直以來,我們的目標都是建立一個固定的 Scenario:
Reactive Chain Scenario
┌──────────────────────────────┐
│ Source State │
└──────────────┬───────────────┘
↓
Computed Chain
↓
Validation State
↓
Display State
↓
Component Render
↓
Evidence
之後不論測的是什麼,我們只替換架構版本。
就像這次的旅程只有做 Vue 3.5 → Vue 3.6,其他條件盡可能維持一致,這樣比較的才是 Framework Version 的差異,結論也比較公平公正。
Reactive Chain 最直觀的變化,就是深度!
State
↓
Computed
↓
Render
State
↓
Computed
↓
Computed
↓
Computed
↓
Computed
↓
Computed
↓
Render
State
↓
Computed
↓
Computed
↓
Computed
↓
⋮
↓
Computed
↓
Render
這時候我們可以問自己:Depth 越深,Update Cost 是否真的越高?
Reactive Hell 也不只是「鏈很長」,同一條 Dependency Chain 如果只更新一次,可能完全沒有壓力。
State
↓
Computed
↓
Computed
↓
Render
但如果變成每次更新次數不同,情況可能會出現完全不同的結果。
10 updates / sec
100 updates / sec
1000 updates / sec
所以我們把 Scenario 拆成幾個可以控制的維度:
| 維度 | 我們想知道什麼? |
|---|---|
| Dependency Depth | Chain 越深,Update Cost 是否增加? |
| Update Frequency | 更新越頻繁,Runtime Cost 如何累積? |
| State Complexity | Reactive State 越複雜,Tracking Cost 是否改變? |
| Render Count | 一次 Update 最後造成多少 Render? |
這樣「Reactive Hell」才真正變成一個可以調整強度的實驗。
前面一直強調實驗一定要固定條件只有一個變數,假設今天設定 Vue 3.5 框架且使用 Depth = 5 讓他 Update 100 次,跑完得到一組數據。
然後在 Vue 3.6 改成 Depth = 10,一樣更新 100 次,最後 Vue 3.6 比較慢。
這個結果有意義嗎?
Vue 版本改變 Dependency Chain 也在變,我們根本不知道到底是誰影響了誰造就今天的結果,所以在 Vue Pain Lab 裡,我們開始建立一個非常簡單的規則 版本可以變,實驗不能跟著變。
例如固定:
Scenario
├─ Component Structure
├─ Dependency Depth
├─ Computed Count
├─ Watch Count
├─ Update Count
├─ Browser
└─ Hardware
↓
Only change
↓
Vue Version
討論後開始寫 Prompt 請夥伴幫忙寫出可以重複使用的假設情境,我給 AI 的情境要求其實很簡單:
建立可調整的 Reactive Chain
DEPTH = 5
↓
自動建立 computed chain
↓
watch / watchEffect
↓
Component Render
↓
收集 Metrics
並且特別限制以下條件:
也為了日後固定所有規則,所以也有把條件放在 skills (https://github.com/MengtingKu/vue-pain-lab/blob/main/.claude/skills/create-pain-scenario/SKILL.md)
實際程式最後被拆成三個責任:
┌─────────────────────────┐
│ ReactiveChainPage.vue │
│ │
│ 控制 Scenario / UI │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ createReactiveChain.ts │
│ │
│ 建立 Reactive Graph │
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ metrics.ts │
│ │
│ 收集 Runtime Evidence │
└─────────────────────────┘
這樣做的目的,不是為了把程式拆得很漂亮,是讓之後的 Lab 可以沿用同一套思路,Scenario 可以換,Validation 方法不要一直重做!
Reactive Chain
│
├── Component Storm
├── Composable Chaos
└── VDOM Stress Test
今天我們建立的是「尺」,下一篇開始,才拿這把尺把「現在的成本」量清楚,切換版本後才知道 升級之後到底走了多遠。