当前位置:首页 > 网站源码 > 正文内容

Vue响应式原理(vue响应式原理简书)

网站源码1年前 (2023-01-19)634

今天给各位分享Vue响应式原理的知识,其中也会对vue响应式原理简书进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

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响应式原理简书的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

扫描二维码推送至手机访问。

版权声明:本文由我的模板布,如需转载请注明出处。


本文链接:http://2565999.com/post/1402.html

分享给朋友:

“Vue响应式原理(vue响应式原理简书)” 的相关文章

拉大锯扯大锯的亲子游戏怎么做(拉大锯游戏规则及玩法)

拉大锯扯大锯的亲子游戏怎么做(拉大锯游戏规则及玩法)

今天给各位分享拉大锯扯大锯的亲子游戏怎么做的知识,其中也会对拉大锯游戏规则及玩法进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: 1、幼儿园中班优秀游戏教案《拉大锯...

手游网游戏交易平台是真的吗(手游交易平台是不是真的)

手游网游戏交易平台是真的吗(手游交易平台是不是真的)

今天给各位分享手游网游戏交易平台是真的吗的知识,其中也会对手游交易平台是不是真的进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: 1、这个游戏交易平台是不是真的?...

零元手游ap(零元手游激活码)

零元手游ap(零元手游激活码)

今天给各位分享零元手游ap的知识,其中也会对零元手游激活码进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: 1、什么手游适合0元党玩家 2、问道手游零元党怎么玩...

王者荣耀启动表情包(王者荣耀启动表情包一套)

王者荣耀启动表情包(王者荣耀启动表情包一套)

今天给各位分享王者荣耀启动表情包的知识,其中也会对王者荣耀启动表情包一套进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: 1、王者荣耀表情怎么自动出现 王者荣耀表情...

Iphone13怎么显示流量(iPhone13怎么显示流量数据)

Iphone13怎么显示流量(iPhone13怎么显示流量数据)

本篇文章给大家谈谈Iphone13怎么显示流量,以及iPhone13怎么显示流量数据对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。 本文目录一览: 1、苹果13流量开关怎么放到桌面 2、中...

使命召唤手游账号怎么换绑(使命召唤手游怎么换绑手机号)

使命召唤手游账号怎么换绑(使命召唤手游怎么换绑手机号)

今天给各位分享使命召唤手游账号怎么换绑的知识,其中也会对使命召唤手游怎么换绑手机号进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: 1、使命召唤账号怎么解绑微信...