-
利用Object.defineProperty+递归 实现响应式原理
// 监听数组, Object.defineProperty做不到, 所以需要重写数组方法!!!
// 重新定义数组原型
const oldArrProperty = Array.prototype
// 创建新对象, 原型指向oldArrProperty, 再扩展新的方法不会影响Array原型
// 使用Object.create是为了不污染全局的原型方法
const arrProto = Object.create(oldArrProperty);
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(element => {
arrProto[element] = function () {
updateView(); // 触发视图更新
oldArrProperty[element].call(this, ...arguments);
}
});// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
// 不是对象或数组
return target;
}if (Array.isArray(target)) { target.__proto__ = arrProto } // 重新定义各个属性(for in 也可以遍历数组) for (let key in target) { defineReactive(target, key, target[key]) }
}
// 重新定义属性, 监听起来
function defineReactive(target, key, value) {
// 深度监听(深层对象):
observer(value);// 核心API Object.defineProperty(target, key, { get() { return value }, set(newVal) { if (newVal !== value) { // 设置新值的时候也要深度监听(深层对象): observer(newVal) // 设置新值 // 注意, value一直闭包中, 此处设置完之后, 再get时也是会获取最新的值 value = newVal // 触发更新视图 updateView(); } } })
}
// 触发更新视图
function updateView() {
console.log('视图更新')
}
const data = {
name: 'zhangsan',
age: 20,
info: {
city: '北京', // 需要深度监听
},
nums: []
}// 监听数据
observer(data)//测试
// data.name='sdhewifewfewfewf'
// data.age = 21
// data.info.city = 'shanghai'
// data.x = '100';//新增属性, 监听不到--所以有V
// delete data.name; // 删除属性, 监听不到 --所以有Vue.delete
data.nums.push({ a: 1 }); // 监听数组
data.nums[0].a = 222
// console.log(data.name, data.age, data.info.city, data.nums)// console.log(arrProto)
// console.log(arrProto.proto.push)
// console.log(arrProto.proto === oldArrProperty)
// console.log(arrProto.prototype.constructor)
测试: 每次修改数据, 查看控制台有没有随着数据变化打印"视图更新"
-
使用Proxy和Reflect实现响应式原理, 如果有深层对象和数组也需要递归
var data = {
name: 'zhangsan',
age: 20,
list: [1, 2],
obj: { a: 1, b: 2, c: 2, city: {
name: '北京'
}}
}function reactive(data) {
const config = {
// receiver其实指的就是Proxy
get(target, key, receiver) {
// Reflect.ownKeys可以获取不是原型上的属性
const ownKeys = Reflect.ownKeys(target);
// 只处理本身(非原型的)属性
// if(ownKeys.includes(key)) {
// console.log('get:', key, '本身(非原型的)属性');
// }
const result = Reflect.get(target, key, receiver);
// 如果对象有深层的对象或者数组, 需要递归
if(Array.isArray(result) || Object.prototype.toString.call(result) === '[object Object]') {
return reactive(result);
}
console.log('get:', key, target[key], result)
return result;
},
set(target, key, val, receiver) {
// 重复的数据不处理
const oldVal = target[key]
if(val === oldVal) {
return true
}const ownKeys = Reflect.ownKeys(target); // 只处理本身(非原型的)属性 if(ownKeys.includes(key)) { // push方法就不执行了 console.log('get:', key, '已有的key'); } else { console.log('get:', key, '新增的key'); } const result = Reflect.set(target, key, val, receiver); console.log('set:', key, val, result) // 触发更新视图 updateView(); return result; }, deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key); console.log('deleteProperty:', key, result) // 触发更新视图 updateView(); return result; }, } const proxyData = new Proxy(data, config); return proxyData
}
// 触发更新视图
function updateView() {
console.log('视图更新')
}const proxyData = reactive(data)
proxyData.list.push('13')
proxyData.obj.a = 999
proxyData.obj.b = undefined
proxyData.obj.city.name = 'shanghai'
Reflect.deleteProperty(proxyData.obj, 'c')
proxyData.obj.d = 'ddddd'
测试: 每次修改数据, 查看控制台有没有随着数据变化打印"视图更新"