15 分鐘掌握 vue 3.0 函數式 api

寫在前面

在分享 vue-next 各個子模塊的實現以前,我覺的有必要比較全面的整理下 vue-next 中提出的函數式 api,瞭解這些的話,不管是對於源碼的閱讀,仍是當正式版發佈時開始學習,應該都會有起到必定的輔助做用。vue

相似的東西在網上有不少,只是會比較零碎,同時有些也相對過期了,固然當前整理的這些也有可能會過期,畢竟代碼還處於 pre-alpha 的階段,但其中的設計思想應該是不會改變了。react

往期文章git

setup

setup 會做爲編寫組件業務邏輯的主戰場,各類 hook 類型的方法均須要在 setup 這個做用域下調用,直接來看 RFC 中的例子:github

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      double: computed(() => state.count \* 2)
    })

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    }
  }
}
</script>
複製代碼

其代碼結構看起來和 vue2 基本保持一致,不過有如下幾點須要注意:typescript

  • setup 自己的調用時機,從目前的源碼來看,介於 beforeCreatecreated 這兩個生命週期之間,言外之意,就是你沒法在這裏使用 this 指向當前組件實例
  • 對於 state 的聲明,咱們使用 reactive 這個 api 來構造,對於 computed state 的聲明,使用 computed,二者本質上均是 Proxy 對象
  • 對於組件方法的聲明,咱們直接經過聲明普通函數的方式進行聲明便可,對於 state 的變動,直接經過閉包使用 reactive 返回的 Proxy 對象便可
  • setup 的返回值被稱做 render context,顧名思義,就是模板中能夠訪問到的各類數據和方法的上下文對象,我在以前的文章中曾說起,模板在解析數據時,會優先考慮從 data 對象取值,以後就是這個 render context
  • 除了返回 render context,還能夠返回模板渲染函數,對,就是那個 h(...),這種形式對應的狀況是,當咱們沒有聲明 template 屬性時

在 vue-next 中,咱們直接從 vue 導入 reactivecomputed 以及其餘的 api 便可,若是在 vue2 版本上,咱們還能夠經過使用 composition-api 這個 plugin 來使用這些 apiapi

state

聲明 state 主要有如下幾種形式。數組

基礎類型

基礎類型能夠經過 ref 這個 api 來聲明,以下:bash

import { ref } from "vue";

export default {
setup() {
  const count = ref(0);
  
  function inc() {
    count.value++;
  }
  
  return { count, inc };
}
};

複製代碼

之因此要經過 ref,是由於 js 中的基礎類型傳值不是引用傳值,所以 vue-next 內部會自動將它封裝爲一個 ref 對象,其值指向原始值。固然,ref 指向引用類型也是沒有問題的,其 value 指向引用類型變量的引用。閉包

引用類型

引用類型除了可使用 ref 來聲明,也能夠直接使用 reactive,以下:函數

import { reactive } from "vue";

export default {
  setup() {
    const state = reactive({
        count: 0
    });
    
    function inc() {
      state.count++;
    }
    
    return { state, inc };
    // 或者經過 toRefs 方法
    // return { ...toRefs(state), inc };
  }
};
複製代碼
這裏使用 `toRefs` 的緣由主要是由於,若是是 reactive 產生的對象,因爲咱們要只是保存對於該 Proxy 對象的引用,咱們沒法使用解構來將它 `flat`,而 `toRefs` 會將每個屬性在內部包裹爲一個 `ref` 對象。
複製代碼

props

對於 prop 能夠經過以下代碼聲明及使用:

export default {
  props: {
    count: Number
  },
  setup(props) {
    watch(() => {
      console.log(\`count is: \` + props.count)
    })
  }
}
複製代碼

這裏能夠發現其實和 vue2 中聲明的方式基本同樣,但值得注意的是,在 vue-next 中,props 的類型聲明不是必須的,若是你使用 typescript,徹底能夠改寫爲以下的版本:

interface PropTypesI {
   count: number
}

export default {
  setup(props: PropTypesI) {
    watch(() => {
      console.log(\`count is: \` + props.count)
    })
  }
}
複製代碼

除此以外,還能夠直接經過 watch 方法來觀察某個 prop 的變更,這是爲何呢?答案很是簡單,就是 props 自己在源碼中,也是一個被 reactive 包裹後的對象,所以它具備響應性,因此在 watch 方法中的回調函數會自動收集依賴,以後當 count 變更時,會自動調用這些回調邏輯。

context

setup 那一小節中,咱們知道,setup 在調用時,組件實例還未建立,那意味着咱們沒法使用 this 訪問當前實例,那我想經過 thisvue2 中訪問一些內置屬性,怎麼辦?好比 attrs 或者 emit。咱們能夠經過 setup 的第二個參數:

setup(props, context) {
  do anything...
}
複製代碼

這個 context 對象也是一個 Proxy 對象,當咱們經過 context.attrs 訪問其屬性時,本質上代理對象會將訪問指向組件的內部實例(即之間文章中說起的 componentInternalInstance)。

生命週期

每個 vue2 中的組件生命週期函數,當前都對應一個生命週期 hook,好比:

import { onMounted, onUpdated, onUnmounted } from "vue";

setup() {
  onMounted(() => { ... });
  onUpdated(() => { ... });
  onUnmounted(() => { ... });
}
複製代碼

這裏值得注意的一點在於,對於 beforeCreatecreated 生命週期,雖然有響應的 hook,可是我覺的沒有必要單獨使用了,由於這些邏輯代碼大部分是一些初始化邏輯的代碼,直接寫在 setup 方法中便可。

如何複用代碼

在這個基礎上,複用代碼的方式也再也不像 vue2 中的那樣,經過 mixin 或者 HOC 來達到複用代碼的目的,這裏稍微說一下,這些複用代碼方式中比較顯著的缺點有:

  • 隱藏了數據來源,主要體如今 mixin
  • 會犧牲一些性能,主要體如今 HOC
  • 可能會遇到命名衝突問題,主要體如今 mixin

vue-next 中,複用代碼的邏輯本質上是利用這些 hook 來拆分業務邏輯與狀態,若是你比較熟悉 react hooks 的話,應該很快就能明白我指的是什麼意思。若是咱們將邏輯都寫在 setup 方法中,很快代碼就會變得難以維護,在這方面,咱們能夠將一些耦合在一塊兒的代碼抽離出來,同時以 use 前綴命名,聲明一個自定義的 hook,以下:

export default {
  setup() {
    const data = useSearch()
    const { sortedData, sort } = useSorting(data)
    return { data, sortedData, sort }
  }
}

function useSearch() { 
    ...fetch data
}

function useSort(data) { 
    ...sort data
}
複製代碼

除了以 inline 的方式來聲明,還能夠將這些自定義的 hook 聲明在單獨的文件中,直接經過 import 導入便可,好比:

import useSearch from '@hooks/search'
import useSort from '@hooks/sort'
複製代碼

react hooks 對比

vue-next 在這方面借鑑了 react hooks 的設計思想,可是從實現層來說,它們是不同的,主要有如下幾點:

  • vue-next 不依賴於其調用順序,而 react 依賴
  • vue-next 提供了生命週期方法,而 react 刻意模糊生命週期的概念
  • vue-next 基於響應式系統實現,意味它的依賴不須要顯示聲明(並且是自動的),而 react 須要手動聲明依賴數組

關注公衆號 全棧_101,只談技術,不談人生

相關文章
相關標籤/搜索