找回密码
 立即注册
查看: 71|回复: 0

Vue2.x 响应式原理

[复制链接]

8

主题

1

回帖

72

积分

注册会员

积分
72
发表于 2022-9-19 13:42:28 | 显示全部楼层 |阅读模式
本帖最后由 MalegeA 于 2022-9-19 13:48 编辑

Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM树上。
  • 虚拟DOM (Virtaul DOM): 用 js 对象模拟的,保存当前视图内所有 DOM 节点对象基本描述属性和节点间关系的树结构。用 js 对象,描述每个节点,及其父子关系,形成虚拟 DOM 对象树结构。
  • 因为只要在 data 中声明的基本数据类型的数据,基本不存在数据不响应问题,所以重点介绍数组和对象在vue中的数据响应问题,vue可以检测对象属性的修改,但无法监听数组的所有变动及对象的新增和删除,只能使用数组变异方法及$set方法。
可以看到,arrayMethods 首先继承了 Array,然后对数组中所有能改变数组自身的方法,如 push、pop 等这些方法进行重写。重写后的方法会先执行它们本身原有的逻辑,并对能增加数组长度的 3 个方法 push、unshift、splice 方法做了判断,获取到插入的值,然后把新添加的值变成一个响应式对象,并且再调用 ob.dep.notify() 手动触发依赖通知,这就很好地解释了用 vm.items.splice(newLength) 方法可以检测到变化
总结:Vue 采用数据劫持结合发布—订阅模式的方法,通过 Object.defineProperty() 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
  • Observer 遍历数据对象,给所有属性加上 setter 和 getter,监听数据的变化
  • compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情
  • 在自身实例化时往属性订阅器 (dep) 里面添加自己
  • 待属性变动 dep.notice() 通知时,调用自身的 update() 方法,并触发 Compile 中绑定的回调
Object.defineProperty(),那么它的用法是什么,以及优缺点是什么呢?
  • 可以检测对象中数据发生的修改
  • 对于复杂的对象,层级很深的话,是不友好的,需要经行深度监听,这样子就需要递归到底,这也是它的缺点。
  • 对于一个对象中,如果你新增加属性,删除属性,**Object.defineProperty()**是不能观测到的,那么应该如何解决呢?可以通过Vue.set()和Vue.delete()来实现。
  1. // 模拟 Vue 中的 data 选项
  2. let data = {
  3.     msg: 'hello'
  4. }
  5. // 模拟 Vue 的实例
  6. let vm = {}
  7. // 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
  8. Object.defineProperty(vm, 'msg', {
  9.   // 可枚举(可遍历)
  10.   enumerable: true,
  11.   // 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
  12.   configurable: true,
  13.   // 当获取值的时候执行
  14.   get () {
  15.     console.log('get: ', data.msg)
  16.     return data.msg
  17.   },
  18.   // 当设置值的时候执行
  19.   set (newValue) {
  20.     console.log('set: ', newValue)
  21.     if (newValue === data.msg) {
  22.       return
  23.     }
  24.     data.msg = newValue
  25.     // 数据更改,更新 DOM 的值
  26.     document.querySelector('#app').textContent = data.msg
  27.   }
  28. })

  29. // 测试
  30. vm.msg = 'Hello World'
  31. console.log(vm.msg)
复制代码

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|粤嵌技术交流空间

GMT+8, 2025-7-8 00:48 , Processed in 0.669237 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表