vue2响应式原理

vue的特性:数据驱动视图双向数据绑定。vue官方文档也提供了响应式原理的解释:

深入响应式原理

Object.defineProperty()

Object.defineProperty()的作用是直接在一个对象上定义一个新属性,或者修改一个属性

使用方式:Object.defineProperty(对象名,属性名,配置项)

<script>
let person = {
name: '张三',
sex: '男',
}
Object.defineProperty(person,'age',{
value: 18
}) //不能参与遍历
</script>

上述写法是给person对象添加一个age属性,属性的值是18

但是这种写法:

  • 不能进行枚举,即无法在遍历的时候获取到age属性的值
  • 不能被修改
  • 不能删除

所以Object.defineProperty()还有其他配置项

Object.defineProperty(person,'age',{
value: 18
enumerable: true //控制属性是否可以枚举,默认值是false
writeable: true //控制属性是否可以被修改,默认值是false
configurable: true //控制属性是否可以被删除,默认值是false
})

现在有一个需求:定义一个新的变量number,当number的值修改后,personage的值也相应被修改;而personage的值被修改后,number的值也相应被修改。

这个时候需要借助新的配置项getset

<script>
let number = 18
let person = {
name: '张三',
sex: '男',
}
//能够实现number的值修改后,person中age的值也相应被修改
Object.defineProperty(person,'age',{
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get:function(){
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且接收到修改的具体值
set(value){
number = value
}
})
</script>

数据代理

数据代理就是通过一个对象代理另一个对象中属性的操作

vue就是通过vm对象来代理data对象中属性的操作

<body>
<div id="app">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
</div> </body>
<script>
const vm = new Vue({
el: '#app',
data: {
name: '张三',
age: 18
}
})
</script>

控制台输出vm,我们可以看到nameage这两个属性

这两个属性都是通过Object.defineProperty()添加到vm上,所以可以发现他们都有对应的getter/setter

也就是说:当读取vm中的name时,会调用getter,把data.namename;当修改vm中的name时,会调用setter,修改data.name中的值(这里跟第一个例子是同一个道理)

但是我们会发现vm上没有data(疑惑:明明在创建vue实例对象的时候,设置了data,为什么取不到)

其实我们以为的这个data其实是_data,可以验证一下:

let data = {
name: '张三',
age: 18
}
const vm = new Vue({
el: '#app',
data
})

控制台进行一下判断:

所以我们获取数据的时候,也可以通过vm._data.age来获取

vue为了编码更方便,进行了数据代理,遍历data中的所有属性,把每个属性都添加到vm中,指定getter/setter

所以可以直接通过vm.age来获取数据

基本原理:

  • 通过Object.defineProperty()data对象中所有属性添加到vm
  • 为每一个添加vm上的属性,都指定一个getter/setter
  • getter/setter内部去操作(读/写)data中对应的属性

实现双向绑定

双向绑定就是数据发生变化时,视图也跟着变。核心是数据劫持发布者-订阅者模式

数据劫持实质就是使用defineProperty重写getter/setter。当数据改变时,set就会劫持这个数据的变化,更新视图(view)

但是由于defineProperty无法检测到对象和数组内部的变化,所以遇到属性为对象时,会递归观察该属性;遇到数组时,会重写pushpopshift等方法。

监测对象中的数据

最开始会想认为利用getter/setter,但是这样会造成死循环。只要有人获取name的值,就会调用get,然后又会获取一次person.name,造成死循环。set同理。

//错误的代码!!!!!!!!!
let person = {
name: '张三',
}
Object.defineProperty(person, 'name', {
get: function() {
return person.name
},
set(value) {
person.age = name
}
})

正确的做法是:监听数据的每一个属性,当监听到属性值发生变化时,通知订阅者去更新视图,重新进行模板解析。

<script>
let data = {
name: '张三',
} //创建一个观察者实例对象,用于监视data中属性的变化
const obs = new Observer(data) //准备一个vm实例对象
let vm = {}
vm._data = data = obs function Observer(obj) {
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj) //遍历
keys.forEach((k) => {
Object.defineProperty(this, k, {
get() {
return obj[k]
},
set(val) {
obj[k] = val
}
})
})
}
</script>

上述代码只是一个例子,只会对一层对象进行处理,vue的操作是递归,直到数据类型是简单数据类型。

如需给后添加的属性做响应式,可以使用

  • Vue.set(object,propertyName,value)
  • vm.$set(object,propertyName,value)
data: {
student:{
name: '张三',
age: 18,
friends:[
{name:'小明',age:20},
{name:'李四',age:15}
]
}
}
Vue.set(this.student, 'sex', '男')
this.$set(this.student, 'sex', '男')

监测数组中的数据

这里可以去看一下vue的官方文档:

通过包裹数组更新元素的方法实现,本质就是做了两件事

(1)调用原生对应的方法对数组进行更新

