手写代码理解vue响应式原理
创始人
2024-06-01 18:54:32
0

vue2响应式应用了Object.defineProperty,vue3中的响应式则是运用proxy。

目录标题

    • 1、defineProperty
    • 2、代码理解defineProperty
    • 3、手写vue2响应式原理
    • 4、vue2监听数组响应式
    • 5、Proxy理解
    • 6、总结

1、defineProperty

Object.defineProperty(obj, prop, descriptor)
obj:要定义属性的对象
prop:要定义或修改的属性的名称或 Symbol
descriptor:要定义或修改的属性描述符
返回值:被传递给函数的对象。

configurable
当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false。

enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false。

数据描述符还具有以下可选键值:

value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined。

writable
当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符 (en-US)改变。 默认为 false。

存取描述符还具有以下可选键值:

get
属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined。

set
属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined。

描述符默认值汇总
拥有布尔值的键 configurable、enumerable 和 writable 的默认值都是 false。
属性值和函数的键 value、get 和 set 字段的默认值为 undefined。

2、代码理解defineProperty

监听obj数据变化

let obj={}Object.defineProperty(obj,"key",{value:1,writable:true,//是否可以赋值enumerable:true,//是否可循环configurable:true //是否可删除
})console.log(obj);let obj1={}
Object.defineProperty(obj1,'key',{get(){console.log("获取到的值:",value);return value},set(newVal){console.log("设置值为",newVal);value=newVal+"设置"}
})obj1.key="橙子很甜"

打印结果:
在这里插入图片描述

3、手写vue2响应式原理

let obj = {name: "橙子",job: {code: 100},arr:[1,2,3]
}//封装监听数据变化
function defineProperty(obj, key, val) {//遍历监听数据每一项observer(val);Object.defineProperty(obj, key, {get() {//读取console.log("读取key", key);return val},set(newVal) {//判断新老值if (newVal === val) returnobserver(newVal);console.log("设置", newVal);val = newVal}})}function observer(obj) {//先判断是不是objectif (typeof obj !== 'object' || obj == null) {return}for (const key in obj) {//对象,当前的key,值defineProperty(obj, key, obj[key])}}observer(obj)
console.log(obj.name);
obj.name = "蛋挞排队"
//...

打印结果:
在这里插入图片描述

obj.job.code=300

打印结果:
在这里插入图片描述

obj.name = {
name1: ‘张三’
}
obj.name.name1 = “李四”

打印结果:
在这里插入图片描述
注意

  • defineProperty中没有办法监听数组的变化
  • 监听数组时读取成功但是没办法监听
  • vue2用了重写了数组的方法监听数组,defineProperty本身是不能监听数组
  • vue2中监听不到通过数组下标改变数组元素

obj.arr.push(4)
console.log(obj.arr);

在这里插入图片描述
Object.defineProperty 是对象的方法监听不到数组的变更的 Vue2.x的做法是重写数组的7个方法。
把数组原型上所有的方法拿出来,即先克隆一份Array的原型出来,给每一个方法都设置响应式

4、vue2监听数组响应式

// Object.defineProperty 是对象的方法监听不到数组的变更的 Vue2.x的做法是重写数组的7个方法
// 封装监听数据变化的函数
let obj = {name: "橙子",job: {code: 100},arr:[1,2,3]
}
const arrayMethods = Array.prototype;
// 把数组原型上所有的方法拿出来,即先克隆一份Array的原型出来,这七个方法
const arrayProto = Object.create(arrayMethods);
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse'
]
methodsToPatch.forEach(method => {arrayProto[method] = function () {// 执行原始操作,给它添加监听,挂到原型链上,内部还是执行数组的方法arrayMethods[method].apply(this, arguments)console.log('监听赋值成功', method)}
})function defineProperty(obj, key, val) {observer(val)Object.defineProperty(obj, key, {get() {// 读取方法console.log('读取', key, '成功')return val},set(newval) {// 赋值监听方法if (newval === val) return// 遍历监听数据的每一项 observer(newval)console.log('赋值成功', newval)val = newval// 可以执行渲染操作}})
}function observer(obj) {if (typeof obj !== 'object' || obj == null) {return}if (Array.isArray(obj)) {// 如果是数组, 重写原型obj.__proto__ = arrayProto// 传入的数据可能是多维度的,也需要执行响应式for (let i = 0; i < obj.length; i++) {observer(obj[i])}} else {for (const key in obj) {// 给对象中的每一个方法都设置响应式defineProperty(obj, key, obj[key])}}
}observer(obj)obj.arr.push(4)

在这里插入图片描述

5、Proxy理解

const p = new Proxy(target, handler)

proxy(数据源对象,处理数据方法)

let obj = {name: '小陈'
}
let handler = {get(target, key, receiver) {console.log('get', key)return Reflect.get(target, key, receiver)},set(target, key, value, receiver) {console.log('set', key, value)return Reflect.set(target, key, value, receiver)}
}
let proxy = new Proxy(obj, handler)
proxy.name = '橙汁'

在这里插入图片描述

handler 对象的方法
handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器(trap)。
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
handler.getPrototypeOf()
Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf()
Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible()
Object.isExtensible 方法的捕捉器。
handler.preventExtensions()
Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.defineProperty()
Object.defineProperty 方法的捕捉器。
handler.has()
in 操作符的捕捉器。
handler.get()
属性读取操作的捕捉器。
handler.set()
属性设置操作的捕捉器。
handler.deleteProperty()
delete 操作符的捕捉器。
handler.ownKeys()
Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
handler.apply()
函数调用操作的捕捉器。
handler.construct()
new 操作符的捕捉器。
在官网可以看到proxy的用法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

监听数组变化

let arr = [1, 2, 3]
let proxy = new Proxy(arr, {get(target, key, receiver) {console.log('get', key)return Reflect.get(target, key, receiver)},set(target, key, value, receiver) {console.log('set', key, value)return Reflect.set(target, key, value, receiver)}
})
proxy.push(4)

在这里插入图片描述

6、总结

1、Proxy 的第二个参数可以有 13 种拦截方法, 比 Object.defineProperty() 要更加丰富,
Proxy 作为新标准受到浏览器厂商的重点关注和性能优化。
2、相比之下Object.defineProperty() 是一个已有的老方法。Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改。
3、Proxy 的兼容性不如 Object.defineProperty() 。
4、proxy可以直接监听数组变化,监听的目标为对象本身,不需要像defineProperty那样遍历每个属性

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...