今天是10月24日,“1024”,程序员们的节日!祝广大程序员朋友们事业顺利,头发茂密,健康长寿。
进入今天的正题,这几天,我们陆续学习了解了关于Vue3.0的一些新特性,尤其是新的Composition API的用法。这套新的API中最重要、最核心的部分,恐怕就是实现响应式功能的这一块了。而且,这套响应式API不仅可以在Vue3.0环境下使用,也可以独立使用。
响应式API主要由2大类组成:
- 针对原始的数据,将其包装成可观察数据的API (ref、reactive)
- 针对可观察数据的变动,执行监听和响应动作的API (effect、computed)
包装API和监听API这2者之间互相协同工作,组成一个完整的响应式系统。下面我们来通过一些简单的例子,观察和了解一下它们是如何进行协同工作的。
示例一:ref + effect
在前面的文章中我们提到过,ref 函数可以将一个数据包装成一个响应式数据对象(Ref类型),该函数的TypeScript类型定义如下:
function ref(raw: T): Ref
effect 函数则可以接受一个监听函数,如果这个监听函数中存在对响应式数据对象的访问,则一旦这些响应式数据对象的值发生变化,该监听函数就会被执行。
来看一个简单的代码示例:
const { ref, effect } = Vue
// 创建观察对象
const greeting = ref("Hello,World!")
// 监听数据变化
effect(() => {
console.log(greeting.value)
})
// 再让数据改变一下吧
greeting.value = "Are you OK?"
以上这段代码先使用 ref 函数创建了一个名为 greeting 的可观察对象,然后通过 effect 函数创建对 greeting 值变化的监听器,对值进行打印。这段代码的执行结果是打印出:
Hello,World!
Are you OK?
这个还是很好理解的,因为这个 greeting 包含的值一共变化了2次,第一次是初始化时设置的 “Hello,World!”,第二次是后面设置的 "Are you OK?"。
示例二:ref 作用于数组数据
在Vue2.x中,对一个数组中的每个元素进行响应式变化监听,做起来还是稍微有点麻烦和不优雅的。在Vue3.0中,这个问题被很好的解决了。
const { ref, effect } = Vue
// 创建一个对数组的观察对象
const arr = ref([1, 2, 3])
effect(() => {
console.log(arr.value[0])
})
// 改变整个数组
arr.value = [5, 6, 7]
// 改变数组中的第一个元素
arr.value[0] = 111
上面这段代码示例输出的结果是:
1
5
111
由此可以说明,在这段代码中,无论是对整个数组重新赋值,还是对数组中的某个元素赋值,都可以被精准的监听到。
示例三:ref 的嵌套
由 ref 函数创建的可观察对象可以嵌套使用。
const { ref, effect } = Vue
const a = ref(1)
const b = ref(2)
const c = ref({ x: a, y: b })
effect(() => {
console.log(c.value.x + c.value.y)
})
// 从c对象上去间接改变a和b值
c.value.x = 5
c.value.y = 10
// 直接改变a和b的值
a.value = 20
b.value = 60
可以看到,可观察对象 c 中包含了对其他2个可观察对象 a 和 b的引用。这段代码的最终执行结果为如下:
3
7
15
30
80
由此可见,无论是通过嵌套引用来改变可观察对象值,或是直接改变可观察对象值,effect 创建的监听器都能正确响应这些变化。
示例四:reactive + effect
之前的文章中也提到过,除了 ref 函数,reactive 是Composition API中的另一个可用于创建可观察对象的函数。最简单的用法如下:
const { reactive, effect } = Vue
const state = reactive({ a: 1, b: 'Hello' })
effect(() => {
console.log(state.a)
})
state.a = 2
使用 reactive 的好处是一次性可以观察多个属性。不过,自从ES6+流行后,我们在实际的代码编写过程中,是不是经常会用到对象解构操作?比如像这样:
const obj = { a: 1, b: 2 }
const { a, b } = obj
而在对可观察对象进行操作的时候,你一定也会自然而然的想这么干:
const state = reactive({ a: 1, b })
let { a, b } = state
state.a = 123 //可以
a = 123 // 行不通!变成不可观察了
为什么将可观察对象中的属性解构出来后,变成不再可观察了呢?因为通过 reactive 函数创建的可观察对象,内部的属性本身并不是可观察类型的,对他们访问和观察其实都是通过Proxy代理访问来实现的。如果将这些属性解构,这些属性就不再通过原对象的代理来访问了,就无法再进行观察。
不过,Composition API也考虑到了这一点,提供了方法来解决这一个问题:
const { reactive, effect, toRefs } = Vue
const state = reactive({ a: 1, b: 2 })
// 这里的a和b都是Ref类型对象
const { a, b } = toRefs(state)
effect(() => {
console.log(a.value)
})
a.value = 123
通过引入一个 toRefs 函数,它可以将 reactive 创建的可观察对象中的属性都转换成可观察的 Ref 对象,这样一来,即使解构后,也可以被独立进行观察了。
示例五:computed
用过Vue的朋友,一定对计算属性不陌生,一般用于定义一个虚拟属性,这个虚拟属性的值来源于一个或多个可观察对象的变化而产生。在Composition API中也有对应的功能:
const { computed, ref } = Vue
// 可观察对象
const num = ref(1)
// 计算属性
const text = computed(() => {
return `Value is ${num.value}`
})
console.log(text.value)
// 改动可观察对象的值
num.value = 2
console.log(text.value)
这个例子中,我们根据数字类型的 num,来生成新的字符串 text,实现了一个比较方便的数据生成转换。
总结
Vue 3.0的Composition API提供了上文示例中所展示的这些简单而有效的函数,实现了响应式功能。其实它们还提供了一些可选项参数,用于实现更为丰富的功能,大家可以自己阅读源码深入研究一下,非常有意思,同时也可以从优秀的源码中学习和提高自己的编程水平。
最新推荐阅读:
《Vue3.0抢先学》系列文章