vue响应式原理
vue2的响应式原理
vue2对对象类型的监听是通过Object.defineProperty实现的,给想要实现响应式的数据对象每个属性加上get,set方法,以实现数据劫持的操作。而对数组类型的监听是通过重写数组的方法实现的。
Object.defineProperty的定义见这里:Object.defineProperty
模拟vue2响应式实现
// 源数据const person = {name: "张三",age: 18,};let p = {};Object.defineProperty(p, "name", {configurable: true,get() {console.log("获取了name");return person.name;},set(name) {console.log("修改了name"); // 模拟复杂的视图变动的代码person.name = name;},});Object.defineProperty(p, "age", {configurable: true,get() {console.log("获取了age");return person.age;},set(age) {console.log("修改了age");person.age = age;},});
调用结果:
存在问题:
- 对象类型不能监听到新增/删除属性的变动
- 数组类型不能监听到直接通过下标修改的变动
addSex/deleteName/deleteName点击没反应,但实际数据有修改
针对以上问题,vue2也提出一些api处理:
- this.$set/Vue.set:修改/新增属性的监听
- $delete/Vue.delete:删除属性的监听
所以,虽然vue2在响应式数据有些问题,但也提出了解决方案,并不是一无是处。
vue3的响应式原理
在说明vue3的响应式原理前先了解window的两个内置对象
链接在这:
Proxy :Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Reflect:Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。
Proxy
vue3是通过Proxy来实现响应式的,大致代码如下:
// 源数据const person = {name: "张三",age: 18,hobby: ["吃饭", "睡觉", "打豆豆"],};const p = new Proxy(person, {get(target, property, receiver) {console.log(`获取了${target}的${property}`);return target[property];},set(target, property, value, receiver) {console.log(`修改了${target}的${property}为${value}`);target[property] = value;},deleteProperty(target, property, receiver) {return delete target[property];},});
target是源对象, property是属性, receiver是代理对象
实际上重写了Proxy的get/set/deleteProperty方法,实现对对象属性的增删改查
Reflect
ECMA组织正在把Object上的一些象defineProperty()之类的方法有用的方法往Reflect上迁移,vue3响应式实现也用到Reflect
把上面的代码改造一下:
const p = new Proxy(person, {get(target, property, receiver) {console.log(`获取了${target}的${property}`);return Reflect.get(target, property, receiver);},set(target, property, value, receiver) {console.log(`修改了${target}的${property}为${value}`);Reflect.set(target, property, value, receiver);},deleteProperty(target, property) {return Reflect.deleteProperty(target, property);},});