在分享 vue-next
各個子模塊的實現以前,我覺的有必要比較全面的整理下 vue-next
中提出的函數式 api
,瞭解這些的話,不管是對於源碼的閱讀,仍是當正式版發佈時開始學習,應該都會有起到必定的輔助做用。vue
相似的東西在網上有不少,只是會比較零碎,同時有些也相對過期了,固然當前整理的這些也有可能會過期,畢竟代碼還處於 pre-alpha
的階段,但其中的設計思想應該是不會改變了。react
往期文章git
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
自己的調用時機,從目前的源碼來看,介於 beforeCreate
和 created
這兩個生命週期之間,言外之意,就是你沒法在這裏使用 this
指向當前組件實例state
的聲明,咱們使用 reactive
這個 api
來構造,對於 computed state
的聲明,使用 computed
,二者本質上均是 Proxy
對象state
的變動,直接經過閉包使用 reactive
返回的 Proxy
對象便可setup
的返回值被稱做 render context
,顧名思義,就是模板中能夠訪問到的各類數據和方法的上下文對象,我在以前的文章中曾說起,模板在解析數據時,會優先考慮從 data
對象取值,以後就是這個 render context
了render context
,還能夠返回模板渲染函數,對,就是那個 h(...)
,這種形式對應的狀況是,當咱們沒有聲明 template
屬性時在 vue-next 中,咱們直接從 vue
導入 reactive
、computed
以及其餘的 api
便可,若是在 vue2
版本上,咱們還能夠經過使用 composition-api 這個 plugin
來使用這些 api
。api
聲明 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` 對象。
複製代碼
對於 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
變更時,會自動調用這些回調邏輯。
在 setup
那一小節中,咱們知道,setup
在調用時,組件實例還未建立,那意味着咱們沒法使用 this
訪問當前實例,那我想經過 this
在 vue2
中訪問一些內置屬性,怎麼辦?好比 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(() => { ... });
}
複製代碼
這裏值得注意的一點在於,對於 beforeCreate
和 created
生命週期,雖然有響應的 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,只談技術,不談人生