zoukankan      html  css  js  c++  java
  • 浅析Vue3响应式原理

      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 中是如何实现这个流程的呢?

      在 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()
            })
        }
    }

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

      Vue3 用 effect 函数存放动态变化的执行条件,用dep集合存储多个effect,用depMap存储对象的属性和dep,用targetMap存放对象和depMap。

  • 相关阅读:
    二分图 洛谷P2055 [ZJOI2009]假期的宿舍
    并查集 洛谷P1640 [SCOI2010]连续攻击游戏
    贪心 洛谷P2870 Best Cow Line, Gold
    贪心 NOIP2013 积木大赛
    快速幂 NOIP2013 转圈游戏
    倍增LCA NOIP2013 货车运输
    树形DP 洛谷P2014 选课
    KMP UVA1328 Period
    动态规划入门 BZOJ 1270 雷涛的小猫
    KMP POJ 2752Seek the Name, Seek the Fame
  • 原文地址:https://www.cnblogs.com/goloving/p/15483805.html
Copyright © 2011-2022 走看看