Vue响应式原理

1.响应式

  响应式?什么是响应式?是不是大家和我一样有这样疑问。我们先把这个高深的名词放一边。看看接下来的例子

    例如:在家的时候,父母都会问你在不在家吃饭,然后等你回答,如果你回答是就是在家吃饭,就会做你的那份,如果回答不是就在家吃饭,就不会给你留饭了。

       而你回答得这一举动就是“响应”,“响应”是需要时间的(因为你现在可能在打游戏,可能不会即时的回答父母)。而父母为了能够用能够立马得到你的回答

       就不得不采取一些手段(拔掉你得路由)。所以“响应式”就是通过一些“手段”当父母问你在不在家吃饭让你即时回复。

 

  那这种情况在我们编程时是什么样得?

let price = 5 
let quantity = 2

//    此时总价是 10,但是我希望改变价格为15然后得出我们得总价
let total = price * quantity 

//    因此我把价格设为了15
price = 15

//    但是打印结果还是10(就像我例子中说的你并没有对父母问你是否在家吃饭,即时做出回应)
console.log('total is' + total)

  那么怎么解决这种情况实现响应式。(让你即时回应)

    我们的目的:当price改变的时候,total也要即时改变

    思路:

    首先我们需要一些方式来告诉我们的应用,“我将要运行的代码,我存储在某一个地方,有时候(当数据发生改变的时候),我需要你再运行一遍”。然后我们

    运行代码,如果price 或 quantity 更新的时候,再一次运行存储好的代码。

   

    let price = 5
    let quantity = 2
    let total = 0
    let leave = 100

    let target = null
    let target1 = null

    //  求总价
    target = () => {
        total = price * quantity
    }

    //  求余额
    target1 = () => {
        leave = 100 - total
    }

    //  storage用来存储类似target函数。(比如我想求自己剩下的前)
    let storage = []

    //  record函数就用来把target函数放入storage中
    function record() {
        storage.push(target)
        storage.push(target1)
    }

    //  执行storage方法
    function replay() {
        storage.forEach(run => run())
    }

    record()

    //  第一次买
    replay()
    console.log("total: " + total) // => 10
    console.log("leave: " + leave)//  => 90

    //  第二次买商家心黑突然改价格 price 5 => 15
    price = 15
    replay()
    console.log("total: " + total) // => 30
    console.log("leave: " + leave)//  => 70

    这样不就实现了我们想要的效果:“价格改 总价就马上改”

    但还有一个问题:如果向上面写的那样代码不能重复利用(复用)就只能固定的写成这样。这样好吗?这样不好。如果你想多次使用不就要多次编写一下吗?

            那把他封装成函数?也不行因为这样还是要多次调用。这就要提及ES6的新特性class了。

    所以 => 大佬就把这个封装为了dependcy类(依赖)这个类就实现了观察者模式(这个东西只有初步了解之后详细了解了在说)。

    这里我就不为大家展示这个类是怎么具体实现的了,有兴趣的去看大佬的(这里有一篇博客是具体介绍 响应式原理: https://www.alonehero.com/2018/08/22/javascript )

    之后我们就用这个类来实现这个功能

    //  引入dependcy类
    const dep = new Dep()

    let price = 5
    let quantity = 2
    let total = 0

    //  在这段代码中只能写target
    let target = () => { total= price * quantity }
    //  没看懂为什么是这么写的?因为dependcy这个类中depend方法相当上面的record方法就是把target 给push到storage中
    dep.depend()

    //  第一次买
    target()
    console.log(total)  // => 10

    //  第二次买
    price = 20
    //  相当于上面的replay
    dep.notify()
    console.log(total)  // => 40

    这样就解决问题了?NO,NO,NO。这样虽然代码复用解决了但不是最优的。大佬们又看这些函数不顺眼了,代码看着一点简洁别扭,一个字不爽。于是watcher出现了

    它直接封装了target、depend这两个函数,接下里你只需要定price然后执行notify得结果。相对于人更直接懒得去把什么target放到storage里面然后去执行。

    watcher(()=>{
       total = price * quantity
    })

    watcher:

    function watcher(myFunc) {
        target = myFunc 
        dep.depend()  
        target()  
        target = null 
    }

    这就是我们所实现了响应式。

  实际上运用是怎么的了?

    在之前我提到是例子中price(价格)是我们想控制的变量,那么我现在也想控制一下quantity(数量),可能接下来也有许多我想要控制的变量,这时候就需要我们使

    用一个对象来封装这些我想控制的变量让它做为一个一个的属性方便我们操作。每个属性也有响应的dep(dependcy)类来封装target函数。

    //    把价格、数量封装为一个类
    let data = { price : 5, quantity : 2}

    所以在运行函数的时候watcher:

    watcher(() => {
        total = data.price * data.quantity
    })

    看到这是不是又有小伙伴晕了?这里面两个变量都在变,这又咋整哦。而且还有一个问题就是price(价格这个变量)我不想让它和quantity(数量相乘),我想做一些单独

    操作。怎么办?

    思路:我们需要找到方法hook住对象的属性(如 price 或quantity),当有地方使用到他们的时候,我们能够把对应target保存到对应的subscriber数组中,当属性值发生变

       化的时候,我们能够运行保存在sunscriber中的函数,那利用什么方法能够做到呢?(hook勾住的意思,也就是在消息过去之前,可以先把消息勾住,不让其传递,

       你可以优先处理。)

  Object.definedProperty

    这个一登场就解决了hook对象属性的问题。那这个函数的功能是什么?这个方法能够允许我们为一个属性定义 setter 和getter 方法。

    let data = {price; 5, quantity: 2}
    let internalValue = data.price // 初始值

    // 这个方法只能由于属性
    Object.defineProperty(data, 'price', { 
        get() { 
          console.log(`Getting price: ${internalValue}`)
          return internalValue
        },
    
       set(newVal) { 
           console.log(`Setting price to ${newVal}`
           internalValue = newVal
        }
    })

    total = data.price * data.quantity // 调用get函数
    data.price = 20 // 调用set函数

     明天继续。。。

 

 

 

  

posted @ 2021-03-06 23:27  强迫生活的大熊猫  阅读(79)  评论(0)    收藏  举报