從 Component Tree 轉向 Reactive Graph,看看 Composable 擴張後到底增加了什麼成本?
前端開發很常遇到 Code Review 過程中被要求,這段邏輯可以抽出去,之後可能會共用。
於是原本寫在 Component 裡的幾十行程式碼,被整理成:
const search = useUserSearch(users)
過幾天需求增加:
const search = useUserSearch(users)
const pagination = usePagination()
const sorting = useSorting()
const selection = useSelection()
再繼續拆,可能會變成:
useUserPage()
├─ useUserSearch()
├─ usePagination()
├─ useSorting()
├─ useSelection()
└─ useLoading()
從程式碼結構來看,這通常更容易閱讀、測試,也更容易重用,但是 我們抽出去的只有程式碼,還是連 Runtime 要處理的 Reactive Dependency 也一起增加了?
先看一個很普通的搜尋功能:
function useUserSearch(users) {
const keyword = ref('')
const result = computed(() => {
return users.value.filter(user =>
user.name.includes(keyword.value)
)
})
watch(keyword, () => {
trackSearch(keyword.value)
})
return { keyword, result }
}
Component 只需要:
const { keyword, result } = useUserSearch(users)
但 Runtime 需要處理的結構已經包含:

因此 Composable 本身不是效能問題,真正值得測量的是:Composable 的組合,最後形成了多少 Reactive Dependency,以及一次 State Change 需要處理多少工作。
實務上很少只有一層,例如:
function useCounter() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
return { count, doubled }
}
function useCounterView() {
const counter = useCounter()
const label = computed(() =>
`Count: ${counter.doubled.value}`
)
return { ...counter, label }
}
function useCounterSummary() {
const view = useCounterView()
const summary = computed(() =>
`${view.label.value}`
)
return { summary }
}
呼叫端看起來只有:
const { summary } = useCounterSummary()
但實際上可能形成:

這就是 WP5 要研究的核心,我們需要把程式碼層級的 Composable 數量,轉換成 Runtime 可以觀察的成本。
前一個 WP4 的 Component Storm,主要觀察:
Component Tree
↓
Render / Update
↓
Runtime Cost
例如:
<UserPage>
<UserTable>
<UserRow />
<UserRow />
<UserRow />
</UserTable>
</UserPage>
問題是一次 State Change 之後,有多少 Component 需要重新執行 Render / Update?
Composable Explosion 不一樣,它不直接增加 DOM,也不會因為多了一個 useXXX() 就多一個 Component。
它主要增加的是:
Composable Composition
↓
Reactive Graph
↓
Dependency Propagation
↓
Runtime Work
所以這一次我們要看的,是 Reactive Architecture。
Composable 越多,不能直接推論 Runtime Cost 越高。
真正需要看的,是 Composable 背後形成的 Reactive Graph,以及 State Change 發生後需要處理的 Dependency。