組合式 API 是一系列 API 的集合,使我們可以使用函數而不是聲明選項的方式書寫 Vue 元件。它是一個概括性的術語,涵蓋了以下方面的 API:
- 響應性 API:例如
和ref()
,使我們可以直接建立響應式狀态、計算屬性和偵聽器。reactive()
- 生命周期鈎子:例如
和onMounted()
,使我們可以在元件各個生命周期階段添加邏輯。onUnmounted()
- 依賴注入:例如
和provide()
,使我們可以在使用響應性 API 時,利用 Vue 的依賴注入系統。inject()
在 Vue 3 中,組合式 API 基本上都會配合
<script setup>
文法在單檔案元件中使用。下面是一個使用組合式 API 的元件示例:
<script setup>
import { ref, onMounted } from 'vue'
// 響應式狀态
const count = ref(0)
// 更改狀态、觸發更新的函數
function increment() {
count.value++
}
// 生命周期鈎子
onMounted(() => {
console.log(`計數器初始值為 ${count.value}。`)
})
</script>
<template>
<button @click="increment">點選了:{{ count }} 次</button>
</template>
雖然這套 API 的風格是基于函數的組合,但組合式 API 并不是函數式程式設計。組合式 API 是以 Vue 中資料可變的、細粒度的響應性系統為基礎的,而函數式程式設計更強調資料不可變。
為什麼要有組合式 API?
更好的邏輯複用
組合式 API 最基本的優勢是它使我們能夠通過組合函數來實作更加簡潔高效的邏輯複用。它解決了所有mixins的缺陷,那是選項式 API 中一種邏輯複用機制。
更靈活的代碼組織
許多使用者都喜歡選項式 API,因為在預設情況下就能夠寫出有組織的代碼:任何東西都有其對應的選項來管理。然而,選項式 API 在單個元件的邏輯複雜到一定程度時,也面臨了一些無法忽視的限制。這些限制主要展現在需要處理多個邏輯關注點的元件中,在許多 Vue 2 已經上線的生産應用中可以看到這一點。
我們以 Vue CLI GUI 中的檔案浏覽器元件為例:這個元件承擔了以下幾個邏輯關注點:
- 追蹤目前檔案夾的狀态,展示其内容
- 處理檔案夾的相關操作(打開、關閉和重新整理)
- 支援建立新檔案夾
- 可以切換到隻展示收藏的檔案夾
- 可以開啟對隐藏檔案夾的展示
- 處理目前工作目錄中的變更
這個元件最原始的版本是由選項式 API 寫成的。而如果用組合式 API 重構這個元件,将會變成:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iM3gDN4YTM0QGNjlTMkBjNzYzXwIDO1UTMxAzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
現在與同一個邏輯關注點相關的代碼被歸為了一組:我們無需再為了一個邏輯關注點在不同的選項塊間來復原動切換。此外,我們現在可以不費吹灰之力地将這一組代碼移動到一個外部檔案中,不再需要為了抽象而重新組織代碼,大大降低了重構成本,這在長期維護的大型項目中非常關鍵。
更好的類型推導
近幾年來,越來越多的開發者開始使用TypeScript書寫更健壯可靠的代碼,TypeScript還提供了非常好的IDE 開發支援。然而選項式 API 是在 2013
年建立的,那時并沒有想到需要進行類型推導。是以我們做了一些荒謬複雜的類型體操來實作對選項式 API 的類型推導。但盡管做了這麼多的努力,選項式 API 的類型推導仍然無法适配混入和依賴注入。
是以,很多想要搭配 TS 使用 Vue 的開發者采用了由vue-class-component提供的Class API。然而,基于 Class 的 API 非常依賴 ES 裝飾器,在 Vue 2019 年開發完成後,它仍是一個僅處于 vue 2的語言功能。我們認為将這樣一種不穩定的方案作為官方 API 的一種實作形式風險過高,在那之後裝飾器提案還進行了一些較大的變動,在書寫這篇文檔時仍未到達 vue 3。另外,基于 Class 的 API 和選項式 API 在邏輯複用和代碼組織方面有相同的限制。
相比之下,組合式 API 主要利用基本的變量和函數,它們本身就是類型友好的。用組合式 API 重寫的代碼可以享受到完整的類型推導,不需要書寫太多類型标注。大多數時候,用 TypeScript 書寫的組合式 API 代碼和用 JavaScript 寫都差不太多!這也同樣讓許多純 JavaScript 使用者能從IDE 中享受到部分類型推導功能。
生産包體積更小
搭配
<script setup>
使用組合式 API 比等價情況下的選項式 API 更高效,對代碼壓縮也更友好。這是由于
<script setup>
;形式書寫的元件模闆被編譯為了一個内連函數,和
<script setup>
中的代碼位于同一作用域。不像選項式 API 需要依賴
this
上下文對象通路屬性,被編譯的模闆可以直接通路
<script setup>
中定義的變量,無需一個代碼執行個體從中代理。這對代碼壓縮更友好,因為變量的名字可以變得更短,但對象的屬性名則不能。
與選項式 API 的關系
組合式 API 是否覆寫了所有場景?
對于有狀态的邏輯來說,的确如此。當使用組合式 API 時,隻需要用到一小部分選項:
props
,
emits
,
name
和
inheritAttrs
。如果使用
<script setup>
,那麼
inheritAttrs
應該是唯一一個需要用額外的
<script>
塊書寫的選項了。
如果你在代碼中隻使用了組合式 API(以及上述必需的選項),得益于編譯時标記你可以減小生産包大概幾 kb 左右的體積,因為丢掉了 Vue 之中關于選項式 API 的所有代碼。注意這也會影響你依賴中的 Vue 元件。
可以同時使用兩種 API 嗎?
可以。你可以在一個選項式 API 元件中使用
setup()
選項。