

Vue 3.0 最新进展,Composition API
source link: https://zhuanlan.zhihu.com/p/83224870
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Vue 3.0 最新进展,Composition API
在上一篇文章Vue 3.0 前瞻,体验 Vue Function API,笔者通过尝试vue-function-api,提前体验了Vue 3.0 即将发布的函数式API,在文章最后,笔者提出了一些思考。最近,Vue 官方发布了最新的3.0 API 修改草案,并在充分采纳社区的意见后,将Vue Function API 更正为 Vue Composition API,提供了在Vue 2.x 能够提前体验此API的库@vue/composition-api,笔者出于学习目的,提前体验了这个库。并结合上一篇文章,描述 Vue 官方团队在采纳社区意见后对 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更改为ref
。ref
用创建一个包装对象,只具备一个响应式属性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
呢?其使用场景其实与我们所习惯的编码风格密切相关,通过下面的例子,我们能更好理解使用ref
和reactive
的区别:
// 风格一:通过基本类型变量来声明状态
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();
toRefs
将reactive
对象转换为普通对象,其中结果对象上的每个属性都是指向原始对象中相应属性的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
可传入get
和set
,用于定义可更改的计算属性
基本示例如下所示,与 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删除了onBeforeCreate
和onCreated
。因为setup
总是在创建组件实例时调用,即onBeforeCreate
之后和onCreated
之前调用,因此onBeforeCreate
和onCreated
将可以使用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 的理解。
Recommend
-
22
The next version of Vue is around the corner and we can already try some new features, like Vue Composition API, which is heavily inspired by React Hooks. A lot of developers are excited about it, others are not so sure. L...
-
11
Vue Composition API 响应式包装对象原理上一篇文章Vue 3.0 最新进展,Composition API中,笔者通过描述Vue Composition API 的最新修...
-
10
作者:陈大鱼头github: KRISACHAN作为新特性 Composition API ,在 Vue3 正式发布之前一段时间就发布过了。距文...
-
8
Vue Composition API vs React Hooks - the core difference Arek Nawo | 05 May 2021 | 4 min read
-
10
BackDiscovering Vue Composition API with examplesMay 25th, 2021 · 6 min read
-
11
了解 vue composition-api 原理 写在前面的心得 1. 不要在 setup 内直接使用 setInterval 与 setTimeout 这类需要手动清理副作用的 api 1. 因为人很容易忘记清理他的副作用,应该自行封装一个类似于计算属性的
-
5
目录 场景 hook 的时代意义 React Hooks Vue Composition API 差别 总结 场景 先理解…目录hook 的时代...
-
8
Vue Composition API vs. React HooksReact Hooks were introduced with the React 16.8 update, and since then, they...
-
6
Creating Computed Properties with Vue's Composition API Oct 10, 2022 To create a computed property with the Vue's Composition API, you should call Vue's computed() function. For example, the followin...
-
6
The reactivity API adds many possibilities to the composition API while keeping the code brief. However, you should be aware of some of the pitfalls of reactivity, for exampl...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK