国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

關于VUE響應式數(shù)據(jù)的最佳解釋

endiat / 1145人閱讀

摘要:遍歷執(zhí)行其中存儲的所有的匿名函數(shù)。如果我們使用一個類來管理相關依賴,這很接近的表現(xiàn)方式代碼看起來就像下面這樣你會發(fā)現(xiàn)現(xiàn)在匿名函數(shù)被儲存在而不是原來的。

許多前端框架(如Angular,React,Vue)都有自己的響應式引擎。通過理解如何響應,提議提升你的開發(fā)能力并能夠更高效地使用JS框架。本文中構建的響應邏輯與Vue的源碼是一毛一樣的!

響應系統(tǒng)

初見時,你會驚訝與Vue的響應系統(tǒng)。看看以下面這些簡單代碼

Price:${{price}}
Total:${{price*quantity}}
Taxes:${{totalPriceWithTax}}

更新頁面上的price

重新計算pricequantity的乘積,更新頁面

調用totalPriceWithTax函數(shù)并更新頁面

等等,你可能會疑惑為何Vue知道price變化了,它是如何跟蹤所有的變化?

這并非日常的JS編程會用到的

如果你疑惑,那么最大的問題是業(yè)務代碼通常不涉及這些。舉個例子,如果我運行下面代碼:

let price = 5
let quantity = 2
let total = price *quantity
price = 20
console.log(`total is ${total}`)

即便我們從未使用過Vue,我們也能知道會輸出10。

>> total is 10

更進一步,我們想要在price和quantity更新時

total is 40

遺憾的是,JS是一個程序,看著它它也不會變成響應式的。這時我們需要coding

難題

我們需要存儲計算的total,以便在pricequantity變化時,重新運行。

解決

首先我們需要告知應用“下面我要運行的代碼先保存起來,我可能在別的時間還要運行!”之后但我們更新代碼中pricequantity的值時,之前存儲的代碼會被再次調用。

// save code

let total = price * quantity

// run code

// later on rung store code again

所以通過記錄函數(shù),可以在變量改變時多次運行:

let price = 5
let quantity = 2
let total = 0
let target = null
target = function(){
    total = price * quantity
}
record() // 稍后執(zhí)行
target()

注意target 存儲了一個匿名函數(shù),不過如果使用ES6的箭頭函數(shù)語法,我們可以寫成這樣:

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

然后我們再簡單滴定義一下record函數(shù):

let storage = [] //在starage 中存放target函數(shù)
function record(){
    storage.push(target)
}

我們存儲了target(上述例子中就是{ total = price * quantity }),我們在稍后會用到它,那時使用target,就可以運行我們記錄的所有函數(shù)。

function target(){
    storage.forEach(run => run())
}

遍歷storage執(zhí)行其中存儲的所有的匿名函數(shù)。在代碼中我們可以這樣:

price = 20
console.log(total)  //  => 10
replay()
console.log(total)  //  => 40

足夠簡單吧!如果你想看看目前階段完整的代碼,請看:

let price = 5
let quantity = 2
let quantity = 0
let target = null
let storage = []
function record () {
    storage.push(target)
}
function replay() {
    storage.forEach(run => run())
}
target = () => {
    total = price * quantity
}
record()
target()


price = 20
console.log(total)  // => 10
replay()
console.log(total)  // => 40
難題

功能雖然可以實現(xiàn),但是代碼似乎不夠健壯。我們需要一個類,來維護目標列表,在需要重新執(zhí)行時來通知執(zhí)行。

解決

通過將所需要的方法封裝成一個依賴類,通過這個類實現(xiàn)標準的觀察者模式。
如果我們使用一個類來管理相關依賴,(這很接近VUE的表現(xiàn)方式)代碼看起來就像下面這樣:

class Dep {
    constructor(){
        this.subscribers = []
    }
    depend() {
        if(target && !this.subscribers.includes(target)){
            this.subscribers.push(target)
        }
    }
    notify() {
        this.subscribers.forEach(sub => sub())
    }
    
}

