这是我参与「第四届青训营」笔记创作活动的第15天。
前言
本文初步从vue响应式介绍,然后说明一下在什么情况下使用 ref、reactive 函数。
Vue3.0中的响应式原理
回顾vue2.x的响应式
-
实现原理:
-
对象类型:通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', { get () {}, set () {} })
-
-
存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
Vue3.0的响应式
-
实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
-
通过Reflect(反射): 对源对象的属性进行操作。
-
MDN文档中描述的Proxy与Reflect:
-
Reflect:developer.mozilla.org/zh-CN/docs/…
new Proxy(data, { // 拦截读取属性值 get (target, prop) { return Reflect.get(target, prop) }, // 拦截设置属性值或添加新属性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 拦截删除属性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) } }) proxy.name = 'tom'
-
Reflect vs Object
Reflect 可以返回一个bool值来判断操作是否成功,在封装时减少 try...catch 使用
let obj = {a:1,b:2}
//通过Object.defineProperty去操作
try {
Object.defineProperty(obj,'c',{
get(){
return 3
}
})
Object.defineProperty(obj,'c',{
get(){
return 4
}
})
} catch (error) {
console.log(error)
}
//通过Reflect.defineProperty去操作
const x1 = Reflect.defineProperty(obj,'c',{
get(){
return 3
}
})
console.log(x1)
const x2 = Reflect.defineProperty(obj,'c',{
get(){
return 4
}
})
if(x2){
console.log('某某某操作成功了!')
}else{
console.log('某某某操作失败了!')
}
//----------------------------------
// Reflect 静态方法
Reflect.apply(target, thisArgument, argumentsList)
//对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
Reflect.construct(target, argumentsList[, newTarget])
//对构造函数进行 new 操作,相当于执行 new target(...args)。
Reflect.defineProperty(target, propertyKey, attributes)
//和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty(target, propertyKey)
//作为函数的delete操作符,相当于执行 delete target[name]。
Reflect.get(target, propertyKey[, receiver])
//获取对象身上某个属性的值,类似于 target[name]。
Reflect.getOwnPropertyDescriptor(target, propertyKey)
//类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined。
Reflect.getPrototypeOf(target)
//类似于 Object.getPrototypeOf()。
Reflect.has(target, propertyKey)
//判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
Reflect.isExtensible(target)
//类似于 Object.isExtensible().
Reflect.ownKeys(target)
//返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).
Reflect.preventExtensions(target)
//类似于 Object.preventExtensions()。返回一个Boolean。
Reflect.set(target, propertyKey, value[, receiver])
//将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
Reflect.setPrototypeOf(target, prototype)
//设置对象原型的函数。返回一个 Boolean, 如果更新成功,则返回true。
使用ref函数
-
作用: 定义一个响应式的数据
-
语法:
const xxx = ref(initValue)- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象) 。
- JS中操作数据:
xxx.value - 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
-
备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()的get与set完成的。 - 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive函数。
使用reactive函数
- 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref函数) - 语法:
const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是*“深层次的”*。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
【reactive的参数必须是一个对象,包括json数据和数组都可以,否则不具有响应式】
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>工作薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<h3>测试的数据c:{{person.job.a.b.c}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
<script>
import {reactive} from 'vue'
export default {
name: 'App',
setup(){
//数据
let person = reactive({
name:'张三',
age:18,
job:{
type:'前端工程师',
salary:'30K',
a:{
b:{
c:666
}
}
},
hobby:['抽烟','喝酒','烫头']
})
//方法
function changeInfo(){
person.name = '李四'
person.age = 48
person.job.type = 'UI设计师'
person.job.salary = '60K'
person.job.a.b.c = 999
person.hobby[0] = '学习'
}
//返回一个对象(常用)
return {
person,
changeInfo
}
}
}
</script>
总结
本文初步从vue响应式介绍,然后说明一下在什么情况下使用 ref、reactive 函数。