Vue 3.0 最新進展,Composition API

本文主要分以下幾個主題討論最新的Composition API:

  • reactive API
  • ref API
  • watch API變化
  • computed API變化
  • 生命周期鉤子變化
  • TypeScript和JSX支持

Composition API 可謂是修復(fù)了 Function API 諸多問題而提供的最新“修正案”,下面來看比起之前的vue-function-api,究竟修改了些什么呢?

state更名為reactive

vue-function-api中,通過state創(chuàng)建響應(yīng)式對象,這個state創(chuàng)建的響應(yīng)式對象并不是包裝對象,不需要使用.value來取值。但問題在于:state通常會被用作描述 Vue 組件狀態(tài)對象的變量名,容易對開發(fā)者造成誤導(dǎo),Vue官方團隊認為將state API 更名為reactive更為優(yōu)雅,reactive等價于 Vue 2.x 的Vue.observable(),用于獲取一個對象的響應(yīng)性代理對象:

const obj = reactive({ count: 0 });
復(fù)制代碼

value更名為ref,并提供isRef和toRefs

vue-function-api中,通過value函數(shù)創(chuàng)建一個包裝對象,它包含一個響應(yīng)式屬性value。在 Vue 官方團隊充分采用社區(qū)意見后,將這個API更改為ref。ref用創(chuàng)建一個包裝對象,只具備一個響應(yīng)式屬性value,如果將對象指定為ref的值,該對象將被reactive方法深度遍歷。要知道, Composition API 之所以被提出和使用,就是為了讓我們更加方便地進行組件復(fù)用,將狀態(tài)經(jīng)過函數(shù)式地傳遞過程中,由于JavaScript函數(shù)傳參是值傳遞的,而基本類型不具備引用,為了保證屬性的響應(yīng)式,將使用ref來創(chuàng)建包裝對象進行傳遞。

const count = ref(0);
console.log(count.value); // 0

count.value++;
console.log(count.value); // 1
復(fù)制代碼

提供isRef,用于檢查一個對象是否是ref對象:

const unwrapped = isRef(foo) ? foo.value : foo;
復(fù)制代碼

如果讀者你看到這里,可能就會比較疑惑了,究竟什么時候該使用ref,什么時候該使用reactive呢?其使用場景其實與我們所習(xí)慣的編碼風(fēng)格密切相關(guān),通過下面的例子,我們能更好理解使用refreactive的區(qū)別:

// 風(fēng)格一:通過基本類型變量來聲明狀態(tài)
let x = 0;
let y = 0;

function updatePosition(e) {
 x = e.pageX;
 y = e.pageY;
}

// --- compared to ---

// 風(fēng)格二:通過單一對象來聲明狀態(tài)
const pos = {
 x: 0,
 y: 0,
};

function updatePosition(e) {
 pos.x = e.pageX;
 pos.y = e.pageY;
}
復(fù)制代碼

如果開發(fā)者習(xí)慣風(fēng)格一的寫法,通常通過ref將基本類型的變量轉(zhuǎn)化為響應(yīng)式包裝對象來使其具備響應(yīng)式,而如果是風(fēng)格二的話,只需要創(chuàng)建reactive對象。

然而,思考下面的場景:

function useMousePosition() {
 const pos = reactive({
 x: 0,
 y: 0,
 });

 // ...
 return pos;
}

// consuming component
export default {
 setup() {
 // 對象解構(gòu)將會導(dǎo)致響應(yīng)式會被丟失
 const { x, y } = useMousePosition();
 return {
 x,
 y,
 };

 // 拓展運算符將導(dǎo)致響應(yīng)式丟失
 return {
 ...useMousePosition()
 }

 // 只有這樣才能保證響應(yīng)式不被丟失
 // 通過pos.x的pos.y來取值才會保留x,y的響應(yīng)式
 return {
 pos: useMousePosition()
 }
 }
};
復(fù)制代碼