你會發(fā)現(xiàn)現(xiàn)在匿名函數(shù)被儲存在subscribers而不是原來的storage。同時,現(xiàn)在的記錄函數(shù)叫做depend而不是record,通知函數(shù)是notify而非replay。看看他們執(zhí)行情況:

const dep = new Dep()
let price = 5
let quantity = 2
let quantity = 0
let target = () => {
    total = price * quantity
}
dep.depend()  //將target添加進subscribers
target()      //執(zhí)行獲取total


price = 20
console.log(total)  // => 10
dep.notify()
console.log(total)  // => 40

現(xiàn)在代碼的復用性已經(jīng)初見端倪,但是還有一件別扭的事,我們還需要配置與執(zhí)行目標函數(shù)。

難題

以后我們會為每個變量創(chuàng)建一個Dep類,對此我們應該使用一個watcher函數(shù)來監(jiān)聽并更新數(shù)據(jù),而非使用這樣的方式:

let target = () => {
    total = price * quantity
}
dep.depend()  
target()      

期望中的代碼應該是:

watcher(() => {
    total = price * quantity
})
解決 實現(xiàn)watcher函數(shù)

在watcher函數(shù)中我們做了下面這些事:

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

如你所見,watcher接受一個myFunc作為參數(shù),將其賦值給全局變量target,并將它添加微訂閱者。在執(zhí)行target后,重置target為下一輪做準備!
現(xiàn)在只需要這樣的代碼

price = 20
console.log(total)  // => 10
dep.notify()
console.log(total)  // => 40

你可能會疑惑為什么target是一個全局變量的形式,而非作為一個參數(shù)傳入。這個問題在結尾處會明朗起來!

難題

現(xiàn)在我們擁有了一個簡單的Dep類,但我們真正想要的是每個變量都能擁有一個自己的Dep類。先讓我們把之前討論的特性變成一個對象吧!

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

我們先假設,每個屬性都有自己的Dep類:

現(xiàn)在我們運行

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

由于total需要依賴price和quantity兩個變量,所以這個匿名函數(shù)需要被寫入兩者的subscriber數(shù)組中!
同時如果我們又有一個匿名函數(shù),只依賴data.price,那么它僅需要被添加進price的dep的subscriber數(shù)組中

但我們改變price的值時,我們期待dep.notify()被執(zhí)行。在文章的最末,我們期待能夠有下面這樣的輸出:

>> total
10
>> price =20
>> total
40

所以現(xiàn)在我們需要去掛載這些屬性(如quantity和price)。這樣當其改變時就會觸發(fā)subscriber數(shù)組中的函數(shù)。

解決 Object.defineProperty()

我們需要了解Object.defineProperty函數(shù)
ES5種提出的,他允許我們?yōu)橐粋€屬性定義getter與setter函數(shù)。在我們把它和Dep結合前,我先為你們演示一個非常基礎的用法:

let data = { price: 5,quantity: 2}
Object.defineProperty(data,"price",{
    get(){
        console.log(`Getting price ${internalValue}`);
        return internalValue
    }
    set(newValue){
        console.log(`Setting price ${newValue}`);
        internalValue = newValue
    }
})
total = data.price * data.quantity  // 調用get
data.price = 20                      //  調用set

現(xiàn)在當我們獲取并設置值時,我們可以觸發(fā)通知。通過Object.keys(data)返回對象鍵的數(shù)組。運用一些遞歸,我們可以為數(shù)據(jù)數(shù)組中的所有項運行它。

let data = { price: 5,quantity: 2}
Object.keys(data).forEach((key) => {
    let internalValue = data[key]
    Object.defineProperty(data, key,{
        get(){
            console.log(`Getting ${key}:${internalValue}`);
            return internalValue
        }
        set(newValue){
            console.log(`Setting ${key} to ${newValue}`);
            internalValue = newValue
        }
    })
})
total = data.price * data.quantity  
data.price = 30     

現(xiàn)在你可以在控制臺上看到:

Getting price: 5
Getting quantity: 20
Setting price to 30
成親了
total = data.price * data.quantity

