首頁>技術>

雖然 Vue3.0 尚未釋出,但前段時間,Vue 釋出了關於 Composition API 的官方外掛,使廣大使用者可以在 Vue2.x 中享受 Function Base 帶來的新體驗。本文作者通過實際試用,對 Composition API 與當前的 Options API 作出了一番對比,並對 Composition API 的特徵與用途進行了通俗易懂的總結。一起來看看!

我最近得到了機會,在一個真實的專案中試用了 Vue 中全新的 Composition API ,進而對它可能的用途以及未來的用法探索了一番。

現在,當我們建立一個新元件時使用的是 Options API 。使用這個 API 時,我們必須通過選項將元件的程式碼分離開來,這意味著我們需要將所有響應性資料放在一個地方,所有計算的屬性放在一個位置,所有方法也都放在一個位置,依此類推。

這個 API 在處理較小的元件時比較易讀和順手,而當元件變得更加複雜,需要處理多種功能時,它用起來就很痛苦了。一般來說,與一個特定功能相關的邏輯會包含一些響應性資料、一些計算屬性,還有一種或一些方法。有時還會用到元件生命週期 hooks。於是在處理單個邏輯問題時,需要不斷在程式碼中的不同選項之間來回切換。

使用 Vue 時,可能遇到的另一個問題是設法提取可被多個元件複用的通用邏輯。Vue 已經提供了一些方案可供選擇,但它們都有各自的缺點(例如 mixins 和作用域插槽)。而新的 Composition API 帶來了一種建立元件、分離程式碼和提取可複用程式碼段的全新方式。

首先來看元件內的程式碼構成。

程式碼構成

假設你有一個核心元件,為整個 Vue 應用設定了一些內容(就像 Nuxt 中的佈局)。它負責處理以下內容:

設定區域;檢查使用者是否處於登入狀態,如果沒有,則將其重定向;防止使用者重新載入應用程式太多次數;跟蹤使用者活動,並在使用者靜默一段時間後做出反應;使用 EventBus 監聽事件(或視窗物件事件)。

這些只是這個元件可以做的事情的一些例子。你可能會設想出一個更復雜的元件,不過這裡的這些已經足夠本文舉例說明了。為了便於閱讀,我只用了 props 的名稱,而沒有實際實現。

下面是使用 Options API 時元件的樣子:

<template>  <div id="app">    ...  </div></template><script>export default {  name: 'App',  data() {    return {      userActivityTimeout: null,      lastUserActivityAt: null,      reloadCount: 0    }  },  computed: {    isAuthenticated() {...}    locale() {...}  },  watch: {    locale(value) {...},    isAuthenticated(value) {...}  },  async created() {    const initialLocale = localStorage.getItem('locale')    await this.loadLocaleAsync(initialLocale)  },  mounted() {    EventBus.$on(MY_EVENT, this.handleMyEvent)    this.setReloadCount()    this.blockReload()    this.activateActivityTracker()    this.resetActivityTimeout()  },  beforeDestroy() {    this.deactivateActivityTracker()    clearTimeout(this.userActivityTimeout)    EventBus.$off(MY_EVENT, this.handleMyEvent)  },  methods: {    activateActivityTracker() {...},    blockReload() {...},    deactivateActivityTracker() {...},    handleMyEvent() {...},    async loadLocaleAsync(selectedLocale) {...}    redirectUser() {...}    resetActivityTimeout() {...},    setI18nLocale(locale) {...},    setReloadCount() {...},    userActivityThrottler() {...},  }}</script>

由此可見,解開這團亂麻有多複雜。

現在假設你需要更改一種功能(例如活動追蹤邏輯)。你不僅需要知道有哪些元素與該邏輯相關,而且就算你知道了,也需要在不同的元件選項之間跳來跳去。

下面我們使用 Composition API,通過邏輯關注點來分離程式碼。為此,我們為每個與特定功能相關的邏輯建立一個函式。這就是我們所說的 composition 函式。

// Activity tracking logicfunction useActivityTracker() {  const userActivityTimeout = ref(null)  const lastUserActivityAt = ref(null)  function activateActivityTracker() {...}  function deactivateActivityTracker() {...}  function resetActivityTimeout() {...}  function userActivityThrottler() {...}  onBeforeMount(() => {    activateActivityTracker()    resetActivityTimeout()  })  onUnmounted(() => {    deactivateActivityTracker()    clearTimeout(userActivityTimeout.value)  })}
// Reload blocking logicfunction useReloadBlocker(context) {  const reloadCount = ref(null)  function blockReload() {...}  function setReloadCount() {...}  onMounted(() => {    setReloadCount()    blockReload()  })}
// Locale logicfunction useLocale(context) {  async function loadLocaleAsync(selectedLocale) {...}  function setI18nLocale(locale) {...}  watch(() => {    const locale = ...    loadLocaleAsync(locale)  })  // No need for a 'created' hook, all logic that runs in setup function is placed between beforeCreate and created hooks  const initialLocale = localStorage.getItem('locale')  loadLocaleAsync(initialLocale)}