(2)重新解析模板,进而更新页面

所以在vue修改数组中的某个元素一定要用如下方法:

  1. 使用API:push(),pop(),shift(),splice(),sort(),reverse()
  2. Vue.set(),vm.$set()

Vue2响应式原理的更多相关文章

  1. [切图仔救赎]炒冷饭--在线手撸vue2响应式原理

    --图片来源vue2.6正式版本(代号:超时空要塞)发布时,尤雨溪推送配图. 前言 其实这个冷饭我并不想炒,毕竟vue3马上都要出来.我还在这里炒冷饭,那明显就是搞事情. 起因: 作为切图仔搬砖汪,长 ...

  2. Vue2 响应式原理

    我们经常用vue的双向绑定,改变data的某个属性值,vue就马上帮我们自动更新视图,下面我们看看原理. Object的响应式原理: 可以看到,其实核心就是把object的所有属性都加上getter. ...

  3. vue2响应式原理与vue3响应式原理对比

    VUE2.0 核心 对象:通过Object.defineProtytype()对对象的已有属性值的读取和修改进行劫持 数组:通过重写数组更新数组一系列更新元素的方法来实现元素的修改的劫持 Object ...

  4. 由浅入深,带你用JavaScript实现响应式原理(Vue2、Vue3响应式原理)

    由浅入深,带你用JavaScript实现响应式原理 前言 为什么前端框架Vue能够做到响应式?当依赖数据发生变化时,会对页面进行自动更新,其原理还是在于对响应式数据的获取和设置进行了监听,一旦监听到数 ...

  5. vue3剖析:响应式原理——effect

    响应式原理 源码目录:https://github.com/vuejs/vue-next/tree/master/packages/reactivity 模块 ref: reactive: compu ...

  6. vue2.0与3.0响应式原理机制

    vue2.0响应式原理 - defineProperty 这个原理老生常谈了,就是拦截对象,给对象的属性增加set 和 get方法,因为核心是defineProperty所以还需要对数组的方法进行拦截 ...

  7. Vue2.x响应式原理

    一.回顾Vue响应式用法 ​ vue响应式,我们都很熟悉了.当我们修改vue中data对象中的属性时,页面中引用该属性的地方就会发生相应的改变.避免了我们再去操作dom,进行数据绑定. 二.Vue响应 ...

  8. Vue源码--解读vue响应式原理

    原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...

  9. Vue响应式原理的实现-面试必问

    Vue2的数据响应式原理 1.什么是defineProperty? defineProperty是设置对象属性,利用属性里的set和get实现了响应式双向绑定: 语法:Object.definePro ...

随机推荐

  1. Android 12(S) 图形显示系统 - 简单聊聊 SurfaceView 与 BufferQueue的关联(十三)

    必读: Android 12(S) 图形显示系统 - 开篇 一.前言 前面的文章中,讲解的内容基本都是从我们提供的一个 native demo Android 12(S) 图形显示系统 - 示例应用( ...

  2. CF1225E Rock Is Push (计数)

    观察性质计数题orz小贺 考场上跟榜才切 我们只能往下和往右走,那么只有连续的往下和往右可能会造成不合法的情况!如果当前这一步是向右,那么只有它前面连续的一段向右可能影响到它. 考虑把连续的向右/下一 ...

  3. [HNOI2016]序列(莫队,RMQ)

    [HNOI2016]序列(莫队,RMQ) 洛谷  bzoj 一眼看不出来怎么用数据结构维护 然后还没修改 所以考虑莫队 以$(l,r-1) -> (l,r)$为例 对答案的贡献是$\Sigma_ ...

  4. 【原创】浅谈指针(十一)alloca函数

    前言 好几天没写了,最近网课,事情也比较多,今天多写点东西. 目录 前言 alloca函数 1.简介 2.反汇编看alloca 3.手工调用alloca函数 4.注意事项 alloca函数 1.简介 ...

  5. Java编程:Lock

    在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...

  6. Kafka 消费者是否可以消费指定分区消息?

    Kafa consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥 ...

  7. Python包装器

    def func(): print("func body") def f1(arg): print("f1:",arg) def wrapper(b): pri ...

  8. elasticsearch 了解多少,说说你们公司 es 的集群架构,索 引数据大小,分片有多少,以及一些调优手段 。

    面试官:想了解应聘者之前公司接触的 ES 使用场景.规模,有没有做过比较大 规模的索引设计.规划.调优. 解答: 如实结合自己的实践场景回答即可. 比如:ES 集群架构 13 个节点,索引根据通道不同 ...

  9. 遇到的问题之"项目启动加载不出页面"

    已找到解决方案1 需要启动这两个微服务(注意:这个属于个人,你们也可以看看是否关联到相关微服务未启动)

  10. spi详解

    来源:https://www.sohu.com/a/211324861_468626 1. SPI简介 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围 ...