iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
Modern Web

Vue 演進驗證 × AI Coding 時代的工程實踐系列 第 6

Day 6:為什麼 Reactive 會變成 Hell?

  • 分享至 

  • xImage
  •  
明明只改了一個欄位,為什麼整個頁面卻開始忙起來了?

從今天開始,正式進入第一個 Runtime Pain Reactive Chain Hell ,它有趣的地方在 Reactive 一開始明明是為了讓程式更簡單,為什麼專案變大之後,反而越來越難理解?

剛開始真的很美好


剛開始寫 Vue,大概都是這種美好的感覺,只要改了 count,畫面自己更新,不用手動操作 DOM,也不用自己通知元件。

const count = ref(0)

const double = computed(() => count.value * 2)

關係純粹的美好

問題是,長期相處後愈來愈了解彼此,才會發現人會變, 大型專案最後通常不會停在原地。

當專案開始長大,Reactive 也跟著長大


正常來說,需求不會一次把整個系統設計完,通常都是...這裡再加一個 watch,那裡欄位要同步 URL,能不能搜尋時順便記錄 Analytics?甲方說會員價格要再套一層,天呀!好多需求!

每一次都很合理,對吧!於是原本的一條線,慢慢變成了一張網,隨著往愈來愈密,我都不知道我改了這個會不會影響那個?誰是我的依賴?

Reactive Chain 開始長大了

最常見的三種「變長」方式


其實大型 Vue 專案裡,Reactive Chain 很常從幾個地方開始。

1. Watch 越加越多

最開始可能只是 watch(keyword, search) ,後來需求變成:

keyword  ──→ search
category ──→ search
sort     ──→ search
page     ──→ search

filters  ──→ URL
filters  ──→ history
filters  ──→ analytics

每一條都沒有錯,但使用者只輸入一個字:

User Input
     │
     ▼
  keyword
     │
     ├──→ search
     ├──→ validation
     ├──→ URL
     └──→ analytics

放心!畫面永遠是對的,只是這時候應該需要關心的是 這一次更新,到底被多少條 Reactive Path 接住了?

2. Computed 一層一層往下疊

我們最常寫的是電商網站,想想看商場價格到年中慶,每個折扣的 computed 都很合理,但當 Chain 越來越深,問題就從 這個 computed 做什麼? 變成 一次更新需要穿過多少 Dependency?

對吧!?一下原價變成年終折扣,一下又考慮會員身分,最後又還有個滿額折扣,所以最後呢?

price
  ↓
discountPrice
  ↓
memberPrice
  ↓
displayPrice
  ↓
finalPrice

3. 表單開始互相影響

表單在大型後台是常客,讓我們一起來看,當一個欄位變更的時候,會出現什麼事?

Country
   │
   ▼
 City
   │
   ▼
District
   │
   ▼
Shipping Fee

改 Country:

清除 City
清除 District
重新取得 API

改 City:

重新取得 District

改 District:

重新計算 Shipping Fee

單看任何一個邏輯,都很合理,但把它們放在一起,問題不一定藏在某一個函式,而可能藏在「更新路徑」本身。

一個操作牽動很多

所以,問題真的在 Composition API 嗎?


我反而不這麼認為!如果真的是,這問題就不會在新公司還是遇到了,真的是走到哪都遇到!

Composition API 只是讓我們更容易組合:

  • ref
  • computed
  • watch
  • watchEffect
  • composable
  • component state

真正讓問題變複雜的,是它們之間形成的 Reactive Dependency Graph,可以把它想成一張城市交通網。

一條路不會有問題,十條路也不一定有問題,但當 State → Computed → Watch → Composable → Component → State ,開始互相連接之後,真正的成本就不只是「某個 API 執行多久」,而是

一次更新,究竟有多少 Dependency 被觸及?
就像重慶的黃桷灣立交橋,下錯一個交流道可能要再前行不知道幾公里才會到下一個交流道。
重庆立交桥夜景

圖片來源:搜狐 https://www.sohu.com/a/588089157_121174825

Reactive Hell 為什麼會變成 Runtime 問題?


這裡真的是今天最重要的轉折,因為 Reactive Chain 變複雜之後,可能增加的不是單一函式的時間,而是整個 Update Path 的工作量,於是出現:Input Lag、UI 更新延遲、CPU 使用率上升或是頻繁操作時卡頓,諸如此類的問題!就像人類工作量增加,就可能開始出現:焦躁、煩悶、執行率下降,甚至...想躺平!

尤其是這些情境 Input、Form Validation、Dashboard、Large List、Realtime Data,因為它們都具有一個共同特徵 State Update 發生得非常頻繁。

事情太多就會爆炸

這裡才出現一個值得驗證的問題


如果 Reactive Chain 真的會增加 Update Cost,那麼 Vue Runtime 的底層實作,應該有機會影響這個成本,然而這正好碰到 Vue 3.6 對 Reactivity Runtime 做了不少底層調整。

所以問題就變得很有意思,如果 Reactive Chain 變深、Dependency 變多時,Vue 3.6 真的能讓 Runtime Update 變便宜嗎? 還是,Benchmark 看起來變快,但真正的 Application Runtime Cost 並沒有改變?

這兩個答案,其實完全不同。

所以今天我們沒有跑版本比較,而是 把 Pain 定義清楚,才知道之後的 Benchmark 到底在量什麼。

Reactive Hell 的真正問題


Reactive 本身不是問題,computed 不是問題,watch 也不是問題!既然大家都不是問題,那真正的問題是什麼?

當 State、Computed、Effect、Composable 與 Component 不斷互相連接之後,Dependency Graph 開始失去可預測性。

一開始是 State → UI,最後:

             ┌── computed
             │
State ───────┼── watch
             │
             ├── composable
             │
             └── Component
                    │
                    ▼
                  State

這就是 Reactive Hell,透過這個流程我們真正想知道的 當 Reactive Chain 變深之後,Vue Runtime 到底要付出多少成本?

所以下一步把 Hell 變成可以測量的 Scenario,也可以讓「Reactive 很複雜」這句話,從感覺變成理性的 Dependency Chain 變深之後,Runtime Cost 到底怎麼變?


上一篇
Day 5:建立 Evidence Matrix:我要看什麼證據?
系列文
Vue 演進驗證 × AI Coding 時代的工程實踐6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言