ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Vue3 响应式原理

2021-10-16 12:33:23  阅读:145  来源: 互联网

标签:dep price Vue3 effect 响应 let key 原理 depMap


Vue3中响应式模块是如何工作的呢?
比如三个属性:价格price,数量quantity ,总价格total 。

let price = 5
let quantity = 2
let total = price * quantity

我们想要做到响应式,即更新price 时,网页上的price 随之更新,而计算后的total 也随之更新。

定义一个计算函数:

function getTotal (price, quantity ){
  let total = price * quantity 
}

我们想要在更新了price 后,再次执行这个 getTotal 函数,这样就能得到一个响应式的 total。



在Vue3中,使用了多种数据结构来执行整个机制,例如:

  • 定义 track 用于保存这段函数代码。
  • 定义 effect 用于执行代码。
  • 定义 trigger 用于执行所有存储的代码。

首先将 上面的 getTotal 用匿名函数的方式写入effect:

let effect = () => {
    total = price * quantity
}

用一个集合 dep 来保存所有的effect。

let dep = new Set()

track 作为函数,往集合dep 里添加effect :
function track() {
    dep.add(effct)
}

trigger 作为函数,作用是遍历 dep 中所有 effect ,并逐一执行。

function trigger() {
    dep.forEach( effct => effct() )
}


单个响应式对象

通常来说,一个对象有多个property ,例如上面提到的price和quantity。

let product = { price : 5, quantity : 2  }

我们想让每个property 都拥有自己的dep ,这样无论修改哪个属性,total都可以变得“响应式”。


可以定义一个dep map (ES6 map), map 的 key 是属性的名字,比如price 或者quantity 。map 的 value 是一个dep ,即一个effect 集合。

track函数和trigger函数需要改写成:

const depMap = new Map()

function track(key) {
    let dep = depMap.get(key)   // 获取property的dep
    if(!dep) {
        depMap.set(key, (dep = new Set()))   //如果不存在dep,则创建一个
    }
    dep.add(effect)   // 往集合中添加effect
}

function trigger(key) {
    let dep = depMap.get(key)   // 获取property的dep
    if(dep) {				
        dep.forEach(effect => {   // dep存在,则遍历dep中的所有effect
            effect()
        })
    }
}

现在汇总所有的代码,让我们看看响应式是否生效:

let product = { price : 5, quantity : 2 }
let total = 0

let effect = () => {
    total = product.price * product.quantity
}

const depMap = new Map()

function track(key) {
    let dep = depMap.get(key)   // 获取property的dep
    if(!dep) {
        depMap.set(key, (dep = new Set()))   //如果不存在dep,则创建一个
    }
    dep.add(effect)   // 往集合中添加effect
}

function trigger(key) {
    let dep = depMap.get(key)   // 获取property的dep
    if(dep) {				
        dep.forEach(effect => {   // dep存在,则遍历dep中的所有effect
            effect()
        })
    }
}

track('quantity')
effect()
console.log(total)   //10

product.quantity = 3
trigger('quantity')
console.log(total)    //15

你也可以复制这段代码到控制台,测试运行结果,笔者测试成功。


很好!!!现在我们就得到了一个响应式的对象product了,修改product 的任何属性,只要调用以上方法,total都会发生变化。



多个响应式对象

但上述代码中的depMap只是存放了单个对象product的属性及dep,如果现在我们需要多个响应式的对象呢?比如一个user对象:

let user = {
    name : 'LUJT',
    age  : 19
}

既然一个depMap只能处理一个响应式对象,如果存在多个对象,一种显而易见的思路是,再定义一个Map,key为某个对象,value为一个depMap。


在此处我们使用WeakMap。MDN中关于WeakMap的定义是:

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。


非常符合我们的需要!因此我们定义一个WeakMap如下:

const targetMap = new WeakMap

我们的核心代码可以改写为:

const targetMap = new WeakMap   //targetMap用于保存对象及其depMap

function track(target, key) {
    let depMap = targetMap.get(target)   // 获取对象的depMap
   	if(!depMap) {
        targetMap.set(target, (depMap = new Map()))   //如果不存在depMap,则创建一个
    }
   
    let dep = depMap.get(key)   // 获取property的dep
    
    if(!dep) {
        depMap.set(key, (dep = new Set()))   //如果不存在dep,则创建一个
    }
    
    dep.add(effect)   // 往集合中添加effect
}

function trigger(target, key) {
    const depMap = targetMap.get(target)
    if(!depMap){return}
    
    let dep = depMap.get(key)   // 获取property的dep
    if(dep) {				
        dep.forEach(effect => {   // dep存在,则遍历dep中的所有effect
            effect()
        })
    }
}

如此,我们设置多个对象的响应式工作就完成了。


总结

最后总结一下笔者对“响应式”的理解:

所谓响应式,就是对某个东西的局部进行修改时,整体也随之而变化。

在Web开发中,响应式设计为我们提供了一种“动态变化”的特性。

无论是整个网页大小的变化,导致整个网页布局的更新,还是Vue中对象属性的改动,导致对象整体的变化,都是一种“响应式”。


Vue3用effect函数存放动态变化的执行条件

用dep集合存储多个effect

用depMap存储对象的属性和dep

用targetMap存放对象和depMap

标签:dep,price,Vue3,effect,响应,let,key,原理,depMap
来源: https://www.cnblogs.com/LuJT/p/15413819.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有