通過上述的例子,要知道,我們沒有辦法通過編碼風(fēng)格的限制來保證通過組合函數(shù)返回的響應(yīng)式狀態(tài)不被丟失,Vue官方團隊建議在組合函數(shù)中都通過返回ref對象來規(guī)避這一類問題,toRef便是做這一件事情的最好方式:

function useMousePosition() {
 const pos = reactive({
 x: 0,
 y: 0
 });

 // ...
 return toRefs(pos);
}

// x 和 y 現(xiàn)在具備了響應(yīng)式
const { x, y } = useMousePosition();
復(fù)制代碼

toRefsreactive對象轉(zhuǎn)換為普通對象,其中結(jié)果對象上的每個屬性都是指向原始對象中相應(yīng)屬性的ref引用對象,這在組合函數(shù)返回響應(yīng)式狀態(tài)時非常有用,這樣保證了開發(fā)者使用對象解構(gòu)或拓展運算符不會丟失原有響應(yīng)式對象的響應(yīng)。

watch可作用于單一函數(shù)

比起上一篇文章中介紹的watch API 的傳參方式,最新的@vue/composition-api修正案中,watch的傳遞方式可以收斂為單一函數(shù),Vue 3.x 將會在其依賴的響應(yīng)式狀態(tài)改變是執(zhí)行watch的回調(diào)函數(shù):

const count = ref(0);

watch(() => console.log(count.value)); // 打印0

setTimeout(() => {
 count.value++; // 打印1
}, 100);
復(fù)制代碼

computed可傳入getset,用于定義可更改的計算屬性

基本示例如下所示,與 Vue 2.x 類似的,可以定義可更改的計算屬性。

const count = ref(1);
const plusOne = computed({
 get: () => count.value + 1,
 set: val => { count.value = val - 1 }
});

plusOne.value = 1;
console.log(count.value); // 0
復(fù)制代碼

生命周期鉤子

比起vue-function-api@vue/composition-api刪除了onBeforeCreateonCreated。因為setup總是在創(chuàng)建組件實例時調(diào)用,即onBeforeCreate之后和onCreated之前調(diào)用,因此onBeforeCreateonCreated將可以使用setup進行代替。

使用TypeScript和JSX

setup現(xiàn)在支持返回一個渲染函數(shù),這個函數(shù)返回一個JSX,JSX可以直接使用聲明在setup作用域的響應(yīng)式狀態(tài):

export default {
 setup() {
 const count = ref(0);
 return () => (<div>{count.value}</div>);
 },
};
復(fù)制代碼

注:如果使用TypeScript,同時希望使用需要在JSX命名空間內(nèi)聲明以下interface

// file: shim-tsx.d.ts
import Vue, { VNode } from 'vue';
import { ComponentRenderProxy } from '@vue/composition-api';

declare global {
 namespace JSX {
 // tslint:disable no-empty-interface
 interface Element extends VNode { }
 // tslint:disable no-empty-interface
 interface ElementClass extends ComponentRenderProxy { }
 interface ElementAttributesProperty {
 $props: any; // specify the property name to use
 }
 interface IntrinsicElements {
 [elem: string]: any;
 }
 }
}
復(fù)制代碼

此外,為了更好地配合 TypeScript 進行類型推斷,Vue Composition API 推薦使用createComponent來定義一個組件,以便于Vue進行類型推導(dǎo):

import { createComponent } from 'vue';

export default createComponent({
 props: {
 foo: String,
 },
 setup(props) {
 console.log(props.foo);
 },
});
復(fù)制代碼

小結(jié)

本文是筆者上一篇文章Vue 3.0 前瞻,體驗 Vue Function API的續(xù)篇,主要描述 Vue Composition API 對比 之前的草案 Vue Function API 的變化,可以看到Vue 官方針對社區(qū)建議修改了 Vue Function API 草案的諸多問題。下一篇文章中,筆者帶來 Vue Composition API 的響應(yīng)式對象原理解讀,在解讀學(xué)習(xí)過程中,加深對 Vue Composition API 的理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容