前言
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 等引數, 通過引入的 ref、onMounted等方法實現資料的雙向繫結、生命週期函式的執行。
核心語法reactive:接收一個普通物件然後返回該普通物件的響應式代理。
ref:接受一個引數值並返回一個響應式且可改變的 ref 物件。ref 物件擁有一個指向內部值的單一屬性 .value。
computed:傳入一個 getter 函式,返回一個預設不可手動修改的 ref 物件。
readonly:傳入一個物件(響應式或普通)或 ref,返回一個原始物件的只讀代理。一個只讀的代理是“深層的”,物件內部任何巢狀的屬性也都是隻讀的。
watchEffect:立即執行傳入的一個函式,並響應式追蹤其依賴,並在其依賴變更時重新執行該函式。可顯式的呼叫返回值以停止偵聽。
watch:全等效於 2.x this.\\$watch (以及 watch 中相應的選項)。
setup 函式現在要介紹的第一個 API 就是 setup 函式。setup 函式是一個新的元件選項。作為在元件內使用 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、呼叫時機
建立元件例項,然後初始化 props ,緊接著就呼叫 setup 函式。從 vue2 生命週期鉤子的視角來看,它會在 beforeCreate 鉤子之後,created 之前被呼叫。
2、模板中使用
如果 setup 返回一個物件,則物件的屬性將會被合併到元件模板的渲染上下文。
3、渲染函式 / JSX 中使用
setup 也可以返回一個函式,函式中也能使用當前 setup 函式作用域中的響應式資料:
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、兩個引數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, 但是頁面並沒有發生改變,這是因為 setup 函式返回的物件中 count 不是響應式資料, 那麼如何建立響應式資料呢?此時就要掌握響應式系統 API,我們可以使用 ref 和 reactive 建立。
<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>
接受一個引數值並返回一個響應式且可改變的 ref 物件。ref 物件擁有一個指向內部值的單一屬性 .value。
當 ref 作為渲染上下文的屬性返回(即在 setup() 返回的物件中)並在模板中使用時, 它會自動解套,無需在模板內額外書寫 .value
Vue 本身已經有 "ref" 的概念了。但只是為了在模板中獲取 DOM 元素或元件例項 (“模板引用”)。新的 ref 系統同時用於邏輯狀態和模板引用。
reactive 接收一個普通物件然後返回該普通物件的響應式代理。
響應式轉換是“深層的”:會影響物件內部所有巢狀的屬性。基於 ES2015 的 Proxy 實現,返回的代理物件不等於原始物件。建議僅使用代理物件而避免依賴原始物件。
不要解構返回的代理物件,那樣會使其失去響應性:
<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 物件,之前已經介紹了 ref 物件是可以直接在模板中使用的。
toRef 可以用來為一個 reactive 物件的屬性建立一個 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 & watchconst countDouble = computed(() => count.value * 2)watch( () => state.count, (count, prevCount) => { /* ... */ })
程式碼邏輯提取和複用
Composition API 的第一個明顯優勢是很容易提取邏輯。解決了
邏輯提取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) //輸出1increase()console.log(count) //輸出2reset()console.log(count) //輸出0
有效的解決了 mixins 複用命名衝突,難以識別命名來自哪個 mixin 檔案的問題。
替代 vuex 狀態管理狀態 store 可以放在一個單一的檔案或者目錄裡,比如設定一個全域性元件可以只用的配置 config
//context/config.tsimport { 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只有函式內部可以修改狀態 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() },})
業務邏輯頁面使用 config
import { useInject } from './context/config'const Components = { setup() { const { config } = useInject() console.log(config.value.name) //輸出“名字” return { config, } },}