@vue/composition-api速成課(通俗易懂版)
Composition API 將是 Vue 3 的核心功能,它具有許多更改和性能改進。我們也可以在 Vue 2 中通過 npm 插件@vue/composition-api 使用它。本人重點將帶你了解:
@vue/composition-api常見 api 使用vue3 代碼邏輯提取和復用 如何使用 provide+inject替代vuex方案
vue2 使用 composition-api
主文件 main.ts 或者 app.vue 添加
import?Vue?from?'vue'
import?VueCompositionAPI?from?'@vue/composition-api'
Vue.use(VueCompositionAPI)
Composition API 不再傳入 data、mounted 等參數(shù),
通過引入的 ref、onMounted等方法實現(xiàn)數(shù)據(jù)的雙向綁定、生命周期函數(shù)的執(zhí)行。
核心語法
reactive:接收一個普通對象然后返回該普通對象的響應式代理。
ref:接受一個參數(shù)值并返回一個響應式且可改變的 ref 對象。ref 對象擁有一個指向內(nèi)部值的單一屬性 .value。
computed:傳入一個 getter 函數(shù),返回一個默認不可手動修改的 ref 對象。
readonly:傳入一個對象(響應式或普通)或 ref,返回一個原始對象的只讀代理。一個只讀的代理是“深層的”,對象內(nèi)部任何嵌套的屬性也都是只讀的。
watchEffect:立即執(zhí)行傳入的一個函數(shù),并響應式追蹤其依賴,并在其依賴變更時重新運行該函數(shù)。可顯式的調(diào)用返回值以停止偵聽。
watch:全等效于 2.x this.\$watch (以及 watch 中相應的選項)。
setup 函數(shù)
現(xiàn)在要介紹的第一個 API 就是 setup 函數(shù)。setup 函數(shù)是一個新的組件選項。作為在組件內(nèi)使用 Composition API 的入口點。先看個簡單 demo
<template>
??<button?@click="increase">count?is:?{{?count?}}button>
template>
<script>
??export?default?{
????setup()?{
??????let?count?=?0
??????const?increase?=?()?=>?count++
??????return?{?count,?increase?}
????},
??}
script>
1、調(diào)用時機
創(chuàng)建組件實例,然后初始化 props ,緊接著就調(diào)用 setup 函數(shù)。從 vue2 生命周期鉤子的視角來看,它會在 beforeCreate 鉤子之后,created 之前被調(diào)用。
2、模板中使用
如果 setup 返回一個對象,則對象的屬性將會被合并到組件模板的渲染上下文。
3、渲染函數(shù) / JSX 中使用
setup 也可以返回一個函數(shù),函數(shù)中也能使用當前 setup 函數(shù)作用域中的響應式數(shù)據(jù):
import?{?h,?ref,?reactive?}?from?'@vue/composition-api'
export?default?{
??setup()?{
????const?count?=?ref(0)
????const?object?=?reactive({?foo:?'bar'?})
????return?()?=>?h('div',?[count.value,?object.foo])
??},
}
4、兩個參數(shù)props(注意 props 對象是響應式的),context(上下文對象,從原來 2.x 中 this 選擇性地暴露了一些 property。)
const?MyComponent?=?{
??setup(props,?context)?{
????let?{
??????attrs,
??????emit,
??????isServer,
??????listeners,
??????parent,
??????refs,
??????root,
??????slots,
??????ssrContext,
????}?=?context
??},
}
ref & reactive
在 App.vue 中,點擊事件綁定了 increase,然后修改了 count,
但是頁面并沒有發(fā)生改變,這是因為 setup 函數(shù)返回的對象中 count 不是響應式數(shù)據(jù),
那么如何創(chuàng)建響應式數(shù)據(jù)呢?此時就要掌握響應式系統(tǒng) API,我們可以使用 ref 和 reactive 創(chuàng)建。
<template>
??<button?@click="increase">
????count?is:?{{?count?}},?state.count?is?{{?state.count?}}
??button>
template>
<script>
??import?{?ref,?reactive?}?from?'vue'
??export?default?{
????setup()?{
??????let?count?=?ref(0)?//?{?value:?0?}
??????let?state?=?reactive({?number:?0?})
??????const?increase?=?()?=>?{
????????count.value++
????????state.count++
??????}
??????return?{?count,?state,?increase?}
????},
??}
script>
接受一個參數(shù)值并返回一個響應式且可改變的 ref 對象。ref 對象擁有一個指向內(nèi)部值的單一屬性 .value。
當 ref 作為渲染上下文的屬性返回(即在 setup() 返回的對象中)并在模板中使用時,
它會自動解套,無需在模板內(nèi)額外書寫 .value
Vue 本身已經(jīng)有 "ref" 的概念了。但只是為了在模板中獲取 DOM 元素或組件實例 (“模板引用”)。新的 ref 系統(tǒng)同時用于邏輯狀態(tài)和模板引用。
reactive 接收一個普通對象然后返回該普通對象的響應式代理。
響應式轉換是“深層的”:會影響對象內(nèi)部所有嵌套的屬性。基于 ES2015 的 Proxy 實現(xiàn),返回的代理對象不等于原始對象。建議僅使用代理對象而避免依賴原始對象。
不要解構返回的代理對象,那樣會使其失去響應性:
<template>
??<button?@click="increase">count?is:?{{?count?}}button>
template>
<script>
??import?{?ref,?reactive?}?from?'@vue/composition-api'
??export?default?{
????setup()?{
??????let?state?=?reactive({?count:?0?})
??????const?increase?=?()?=>?state.count++
??????return?{?...state,?increase?}?//?展開state屬性將失去響應式
????},
??}
script>
toRef 和 toRefs
那如果我們真的想展開 state 的屬性,在模板使用 count 而不是 state.count 的寫法那怎么辦呢?我們可以使用 toRef 和 toRefs 這兩個 API,進行轉換成 ref 對象,之前已經(jīng)介紹了 ref 對象是可以直接在模板中使用的。
toRef 可以用來為一個 reactive 對象的屬性創(chuàng)建一個 ref。這個 ref 可以被傳遞并且能夠保持響應性。
<template>
??<button?@click="increase">
????count?is:?{{?count?}},count2?is:?{{?count2?}}
??button>
template>
<script>
??import?{?ref,?reactive,?toRef,?toRefs?}?from?'@vue/composition-api'
??export?default?{
????setup()?{
??????let?state?=?reactive({?count:?0?})
??????let?countRef?=?toRef(state,?'count')
??????let?state2?=?reactive({?count2:?0?})
??????const?increase?=?()?=>?state.count++
??????let?stateAsRefs?=?toRefs(state2)
??????return?{?count:?countRef,?increase,?...stateAsRefs?}
????},
??}
script>
把一個響應式對象轉換成普通對象,該普通對象的每個 property 都是一個 ref ,和響應式對象 property 一一對應。
computed & watch
const?countDouble?=?computed(()?=>?count.value?*?2)
watch(
??()?=>?state.count,
??(count,?prevCount)?=>?{
????/*?...?*/
??}
)
代碼邏輯提取和復用
Composition API 的第一個明顯優(yōu)勢是很容易提取邏輯。解決了
邏輯提取
export?const?useCount?=?(number)?=>?{
??const?count?=?ref(0)
??const?increase?=?()?=>?{
????count.value?+=?1
??}
??const?reset?=?()?=>?{
????count.value?=?0
??}
??onMounted(()?=>?{
????count.value?=?number
??})
??return?{
????count,
????increase,
????reset,
??}
}
代碼復用
//?另外一個文件使用:
const?{?count,?increase?}?=?useCount(1)
console.log(count)?//輸出1
increase()
console.log(count)?//輸出2
reset()
console.log(count)?//輸出0
有效的解決了 mixins 復用命名沖突,難以識別命名來自哪個 mixin 文件的問題。
替代 vuex 狀態(tài)管理
狀態(tài) store 可以放在一個單一的文件或者目錄里,比如設置一個全局組件可以只用的配置 config
//context/config.ts
import?{?provide,?inject,?ref,?onMounted,?readonly?}?from?'@vue/composition-api'
const?configSymbol:?symbol?=?Symbol()
export?const?useProvider?=?{
??setup()?{
????let?config?=?ref(null)
????const?configServer?=?async?()?=>?{
??????//?await?一些異步操作,比如api等
??????config.value?=?{?name:?'名字'?}
????}
????onMounted(async?()?=>?{
??????await?configServer()
????})
????provide(configSymbol,?{
??????//導出只讀的config只有函數(shù)內(nèi)部可以修改狀態(tài)
??????config:?readonly(config),
????})
??},
}
export?const?useInject?=?()?=>?{
??return?inject(configSymbol)
}
在最頂層的組件(例如 main.ts)上注入,config 就可以在所有的組件中使用
import?{?defineComponent?}?from?'@vue/composition-api'
import?{?useProvider?}?from?'./context/config'
export?default?defineComponent({
??setup()?{
????useProvider()
??},
})
業(yè)務邏輯頁面使用 config
import?{?useInject?}?from?'./context/config'
const?Components?=?{
??setup()?{
????const?{?config?}?=?useInject()
????console.log(config.value.name)?//輸出“名字”
????return?{
??????config,
????}
??},
}
