likes
comments
collection
share

Vue2数据响应式原理深度解析( 一 )

作者站长头像
站长
· 阅读数 32

Vue2数据响应式原理逻辑深度解析(一)

前言

各位小伙伴们大家好呀,最近看到很多朋友在面试的时候被问及vue2中的数据响应式原理的时候,能够提到Object.defineProperty中的setter函数和getter函数,在说出它们的作用后就浅尝辄止,并不能理解较深层面的逻辑嵌套。因此本人开始执笔这vue2响应式原理解析的专栏,帮大家从源码逻辑层面梳理其中的相关实现。

家人们做好准备哦,相信各位JS的基础都不错,关于细节方面本系列应提尽提,不客气哈哈哈~

Object.defineProperty

相信这个方法各位都不陌生,它能为一个新对象设置属性,其中内部的getter函数和setter函数能够迅速捕捉用户对于数据的操作并触发相应的函数,这一现象被称为 '数据劫持'

废话不多说,直接上图

Vue2数据响应式原理深度解析( 一 )

代码

let obj = { }

Object.defineProperty(obj, 'a', {

  get() {
    console.log(`您正在视图查看obj的a属性`)
    return 9
  },

  set() {
    console.log(`您正在视图修改obj的a属性`)
  }
})

console.log(obj.a)

可以看到我们最初定义了一个obj空对象,并利用Object.defineProperty中的get方法为其设定了一个键值对,键为a,因此对于属性a的操作会被监视; 而其对应的值是我们给其内部get函数设置的返回值,值为9

此时我们来打印obj里的a属性,来看控制台

Vue2数据响应式原理深度解析( 一 )

那这里的get( )函数捕捉到了我们想要打印这个被监视的属性a的值这个操作,最终执行并触发自己内部的代码,肥肠好~

此时我们现在在外部修改obj.a的值,如图

Vue2数据响应式原理深度解析( 一 )

来看控制台的反应

Vue2数据响应式原理深度解析( 一 )

这时set( )函数也捕捉到了我们想修改这个被监视的属性a的值变为10,并也触发了这个函数本身,没毛病~

那如果此时小伙伴们再次打印obj . a的值,发现a的值居然还是原来get函数的返回值9,这是为什么??我们不是改成10了吗?原来a的值只能由get函数的返回值决定,不论外部做什么操作,都无法直接影响到a本身的,此时我们就需要创建一个函数,传相关的参数进去,间接去影响get函数的返回值,说到这里,相信大家都有点感觉了

修改对象内部属性的值

这里我们创建一个defineReactive函数,其接收三个参数,对象data,要监视的值key,以及设置的值val,再把我们刚刚的Object.defineProperty()函数放入内部,而它也要接收上面传递过来的参数,再在这个函数内部完成变量的一些简单周转,如图

Vue2数据响应式原理深度解析( 一 )

代码

function defineReactive(data, key, val) {

  Object.defineProperty(data, key, {

    get() {
      console.log(`您正在视图查看obj的${key}属性`)
      return val
    },

    set(newVal) {
      console.log(`您正在视图修改obj的${key}属性`)
      val = newVal
    }
  })
}

在这里,defineReactive函数接收的data和和key不用多说,而属性值val会传递给下面的Object.defineProperty()函数,val会在内部进行一些周转,最终使得get函数的返回值就为传递进来的这个val,这是函数闭包的一个实现,相信大家不会对此表示陌生

此时我们来调用这个defineReactive函数

Vue2数据响应式原理深度解析( 一 )

打印结果

Vue2数据响应式原理深度解析( 一 )

这样一来起来我们就通过外部的操作影响到了get函数的返回值,也就是设置了a的值

此时我们在外面设置a的值能否直接生效呢?如图

Vue2数据响应式原理深度解析( 一 )

当给被监视的对象a,直接设置属性值时,会触发Object.defineProperty()函数内部的set函数,它接收这个即将修改的值666, 并传递给val,因此上面的get函数的返回值也是666,因此这时候就成功修改了属性a的值

打印结果

Vue2数据响应式原理深度解析( 一 )

可以看到结果打印正确,奈斯~

思考

那么问题来了,如果此时我这样 把obj设置成如下

Vue2数据响应式原理深度解析( 一 )

再次调用defineReactive函数

Vue2数据响应式原理深度解析( 一 )

注意此时我们传递进来的只有两个参数obj和 'a'

并打印obj最里面的c

Vue2数据响应式原理深度解析( 一 )

打印结果

Vue2数据响应式原理深度解析( 一 )

可以看到这只触发了get关于a的函数,但为什么没打印出 您正在试图查看c属性呢?

也就是说我们现在只是给了obj对象本身绑定了数据劫持,而其内部的属性并没有绑定数据劫持, 这显然不正常。因此这里需要我们规范的去书写自己的逻辑代码,来给对象内部的属性都绑定上响应式数据,关于如何去开展接下来的绑定数据劫持的操作,我们在下一篇章中正式开始手写自己的逻辑代码

注意:本系列需要大量的运用到模块式编写,因此涉及到多层文件相互引用嵌套,请各位先熟悉其相关知识,更有助于后期的理解

总结

这节课我们完成了关于 Object.defineProperty( )函数的运用,复盘了关于get( )和set( )的功能,并最后利用了defineReactive函数实现了直接在外部修改属性值的操作,今天的文章只是基础部分,后期文章难度系数会逐渐增大,请各位做好心理准备

本节代码

index.js

let obj = {
  a: {
    b: {
      c: 555
    }
  }
}

function defineReactive(data, key, val) {
  if (arguments.length === 2) {
    val = obj[key]
  }

  Object.defineProperty(data, key, {
  
    get() {
      console.log(`您正在视图查看obj的${key}属性`)
      return val
    },

    set(newVal) {
      console.log(`您正在视图修改obj的${key}属性`)
      val = newVal
    }
  })
}

defineReactive(obj, 'a')

console.log(obj.a.b.c)