在上一篇文章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 做出的一些更正。javascript
本文主要分如下几个主题讨论最新的Composition API:vue
Composition API 可谓是修复了 Function API 诸多问题而提供的最新“修正案”,下面来看比起以前的vue-function-api,究竟修改了些什么呢?java
state
改名为reactive
在vue-function-api中,经过state
建立响应式对象,这个state建立的响应式对象并非包装对象,不须要使用.value
来取值。但问题在于:state
一般会被用做描述 Vue 组件状态对象的变量名,容易对开发者形成误导,Vue官方团队认为将state
API 改名为reactive
更为优雅,reactive
等价于 Vue 2.x 的Vue.observable()
,用于获取一个对象的响应性代理对象:react
const obj = reactive({ count: 0 });
复制代码
在vue-function-api中,经过value
函数建立一个包装对象,它包含一个响应式属性value
。在 Vue 官方团队充分采用社区意见后,将这个API更改成ref
。ref
用建立一个包装对象,只具有一个响应式属性value
,若是将对象指定为ref
的值,该对象将被reactive
方法深度遍历。要知道, Composition API 之因此被提出和使用,就是为了让咱们更加方便地进行组件复用,将状态通过函数式地传递过程当中,因为JavaScript
函数传参是值传递的,而基本类型不具有引用,为了保证属性的响应式,将使用ref
来建立包装对象进行传递。git
const count = ref(0);
console.log(count.value); // 0
count.value++;
console.log(count.value); // 1
复制代码
提供isRef
,用于检查一个对象是不是ref
对象:github
const unwrapped = isRef(foo) ? foo.value : foo;
复制代码
若是读者你看到这里,可能就会比较疑惑了,究竟何时该使用ref
,何时该使用reactive
呢?其使用场景其实与咱们所习惯的编码风格密切相关,经过下面的例子,咱们能更好理解使用ref
和reactive
的区别:typescript
// 风格一:经过基本类型变量来声明状态
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
对象。api
然而,思考下面的场景:app
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
进行代替。
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 的理解。