好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Vue3 Reactive响应式原理逻辑详解

前言

本篇文章主要讲解 vue 响应式原理的 逻辑 ,也就是 vue 怎么从最开始一步步推导出响应式的 结构框架 。 先从头构建一个简单函数推导出Vue3的Reactive原理,最后再进行源码的验证。

一、怎么实现变量变化

怎么实现变量变化,相关依赖的结果也跟着变化

 当原本 price=5 变为 price=20 后 total 应该变为 40 ,但是实际 total 并不会改变。 解决办法可以这样,当变量改变了,重新计算一次,那么结果就会改变为最新的结果。

如果需要重新计算,我们需要将 total 语句存储为一个 函数 ,才能实现依赖的变量改变就进行一次依赖项计算。这里就用 effect 表示函数名。

来,试一下:

 ??,实现了变量 price 改变,依赖变量 price quantity 的变量 total 也发生改变。

下一步,我们要解决的问题是:应该怎么把 effect 存储起来,让代码更加有通用性,而不是一直复写 effect ,分离出其他的功能的函数各司其职,也就是大家常说的 解耦 。

二、怎么实现变量变化

怎么实现变量变化,变量改变后就取出effect执行

用什么存储 effect 呢?当然是用Set,因为Set会过滤出重复的元素,所以能够保证存储在Set中的函数不是重复的。 这里定义一个存储 effect 依赖的变量为 dep = new Set() ,定义 track 函数表示存储的过程。 定义 trigger 函数用以取出 dep 中相关的 effect 函数执行(这里定义的函数与Vue3源码同名同意义)。

effect : 会影响结果的函数(要实现响应式的依赖语句) track :保存所有的effect trigger : 当变量改变重新执行代码

 ??,解耦之后代码结构更清晰了。

下面需要解决的一个问题:一个object通常有多个属性,比如 product = { price: 5, quantity: 2 } ,在保存依赖时只创建了一个 dep 的集合,应该给 price 和 quantity 都创建 dep ,因为 total 的最终结果依赖这两个属性,其中任何一个改变都要触发 trigger 函数。创建了两个 dep 就需要一个容器将 dep 存储起来。

三、将多个dep存储在Map中

因为不同的属性名有自己对应的 dep ,所以我们用Map结构(键值对形式)来保存不同 dep 。

 ??,一个object的多个属性依赖问题解决,更具有通用性了。

下一个问题是:不可能只有一个对象,多个对象又怎么办? let product = { price: 5, quantity: 2 }   let user = { firstName: "Joe", lastName: "Smith" } ,比如两个对象的时候就需要进一步修改上面的代码了。

四、将多个object的depsMap继续存储起来

这里用 WeakMap 数据结构去存储多个需要响应式的object的 depsMap 。 WeakMap 的基本使用和 Map 差不多,只不过 WeakMap 只接受对象为键值,而 depsMap 是一个 Map 结构刚好(必须是)是对象类型。 targetMap 作为存储多个 depsMap 的容器名。

??,到这里已经基本实现了通用性的响应式代码了,但是还有最后一个问题就是:我们的代码都需要手动执行(自己添加 trigger 运行),不能自动运行。怎么让它能够自动检测变量改变,然后自动修改结果呢?

五、核心

通过Reflect和Proxy解决自执行问题

在JavaScript中,自动检测变量不就是 get 、自动修改变量不就是 set 吗?在Vue2.x版本中用ES5的 Obeject.defineProperty() 自带的 getter/setter 去解决这个问题。ES6中 Proxy 也能解决这个问题,但是 Proxy 不兼任IE浏览器,当时大家还讨论过说不知道尤大怎么去考虑这个问题,现在问题的答案就是——不考虑。也就是根本不考虑IE兼不兼容????。

Proxy 就是代理的意思,任何对真实数据的操作它都能拦截并且代理操作,也就是说 Object 上一些能实现的方法, Proxy 也能实现。 Proxy 使用语法是 new Proxy(target, hanler) , handler 是你想实现什么样的代理功能配置。 而 Reflect 就更神奇了,它的作用是取代 Object 类上的一些方法让 Obeject 类更纯粹的代表一个类,不要附加太多方法在上面,比如 a in obj 表示判断 obj 中是否有 a ,在 Reflect 中用 Reflect.has(a) 比较语义化的方式就可以代替之前的方法。

正是因为这样, Proxy 和 Reflect 就对应上了,都有 Object 上的方法。

稍微封装一下我们的函数,名叫 Reactive

 ??,至此,Vue3基本的响应式原理就解析完了。

六、源码解析(TypeScript)

  return 了 createReactiveObject 函数,所以去看 createReactiveObject 。

 前面的代码都是判断各种情况,我们就看最后几行

const observed = new Proxy(
    target,
    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
  )

可以看到 Proxy 的 handler 为 collectionHandlers 或者  baseHandlers ,继续选择一个看一看。

在  baseHandlers 中可以看到导出了 get/set/deleteProperty 等属性配置:

我们看一下 set :

 和我们之前的逻辑差不多,只不过真正实现就很复杂,因为有很多复杂条件需要去处理。

其他的 get 等方法也一样,做了很多条件判断处理,完善了每一种会出现的情况。

到此这篇关于Vue3 Reactive响应式原理逻辑详解的文章就介绍到这了,更多相关Vue3 Reactive响应式 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

查看更多关于Vue3 Reactive响应式原理逻辑详解的详细内容...

  阅读:31次