如你所見,我們可以宣告響應性資料(ref/reactive)、計算的 props、方法(純函式)、觀察者(watch)和生命週期 hooks(onMounted/onUnmount)。基本上你平時在元件中使用的所有內容都能宣告。

關於儲存程式碼的位置,我們有兩個選擇。我們可以將其保留在元件中,或提取到單獨的檔案中。由於 Composition API 尚未正式釋出,因此還沒有關於如何使用它的最佳實踐或規則。我的看法是,如果邏輯與特定元件緊密耦合(即不會在其他任何地方複用),並且邏輯離開了元件就無法生存,我建議將其保留在元件中。另一方面,如果是可能會被複用的一般性功能,則建議將其提取到單獨的檔案中。但如果我們要將其儲存在單獨的檔案中,則需要從檔案中匯出函式並將其匯入到元件中。

這是使用新建立的 composition 函式後,我們元件的樣子:

<template>  <div id="app">        </div></template><script>export default {  name: 'App',  setup(props, context) {    useEventBusListener(MY_EVENT, handleMyEvent)    useActivityTracker()    useReloadBlocker(context)    useLocale(context)    const isAuthenticated = computed(() => ...)    watch(() => {      if (!isAuthenticated) {...}    })    function handleMyEvent() {...},    function useLocale() {...}    function useActivityTracker() {...}    function useEventBusListener() {...}    function useReloadBlocker() {...}  }}</script>

這裡每個邏輯關注點都有了一個函式。如果要使用某一個關注點,則需要在新的 setup 函式中呼叫相關的 composition 函式。

再設想一下,你需要對活動跟蹤邏輯作一些更改。與該功能相關的所有內容都放在 useActivityTracker 函式中。現在你就能立刻找出並跳轉到正確的位置,檢視所有相關的程式碼段了。非常漂亮!

提取可複用的程式碼段

在我們的例子中,事件匯流排偵聽器註冊(Event Bus listener registrations)看來是一段程式碼,如果有元件需要偵聽事件總線上的事件,我們就可以用這段程式碼來實現。

如前所述,我們可以將與特定功能相關的邏輯儲存在單獨的檔案中。下面我們將事件匯流排偵聽器設定轉移到一個單獨的檔案中。

// composables/useEventBusListener.jsimport EventBus from '@/event-bus'export function useEventBusListener(eventName, handler) {  onMounted(() => EventBus.$on(eventName, handler))  onUnmounted(() => EventBus.$off(eventName, handler))}

要在元件中使用它,我們需要匯出函式(命名或預設),並將其匯入元件中。

<template>  <div id="app">    ...  </div></template><script>import { useEventBusListener } from '@/composables/useEventBusListener'export default {  name: 'MyComponent',  setup(props, context) {    useEventBusListener(MY_EVENT, myEventHandled)    useEventBusListener(ANOTHER_EVENT, myAnotherHandled)  }}</script>

完事了!現在我們能在任何元件中使用它了。

小 結

關於 Composition API 的討論還在進行中。這篇文章無意在討論中站隊,更關心的是新 API 可能的用途,以及它能在哪些情況下帶來附加價值。

我認為在現實案例中理解概念總是比較容易的,就像上面這個例子一樣。用例越多,使用新 API 的次數越多,我們也就能發現更多的模式。這篇文章只涉及了一些基本的模式,供拋磚引玉。

我們再來看一遍上面這些用例,看看 Composition API 的用途有哪些:

1. 無需與任何特定元件緊密耦合也能獨立執行的一般性功能

與一個特定功能相關的所有邏輯都放在一個檔案中;將其儲存在 @/composables/*.js,並將其匯入元件中;示例:活動跟蹤、阻止重新載入和區域設定。

2. 可在多個元件中使用的可複用功能

與一個特定功能相關的所有邏輯都放在一個檔案中;將其儲存在 @/composables/*.js,並將其匯入元件中;示例:事件匯流排偵聽器註冊、視窗事件註冊、通用動畫邏輯、通用庫的使用。

3. 元件內的程式碼組織

與一個特定功能相關的所有邏輯都放在一個函式中;將程式碼保留在元件內的 composition 函式中;與同一邏輯關注點相關的程式碼位於同一位置。也就是說,無需在資料、計算屬性、方法、生命週期 hooks 等內容之間來回跳轉)。記住:這些都尚在開發中!

Vue Composition API 目前尚處於開發階段,未來還可能出現更改。上面示例中提到的任何內容、語法和用例都可能出現變化。這個 API 計劃將隨 Vue 3.0 一起推出。另外,你可以在 view-use-web 上檢視一組 composition 函式的資訊,這些函式預計會包含在 Vue 3 中,但也能用在 Vue 2 中的 Composition API 上。

如果你想嘗試新的 API,可以使用 @vue/composition 庫。

82
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Golang——詳解Go語言的程式碼規範