通过监听数据的变化,实现响应式。
1. vue2 通过Object.defineProperty
实现监听
-
Object.defineProperty
精确地添加或修改对象的属性
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
value = newValue
}
})
Object.definePropery
的缺点:
- 深度监听需要一次性递归
- 无法监听数组
- 无法监听新增属性和删除属性
- 实现数据监听
function updateView() {
console.log('视图更新');
}
// 重新定义原型,重写方法数组实现监听
const originalProto = Array.prototype;
const arrayProto = Object.create(originalProto); // 先克隆一份Array的原型出来
['push', 'pop', 'shift', 'unshift', 'slice', 'splice'].forEach(item => {
arrayProto[item] = function () {
// 执行原始操作
originalProto[item].call(this, ...arguments);
updateView();
}
});
// 定义属性
function defineReactive(target, key, value) {
// 深度监听, 例如这种修改 data.info.msg = 'mmm' 需要递归监听
observe(value)
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
// 设置新值深度监听 data.name = { n: 1 } ; data.name.n = 2 新的值需要监听
observe(newValue)
// value一直存在闭包中,再get就能获取到
value = newValue
// 触发更新
updateView()
}
}
})
}
// 监听函数
function observe(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target
}
// 监听数组
if (Array.isArray(target)) {
console.log('target', target);
target.__proto__ = arrayProto
}
// 重新定义属性
for (const key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: '名字',
age: 23,
info: {
msg: 233
},
nums: [1, 2, 3]
}
observe(data)
// 测试
data.name = 'list'
// data.name = { n: 1 }
// data.name.n = 2
data.age = 22
data.info.msg = 'mm'
data.nums.push(2)
2. vue3 通过proxy
实现监听
-
const p = new Proxy(target, handler)
创建一个对象的代理,从而实现基本操作的拦截和自定义 - 实现数据监听
function reactive(target = {}) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target
}
// 代理配置
const proxyConf = {
get(target, key, receiver) {
// 只处理本身(非原型)属性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('key 监听', key) // 监听
}
const result = Reflect.get(target, key, receiver)
console.log('get', key, result);
// **如何提升性能: 只在get 使用的时候进行递归
return reactive(result) // 返回结果, 只对调用的内一层进行监听
},
set(target, key, val, receiver) {
// 不重复修改数据
if (val === target[key]) {
return true
}
// 监听数据新增
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('已有的key 监听', key) // 监听
} else {
console.log('新增的key 监听', key) // 监听
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val, result);
return result //是否设置
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete', key, result);
return result //是否删除成功
}
}
// 生成代理对象
const observed = new Proxy(target, proxyConf)
return observed
}
const data = {
name: 'zhangsan',
age: 20,
info: {
city: 'beijing',
a: {
b: {
c: {
d:
1
}
}
}
},
}
const proxyData = reactive(data)
console.log('proxyData', proxyData.info.a);