前言
vue3中最核心的就是基于Proxy实现的响应式API(用于代理对象类型)
响应式系统 API,顾名思义,就是指在新的特性中是如何去实现 Vue 的响应式功能。在这里我们介绍一下 reactive、ref、2 个响应式 API 的使用方法及Vue3响应式实现原理。
Vue3 响应式Api
reactive
返回对象的响应式副本
const obj = reactive({ count: 0 })
响应式转换是“深层”的——它影响所有嵌套 property。在基于 ES2015 Proxy 的实现中,返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。
reactive 参数必须是对象
reactive 将解构所有深层的 refs,同时维持 ref 的响应性。
const count = ref(1)
const obj = reactive({ count })
// ref 不会被解构
console.log(obj.count === count.value) // true
// 它会更新 `obj.value`
count.value++
console.log(count.value) // 2
console.log(obj.count) // 2
// 它也会更新 `count` ref
obj.count++
console.log(obj.count) // 3
console.log(count.value) // 3
reactive 同样可以包裹数组进行响应式赋能。(数组可以看成是特殊的 对象)
ref
ref 和 reactive 一样,同样是实现响应式数据的方法。在业务开发中,我们可以使用它来定义一些简单数据类型数据
<template>
<p>{{ count }}</p>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup() {
const count = ref(0)
return {
count
}
}
}
</script>
修改数据,可以通过 count.value = 1 类似这样的语法去修改。但是为什么它需要这样去修改变量,而 reactive 返回的对象可以直接修改如 state.count = 1 。
原因是 Vue 3.0 内部将 ref 悄悄的转化为 reactive,如上述代码会被这样转换:
ref(0) => reactive({ value: 0 })
所以 count 相当于 reactive 返回的一个值,根据 reactive 修改值的方式,就可以理解为什么 ref 返回的值是通过 .value 的形式修改值了。
shallowReactive:vue3中能够将对象变成响应式的API,只代理最外层对象readonly:将对象属性变为只读,且不管对象有多少层shallowReadonly:将对象属性变为只读,但是只代理最外层
Vue响应式实现原理
-
vue2 响应式原理回顾
- 对象响应化:遍历每个key,通过 Object.defineProperty API定义getter,setter
// 伪代码
function observe(){
if(typeof obj !='object' || obj == null){
return
}
if(Array.isArray(obj)){
Object.setPrototypeOf(obj,arrayProto)
}else{
const keys = Object.keys()
for(let i=0;i<keys.length;i++){
const key = keys[i]
defineReactive(obj,key,obj[key])
}
}
}
function defineReactive(target, key, val){
observe(val)
Object.defineProperty(obj, key, {
get(){
// 依赖收集
dep.depend()
return val
},
set(newVal){
if(newVal !== val){
observe(newVal)
val = newVal
// 通知更新
dep.notify()
}
}
})
}
- 数组响应化:覆盖原生JS数组的原型方法,增加通知变更的逻辑
// 伪代码
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(key=>{
arrayProto[key] = function(){
originalProto[key].apply(this.arguments)
notifyUpdate()
}
})
vue2响应式痛点
- 递归,消耗大
- 新增/删除属性,需要额外实现单独的API
- 数组,需要额外实现
- Map Set Class等数据类型,无法响应式
vue3响应式方案
使用ES6的 Proxy 进行数据响应化,解决上述Vue2所有痛点
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
相比 Object.defineProperty ,Proxy支持的对象操作十分全面:get、set、has、deleteProperty、ownKeys、defineProperty......等等
// reactive 伪代码
function reactice(obj){
return new Proxy(obj,{
get(target, key, receiver){
const ret = Reflect.get(target, key, receiver)
return isObject(ret) ? reactice(ret) : ret
},
set(target, key, val, receiver){
const ret = Reflect.set(target, key, val, receiver)
return ret
},
deleteProperty(target, key){
const ret = Reflect.deleteProperty(target, key)
return ret
},
})
}
作者:chenuvi
参考内容:
vue3 官网教程
本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情