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。

  • 相关阅读:
    EFI下WIN8.1和Ubuntu的双系统安装
    硬盘损坏,全盘数据没了,杯具
    GEC2440的RTC时钟
    纠正一下apache2服务器的搭建
    qt和html的比较
    dump做个备份,发个随笔记录下
    忙了1天的qte-arm环境的搭建
    内核版本不同导致无法加载驱动
    wayne生产环境部署(360的容器发布平台-开源)
    openstack swift curl 常用操作
  • 原文地址:https://www.cnblogs.com/goloving/p/15483805.html
Copyright © 2011-2022 走看看