赞
踩
提示:本博客用于分享本人学习心得,如有错误之处欢迎大家指出。
在日常的开发中,在data中定义的数据,可以在template中用*{{}}*在视图中显示,如下图所示
我们加个按钮,在test后面追加“–test”,看看页面是否会改变
从上面可以看出,通过修改data中的数据来改变页面渲染的内容,那vue中内部是怎样实现这种相应式的功能的呢?
在这之前,我们先来看一下Object.defineProperty这个API
Object.defineProperty: 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
function updateView() { console.log('视图更新') } function defineReactive(target, key, value) { Object.defineProperty(target, key, { get() { return value }, set(newValue) { if (newValue !== value) { value = newValue updateView() } } }) } function observe(target) { if (target === null || typeof target !== 'object') { return target } for(let key in target) { defineReactive(target, key, target[key]) } } const data = { name: '张三', age: 21 } observer(data) data.name = 'lisi'
oberve函数判断源数据是否是对象,然后遍历,将源数据、键值、属性值传入defineReactive函数中
defineReactive函数中通过Object.defineProperty对对象的每一项进行监听,如果修改了对象就触发了updateView函数。
如果data是一个复杂对象,应该如何监听呢?我们将data替换成下面代码,再将children进行修改,看是否会触发updateView。
const data = {
name: '张三',
age: 21,
children: {
name: '李四'
}
}
data.children.name = 'lisi'
打开控制台可以看到,并未输出内容,因此,Object.defineProperty不能够深度监听对象。
想到深度监听,我们就可以使用递归转换成监听普通对象,
① 在defineReactive函数中Object.defineProperty调用之前调用oberserv函数,将传过来的属性值进行再次判断
function defineReactive(target, key, value) {
// 深度监听
observe(value)
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
value = newValue
updateView()
}
}
})
}
注:如果data的层级过深,就会一次性递归到底,计算量很大
如果我们给data新增属性或者删除属性,我们还能够监听的到吗?
...
data.sex = 'man'
delete data.name
...
加入以上代码,如果能监听的到的话,控制台会打印两次“视图更新”,但是我们看控制台并未打印任何内容
因此,Object.defineProperty无法监听新增、删除属性,于是vue2.x通过Vue.set和Vue.delete来进行新增和删除属性
我们在data中定义一个数组,并且对数组进行push操作
...
const data = {
nums: [1, 2, 3]
}
...
data.nums.push(4)
刷新浏览器,控制台并未打印任何内容,进行push操作会进入defineReactive中,因为Object.defineProperty不具备监听原生数组的功能,因此未监听成功,那应该如何进行数组监听呢?
① 新建一个空对象,原型指向Array.property
const arrProto = Object.create(Array.property) // 数组的方法名称 let arrFn = [ 'push', 'pop', 'shift', 'unshift', ... ] arrFn.forEach(method => { arrProto[method] = function() { // 视图更新 updateView() Array.propery[method].call(this, ..arguments) } })
② 在observe函数中进行数组判断,并将源数据的原型指向arrProto
function observe(target) {
if (target === null || typeof target !== 'object') {
return target
}
if (Array.isArray(target)) }{
target.__proto__ = arrProto
}
for(let key in target) {
defineReactive(target, key, target[key])
}
}
注: 当data中的某一项是数组时,在observe函数中遍历进入defineReactive中,然后进行深度监听,如果是数组的话,就会将此数组的原型指向为arrProto,此时当前数组调用的数组方法,就会执行updateView()和数组原型的方法。
例如,执行data.nums.push(4),
① 进入observe函数
…
② for…in…循环
③ 进入defineReactive
④ 深度监听,进入observe
…
⑤ 判断是否是数组
⑥ 将数组的原型指向arrProto
⑦ 执行updateView
⑧ 执行Array.prototype的push方法
从上面的分析中,我们可以总结出Object.defineProperty的以下缺点
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。