Vue响应式原理(vue响应式原理简书)
今天给各位分享Vue响应式原理的知识,其中也会对vue响应式原理简书进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
- 1、Vue3.0采用新特性Proxy来实现数据状态的响应,它的原理是什么?
- 2、Proxy(vue响应式原理:数据侦测--数据劫持和数据代理)
- 3、【手把手教你搓Vue响应式原理】(五) Watcher 与 Dep
- 4、Vue响应式原理(Object.defineProperty)全过程解析
- 5、深入理解和手写--Vue响应式原理/剖析Dep与Wacher关系
Vue3.0采用新特性Proxy来实现数据状态的响应,它的原理是什么?
Vue3 使用了 Proxy 替换了原来的 Object.defineProperty 来实现数据响应。
很简单,直接Vue引入reactive方法,接收一个对象参数,就实现了数据的响应式:
reactive 内部的核心代码 简化 如下:
首先判断传入的参数类型是否可以用于观察,目前支持的类型为 Object|Array|Map|Set|WeakMap|WeakSet。
接下来判断参数的构造函数,根据类型获得不同的 handlers。这里我们就统一使用 baseHandlers ,因为这个已经覆盖 99% 的情况了。只有 Set, Map, WeakMap, WeakSet 才会使用到 collectionHandlers。
对于 baseHandlers 来说,最主要的是劫持了 get 和 set 行为,这两个行为同时也能原生劫持 数组下标修改值及对象新增属性的行为, 这一点非常重要,因为 Object.defineProperty 就不行。
最后就是构造一个 Proxy 对象完成数据的响应式。相比 Object.defineproperty 一开始就要 递归遍历整个对象 的做法来说,使用 Proxy 性能会好得多。比如原来 forEach 遍历:
接下来当我们去使用 state 对象的时候,就能劫持到内部的行为。
读取时:state.num 就会触发 get 函数;
修改时:state.num = 100 就会触发 set 函数。
以下是这两个函数的核心(TS语法):
对于 get 函数来说,获取值肯定是最核心的一步骤了。接下来是调用 track,这个和 effect 有关,下文再说。最后是判断值的类型,如果是对象的话就继续包装成 Proxy。
对于 set 函数来说,设置值是第一步骤,然后调用 trigger,这也是 effect 中的内容。
简单来说,如果某个 effect 回调中有使用到 state.num,那么这个回调会被收集起来,并在调用 state.num =100 时触发。
那么怎么收集这些内容呢?这就要说说 targetMap 这个对象了。它用于存储依赖关系,类似以下结构,这个结构会在 effect 文件中被用到
先来解释下三者到底是什么,这个很重要 :
这里笔者把这些内容脱离源码串起来讲一下流程。
首先创建一个 Proxy 对象,targetMap 会把这个对象收集起来当做 key。
接下来调用 effect 回调的时候会把这个回调保存起来,用于下面的依赖收集。在调用的过程中会触发 counter 的 get 函数,内部调用了 track 函数,这个函数会使用到 targetMap。
这里首先通过 target 从 targetMap 中取到一个对象,这个对象也就是 target 所有的依赖关系。那么对于 counter.num 来说,num 就是这个对象的 key(这里如果有点模糊的话可以先看下上面的数据结构),值是一个依赖回调的集合,因为 counter.num 可能会被多个地方依赖到。
回调执行完毕以后会把保存的回调销毁掉。
当我们调用 counter.num = 7 时,触发 set 函数,内部调用 trigger 函数,同样会使用到 targetMap。
同样通过 target 取到一个对象,然后通过 key 也就是 num 去取出依赖集合,最后遍历这个集合执行里面所有的回调函数。
另外对于 computed 来说,内部也是使用到了 effect,无非它的回调不会在调用 effect 后立即执行,只有当触发 get 行为以后才会执行回调并进行依赖收集,举个例子:
对于以上代码来说,computed 的回调永远不会执行,只有当使用到了 cValue.value 时才会执行回调,然后接下来的操作就和上面的没区别了。
Proxy(vue响应式原理:数据侦测--数据劫持和数据代理)
Object.defineProperty : 通过设定对象属性getter/setter方法来监听数据的变化,同时getter也用于依赖收集,而setter在数据变更时通知订阅者更新视图。
1.无法检测到对象属性的新增或删除
由于js的动态性,可以为对象追加新的属性或者删除其中某个属性,这点对经过Object.defineProperty方法建立的响应式对象来说,只能追踪对象已有数据是否被修改,无法追踪新增属性和删除属性,这就需要另外处理。
2.不能监听数组的变化
vue在实现数组的响应式时,它使用了一些hack,把无法监听数组的情况通过重写数组的部分方法来实现响应式,这也只限制在数组的push/pop/shift/unshift/splice/sort/reverse七个方法,其他数组方法及数组的使用则无法检测到。
Proxy,字面意思是代理,是ES6提供的一个新的API,用于修改某些操作的默认行为,可以理解为在目标对象之前做一层拦截,外部所有的访问都必须通过这层拦截,通过这层拦截可以做很多事情,比如对数据进行过滤、修改或者收集信息之类。借用 proxy的巧用 的一幅图,它很形象的表达了Proxy的作用。
ES6原生提供的Proxy构造函数,用法如下:
其中obj为Proxy要拦截的对象,handler用来定制拦截的操作,返回一个新的代理对象proxy;Proxy代理特点:
1.Proxy的代理针对的是整个对象,而不是像Object.defineProperty针对某个属性。只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性
2.Proxy也可以监听数组的变化
参考:
【手把手教你搓Vue响应式原理】(五) Watcher 与 Dep
【手把手教你搓Vue响应式原理】(一)初识Vue响应式
【手把手教你搓Vue响应式原理】(二)深度监测对象全部属性
【手把手教你搓Vue响应式原理】(三)observe 以及 ob
【手把手教你搓Vue响应式原理】(四) 数组的响应式处理
之前已经将数据劫持已经全部完成了。
那么,接下来,主要的要点就是在于两点,依赖收集和触发依赖更新。
它的意义主要在于控制哪些地方使用了这个变量,然后,按照最小的开销来更新视图 。
首先,要先明白,依赖是什么,比方说在我们的模板中有 {{a}} ,那么,这个地方就有对于变量 a 的依赖。
在模板编译的时候,就会触发 a 变量的 getter 。
然后,当我们执行 a++; 的时候,那么,我们就要触发依赖的更新,当初模板中 {{a}} 的地方,就要更新,是吧!
所以,我们都是 在 getter 中收集依赖,在 setter 中触发依赖更新 。
这一节的内容,主要就是用来专门讲清楚这两件事情。
依赖收集和触发依赖更新主要由两个类来完成, Dep 和 Watcher 。
Dep 和 Watcher 在设计模式中,就是 发布-订阅者 的模式。
而依赖,你可以理解为所谓的订阅者。
Dep 说白了就是发布者,它的工作就是依赖管理,要知道哪些地方用到了这个变量,可能用到这个变量的地方有很多,所以,它会有多个订阅者。
然后,每个变量都应该有属于自己的 Dep ,因为每个变量所在的依赖位置是不一样的,所以他们的订阅者也不一样。
然后在变量更新之后,就去通知所有的订阅者(Watcher),我的变量更新了,你们该触发视图更新了。
Watcher 说白了就是订阅者,它接受 Dep 发过来的更新通知之后,就去执行视图更新了。
它其实就是所谓的 watch 监听器,变量改变之后,执行一个回调函数。
我们先按照图例来创建我们的 Dep 类
根据我们的需求:
Dep 我们在前面也说了,每个属性都应该有它自己的 Dep ,用来管理依赖。
所以,首先,如果我们在 Observer 中创建 Dep,那不就可以了。毕竟 Observer 会遍历到每一个对象。
所以,很明显,我们可以在 defineReactive 的 get 中收集依赖
因为有了 if(Dep.target) 的判断,所以, 只有绑定 Watcher 的变量触发 getter 时,才会添加依赖 。
这个 Dep.target 其实就是 Watcher 的实例
所以,很明显,我们可以在 defineReactive 的 set 中收调用 notify() 方法告知 Watcher 实例,数据更新了。
至此, Dep 的所有职责,我们已经帮它完成了。
其实照道理应该有一个删除依赖,我们这里就不再扩展了。
首先, Watcher 实例应该大家会相对而言更加好理解点,因为,我们有一个 watch 侦听器,大家一定都很熟悉,这两个其实一样。
我们先按照图例来创建我们的 Watcher 类
根据我们的需求:
这个 parsePath 需要单独拎出来说一下,比方说我们现在有这么一个对象
我们要监听到 a.b.c.d ,所以,我们需要下面的这种格式
所以,这个 get 很明显就有点难度了。 我们需要通过循环 拿到 a.b 然后 .c 然后 .d。
我们将这个方法命名为 parsePath 。
入参接受我们的 b.c.d ,我们可以看到 第一句执行之后 segments=['b','c','d'] ,然后进行第二层,这是返回了一个方法,按照循环,那就是 obj=obj.b = obj=obj.c = obj=obj.d ,所以,就是返回一个对象的 obj.b.c.d,相当于是遍历字符串中的属性树。
在执行 a.b.c.d=55; 的同时,我们的控制台就会输出 ok 55 10 。
【尚硅谷】Vue源码解析之数据响应式原理
Vue响应式原理(Object.defineProperty)全过程解析
忽视掉和响应式数据无关的部分,到这里基本就是mount结束的地方了,总结下都干了什么,触发beforeMount生命周期,new了一个Watcher对象,渲染模板,触发数据的get初始化,对每个响应式数据的Dep实例进行依赖收集,然后触发Mounted生命周期。
大致流程就是这样了,似乎写的有点乱,如有问题欢迎大佬们指正
深入理解和手写--Vue响应式原理/剖析Dep与Wacher关系
其实从因为单词意思中是依赖,观察;
vue中依赖的意思:数据和模板表达式依赖关系,一 对 多。
vue中观察的意思:模板表达式和数据的关系, 一 对 多。
由于vue要是实现数据和视图双向绑定,必须要监听数据改变,也要监听视图变化,从而定义两个核心对象,Dep对象收集依赖,Wacher对象监听视图 模板表达式。
下面详细介绍两个对象咋样关联的,它们之间到底存在什么联系
| 当数据变化了咋样通知视图更新? |设置数据 = defineObserve的set方法 = Dep.notify = 遍历watcher.update = 调用传过来的更新节点cb = 从而更新视图最新状态数据 |
注意
关于Vue响应式原理和vue响应式原理简书的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。