对象的属性分为:数据属性和访问器属性。如果要修改对象的默认特性,必须使用Object.defineProperty方法,它接收三个参数:属性所在的对象、属性的名字、一个描述符对象。那么接下来先看看他如何深度监听原生数组的~~~~~~
observer
//先准备数据
const data = {
name:'zhangsan',
age:20,
info:{
address: '北京' // 需要深度监听
},
nums:[10, 20, 30]
}
1. [endif]其中的info需要深度监听 其次定义一个方法,这个方法判断了这个不是对象或数组 然后重新定义各个属性(for in 也可以遍历数组)
2. [endif]其次重新定义属性 监听起来并且运用到了刚刚封装的监听方法 和核心API,( 注意,value 一直在闭包中,value
=newValue此处设置完之后,再 get 时也是会获取最新的值)
3. [endif]其次监听原生数组方法, 原理就是重写数组的七个原始方法,当使用者执行这些方法时,我们就可以监听到数据的变化,然后做些跟新操作
完整代码流程
// 触发更新视图
function updateView() {
console.log('视图更新')
}
// 重新定义数组原型 监听原生数组方法
constoldArrayProperty = Array.prototype
// 创建新对象,原型指向oldArrayProperty ,再扩展新的方法不会影响原型
const arrProto =Object.create(oldArrayProperty);
['push', 'pop', 'shift', 'unshift', 'splice', sort,reverser].forEach(methodName => {
arrProto[methodName] = function () {
updateView() //触发视图更新
//执行原始操作
oldArrayProperty[methodName].call(this, ...arguments)
// Array.prototype.push.call(this, ...arguments) //执行原始操作
}
})
// 重新定义属性,监听起来
function defineReactive(target, key, value) {
//深度监听
observer(value)
//核心API
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
//深度监听
observer(newValue)
//设置新值
//注意,value 一直在闭包中,此处设置完之后,再 get 时也是会获取最新的值
value = newValue
//触发更新视图
updateView()
}
}
})
}
// 监听对象属性
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])
}
}
// 准备数据
const data = {
name:'zhangsan',
age:20,
info:{
address: '北京' // 需要深度监听
},
nums:[10, 20, 30]
}
// data.name = 'lisi'
// data.age = 21
// // console.log('age', data.age)
// data.x = '100' // 新增属性,监听不到 —— 所以有Vue.set
// delete data.name // 删除属性,监听不到 —— 所有已Vue.delete
// data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组
其实我们发现 observer 其实是个递归,最后会把所有的数据都变成响应式
总结
可以感觉到,在用Object.defineProperty实现数据响应式时我们必须要遍历所有的数据,还需要重写数组的方法,性能消耗也比较大,我们知道Vue2.x就是基于Object.defineProperty实现数据响应式的但新版本的Vue3放弃了Object.defineProperty采用Proxy重写了响应式系统,那Vue3为什么要选择Proxy?Proxy又是如何实现数据拦截的呢?,我们下期分享用Proxy如何实现响应式。