Vue 3.0 最新进展,Composition API

本文主要分以下几个主题讨论最新的Composition API:

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

Composition API 可谓是修复了 Function API 诸多问题而提供的最新“修正案”,下面来看比起之前的vue-function-api,究竟修改了些什么呢?

state更名为reactive

vue-function-api中,通过state创建响应式对象,这个state创建的响应式对象并不是包装对象,不需要使用.value来取值。但问题在于:state通常会被用作描述 Vue 组件状态对象的变量名,容易对开发者造成误导,Vue官方团队认为将state API 更名为reactive更为优雅,reactive等价于 Vue 2.x 的Vue.observable(),用于获取一个对象的响应性代理对象:

const obj = reactive({ count: 0 });
复制代码

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

vue-function-api中,通过value函数创建一个包装对象,它包含一个响应式属性value。在 Vue 官方团队充分采用社区意见后,将这个API更改为refref用创建一个包装对象,只具备一个响应式属性value,如果将对象指定为ref的值,该对象将被reactive方法深度遍历。要知道, Composition API 之所以被提出和使用,就是为了让我们更加方便地进行组件复用,将状态经过函数式地传递过程中,由于JavaScript函数传参是值传递的,而基本类型不具备引用,为了保证属性的响应式,将使用ref来创建包装对象进行传递。

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

count.value++;
console.log(count.value); // 1
复制代码

提供isRef,用于检查一个对象是否是ref对象:

const unwrapped = isRef(foo) ? foo.value : foo;
复制代码

如果读者你看到这里,可能就会比较疑惑了,究竟什么时候该使用ref,什么时候该使用reactive呢?其使用场景其实与我们所习惯的编码风格密切相关,通过下面的例子,我们能更好理解使用refreactive的区别:

// 风格一:通过基本类型变量来声明状态
let x = 0;
let y = 0;

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

// --- compared to ---

// 风格二:通过单一对象来声明状态
const pos = {
 x: 0,
 y: 0,
};

function updatePosition(e) {
 pos.x = e.pageX;
 pos.y = e.pageY;
}
复制代码

如果开发者习惯风格一的写法,通常通过ref将基本类型的变量转化为响应式包装对象来使其具备响应式,而如果是风格二的话,只需要创建reactive对象。

然而,思考下面的场景:

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

 // ...
 return pos;
}

// consuming component
export default {
 setup() {
 // 对象解构将会导致响应式会被丢失
 const { x, y } = useMousePosition();
 return {
 x,
 y,
 };

 // 拓展运算符将导致响应式丢失
 return {
 ...useMousePosition()
 }

 // 只有这样才能保证响应式不被丢失
 // 通过pos.x的pos.y来取值才会保留x,y的响应式
 return {
 pos: useMousePosition()
 }
 }
};
复制代码

通过上述的例子,要知道,我们没有办法通过编码风格的限制来保证通过组合函数返回的响应式状态不被丢失,Vue官方团队建议在组合函数中都通过返回ref对象来规避这一类问题,toRef便是做这一件事情的最好方式:

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

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

// x 和 y 现在具备了响应式
const { x, y } = useMousePosition();
复制代码

toRefsreactive对象转换为普通对象,其中结果对象上的每个属性都是指向原始对象中相应属性的ref引用对象,这在组合函数返回响应式状态时非常有用,这样保证了开发者使用对象解构或拓展运算符不会丢失原有响应式对象的响应。

watch可作用于单一函数

比起上一篇文章中介绍的watch API 的传参方式,最新的@vue/composition-api修正案中,watch的传递方式可以收敛为单一函数,Vue 3.x 将会在其依赖的响应式状态改变是执行watch的回调函数:

const count = ref(0);

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

setTimeout(() => {
 count.value++; // 打印1
}, 100);
复制代码

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
复制代码

生命周期钩子

比起vue-function-api@vue/composition-api删除了onBeforeCreateonCreated。因为setup总是在创建组件实例时调用,即onBeforeCreate之后和onCreated之前调用,因此onBeforeCreateonCreated将可以使用setup进行代替。

使用TypeScript和JSX

setup现在支持返回一个渲染函数,这个函数返回一个JSX,JSX可以直接使用声明在setup作用域的响应式状态:

export default {
 setup() {
 const count = ref(0);
 return () => (<div>{count.value}</div>);
 },
};
复制代码

注:如果使用TypeScript,同时希望使用需要在JSX命名空间内声明以下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;
 }
 }
}
复制代码

此外,为了更好地配合 TypeScript 进行类型推断,Vue Composition API 推荐使用createComponent来定义一个组件,以便于Vue进行类型推导:

import { createComponent } from 'vue';

export default createComponent({
 props: {
 foo: String,
 },
 setup(props) {
 console.log(props.foo);
 },
});
复制代码

小结

本文是笔者上一篇文章Vue 3.0 前瞻,体验 Vue Function API的续篇,主要描述 Vue Composition API 对比 之前的草案 Vue Function API 的变化,可以看到Vue 官方针对社区建议修改了 Vue Function API 草案的诸多问题。下一篇文章中,笔者带来 Vue Composition API 的响应式对象原理解读,在解读学习过程中,加深对 Vue Composition API 的理解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容