本帖最后由 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()来实现。
- // 模拟 Vue 中的 data 选项
- let data = {
- msg: 'hello'
- }
- // 模拟 Vue 的实例
- let vm = {}
- // 数据劫持:当访问或者设置 vm 中的成员的时候,做一些干预操作
- Object.defineProperty(vm, 'msg', {
- // 可枚举(可遍历)
- enumerable: true,
- // 可配置(可以使用 delete 删除,可以通过 defineProperty 重新定义)
- configurable: true,
- // 当获取值的时候执行
- get () {
- console.log('get: ', data.msg)
- return data.msg
- },
- // 当设置值的时候执行
- set (newValue) {
- console.log('set: ', newValue)
- if (newValue === data.msg) {
- return
- }
- data.msg = newValue
- // 数据更改,更新 DOM 的值
- document.querySelector('#app').textContent = data.msg
- }
- })
- // 测试
- vm.msg = 'Hello World'
- console.log(vm.msg)
复制代码
|