類似上述代碼運行后,獲得了price的值。我們還期望能夠記錄這個匿名函數(shù)。當price變化或事被賦予了一個新值(譯者:感覺這是一回事)這個匿名函數(shù)就會被促發(fā)。

Get => 記住這個匿名函數(shù),在值變化時再次執(zhí)行!
Set => 值變了,快去執(zhí)行剛才記下的匿名函數(shù)

就Dep而言:
Price被讀 => 調用dep.depend()保存當前目標函數(shù)
Price被寫 => 調用dep.notify()去執(zhí)行所有目標函數(shù)

好的,現(xiàn)在讓我們將他們合體,并祭出最后的代碼。

let data = {price: 5,quantity: 2}
let target = null
class Dep {
    constructor(){
        this.subscribers = []
    }
    depend() {
        if(target && !this.subscribers.includes(target)){
            this.subscribers.push(target)
        }
    }
    notify() {
        this.subscribers.forEach(sub => sub())
    }
}

Object.keys(data).forEach((key) => {
    let internalValue = data[key]
    const  dep = new Dep()
    Object.defineProperty(data, key,{
        get(){
            dep.depend()
            return internalValue
        }
        set(newValue){
            internalValue = newValue
            dep.notify()
        }
    })
})
function watcher(myFunc){
    target = myFunc
    target();
    target = null;
}
watch(() => {
    data.total = data.price * data.quantity
})

猜猜看現(xiàn)在會發(fā)生什么?

>> data.total
10
>> data.price = 20
20
>> data.total
40
>> data.quantity = 3 
3
>> data.total
60

正如我們所期待的那樣,pricequantity現(xiàn)在是響應式的了!當pricequantity更跟新時,被監(jiān)聽函數(shù)會被重新執(zhí)行!
現(xiàn)在你應該可以理解Vue文檔中的這張圖片了吧!

看到圖中紫色數(shù)據(jù)圈gettersetter嗎?看起來應該很熟悉!每個組件實例都有一個watcher實例(藍色),它從getter(紅線)收集依賴項。稍后調用setter時,它會通知觀察者導致組件重新渲染。下圖是一個我注釋后的版本。

雖然Vue實際的代碼愿彼此復雜,但你現(xiàn)在知道了基本的實現(xiàn)了。

那么回顧一下

我們創(chuàng)建一個Dep類來收集依賴并重新運行所有依賴(notify)

watcher函數(shù)來將需要監(jiān)聽的匿名函數(shù),添加到target

使用Object.defineProperty()去創(chuàng)建gettersetter

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96416.html

相關文章

  • 前方來報,八月最新資訊--關于vue2&3最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運,我不曉得。我只曉得,不認命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評論0 收藏0
  • VUE防抖與節(jié)流最佳解決方案——函數(shù)組件

    摘要:案例持續(xù)觸發(fā)事件時,并不立即執(zhí)行函數(shù),當毫秒內沒有觸發(fā)事件時,才會延時觸發(fā)一次函數(shù)。也以函數(shù)形式暴露普通插槽。這樣的場景組件用函數(shù)式組件是非常方便的。相關閱讀函數(shù)式組件自定義指令前言 有echarts使用經(jīng)驗的同學可能遇到過這樣的場景,在window.onresize事件回調里觸發(fā)echartsBox.resize()方法來達到重繪的目的,resize事件是連續(xù)觸發(fā)的這意味著echarts...

    OldPanda 評論0 收藏0
  • React 可視化開發(fā)工具 Shadow Widget 非正經(jīng)入門(之一:React 三宗罪)

    摘要:前言非正經(jīng)入門是相對正經(jīng)入門而言的。不過不要緊,正式學習仍需回到正經(jīng)入門的方式。快速入門建議先學會用拼文寫文檔注冊一個賬號,把庫到自己名下,然后用這個庫寫自己的博客,參見這份介紹。會用拼文寫文章,相當于開發(fā)已入門三分之一了。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點,既作為用戶手冊的補充,也從更本質角度幫助大家理解 Shadow Widget 為什么這...

    dongxiawu 評論0 收藏0

發(fā)表評論

0條評論

endiat

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<