当前位置:   article > 正文

vue2 响应式之Object.defineProperty_object.defineproperty()如何设置数组响应式

object.defineproperty()如何设置数组响应式

提示:本博客用于分享本人学习心得,如有错误之处欢迎大家指出。


1.前言

​ 在日常的开发中,在data中定义的数据,可以在template中用*{{}}*在视图中显示,如下图所示
响应式

​ 我们加个按钮,在test后面追加“–test”,看看页面是否会改变
相应式

2.Object.defineProperty

​ 从上面可以看出,通过修改data中的数据来改变页面渲染的内容,那vue中内部是怎样实现这种相应式的功能的呢?

​ 在这之前,我们先来看一下Object.defineProperty这个API

​ Object.defineProperty: 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

(1)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'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

oberve函数判断源数据是否是对象,然后遍历,将源数据、键值、属性值传入defineReactive函数中

defineReactive函数中通过Object.defineProperty对对象的每一项进行监听,如果修改了对象就触发了updateView函数。

(2)Object.defineProperty深度监听data变化

​ 如果data是一个复杂对象,应该如何监听呢?我们将data替换成下面代码,再将children进行修改,看是否会触发updateView。

const data = {
    name: '张三',  
    age: 21,
    children: {
        name: '李四'
    }
}

data.children.name = 'lisi'

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

​ 打开控制台可以看到,并未输出内容,因此,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()
            }
        }
    })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注:如果data的层级过深,就会一次性递归到底,计算量很大

(3)Object.defineProperty监听新增、删除属性

​ 如果我们给data新增属性或者删除属性,我们还能够监听的到吗?

...
data.sex = 'man'
delete data.name
...
  • 1
  • 2
  • 3
  • 4

​ 加入以上代码,如果能监听的到的话,控制台会打印两次“视图更新”,但是我们看控制台并未打印任何内容

​ 因此,Object.defineProperty无法监听新增、删除属性,于是vue2.x通过Vue.setVue.delete来进行新增和删除属性

(4)监听数组变化

​ 我们在data中定义一个数组,并且对数组进行push操作

...
const data = {
    nums: [1, 2, 3]
}
...
data.nums.push(4)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

​ 刷新浏览器,控制台并未打印任何内容,进行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)
    }    
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

​ ② 在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])
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注: 当data中的某一项是数组时,在observe函数中遍历进入defineReactive中,然后进行深度监听,如果是数组的话,就会将此数组的原型指向为arrProto,此时当前数组调用的数组方法,就会执行updateView()和数组原型的方法。

例如,执行data.nums.push(4),

① 进入observe函数

② for…in…循环

③ 进入defineReactive

④ 深度监听,进入observe

⑤ 判断是否是数组

⑥ 将数组的原型指向arrProto

⑦ 执行updateView

⑧ 执行Array.prototype的push方法

3.Object.defineProperty的缺点

从上面的分析中,我们可以总结出Object.defineProperty的以下缺点

  1. 如果data的层级过深,就会一次性递归到底,计算量很大
  2. Object.defineProperty无法监听新增、删除属性
  3. Object.defineProperty不具备监听原生数组
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号