亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

關(guān)于VUE響應(yīng)式數(shù)據(jù)的最佳解釋

endiat / 1200人閱讀

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

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

響應(yīng)系統(tǒng)

初見(jiàn)時(shí),你會(huì)驚訝與Vue的響應(yīng)系統(tǒng)。看看以下面這些簡(jiǎn)單代碼

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

更新頁(yè)面上的price

重新計(jì)算pricequantity的乘積,更新頁(yè)面

調(diào)用totalPriceWithTax函數(shù)并更新頁(yè)面

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

這并非日常的JS編程會(huì)用到的

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

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

即便我們從未使用過(guò)Vue,我們也能知道會(huì)輸出10。

>> total is 10

更進(jìn)一步,我們想要在price和quantity更新時(shí)

total is 40

遺憾的是,JS是一個(gè)程序,看著它它也不會(huì)變成響應(yīng)式的。這時(shí)我們需要coding

難題

我們需要存儲(chǔ)計(jì)算的total,以便在pricequantity變化時(shí),重新運(yùn)行。

解決

首先我們需要告知應(yīng)用“下面我要運(yùn)行的代碼先保存起來(lái),我可能在別的時(shí)間還要運(yùn)行!”之后但我們更新代碼中pricequantity的值時(shí),之前存儲(chǔ)的代碼會(huì)被再次調(diào)用。

// save code

let total = price * quantity

// run code

// later on rung store code again

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

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

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

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

然后我們?cè)俸?jiǎn)單滴定義一下record函數(shù):

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

我們存儲(chǔ)了target(上述例子中就是{ total = price * quantity }),我們?cè)谏院髸?huì)用到它,那時(shí)使用target,就可以運(yùn)行我們記錄的所有函數(shù)。

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

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

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

足夠簡(jiǎn)單吧!如果你想看看目前階段完整的代碼,請(qǐng)看:

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
難題

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

解決

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

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

你會(huì)發(fā)現(xiàn)現(xiàn)在匿名函數(shù)被儲(chǔ)存在subscribers而不是原來(lái)的storage。同時(shí),現(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添加進(jìn)subscribers
target()      //執(zhí)行獲取total


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

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

難題

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

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

期望中的代碼應(yīng)該是:

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

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

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

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

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

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

難題

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

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

我們先假設(shè),每個(gè)屬性都有自己的Dep類:

現(xiàn)在我們運(yùn)行

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

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

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

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

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

解決 Object.defineProperty()

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

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  // 調(diào)用get
data.price = 20                      //  調(diào)用set

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

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)在你可以在控制臺(tái)上看到:

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

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

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

就Dep而言:
Price被讀 => 調(diào)用dep.depend()保存當(dāng)前目標(biāo)函數(shù)
Price被寫(xiě) => 調(diào)用dep.notify()去執(zhí)行所有目標(biāo)函數(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)在會(huì)發(fā)生什么?

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

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

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

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

那么回顧一下

我們創(chuàng)建一個(gè)Dep類來(lái)收集依賴并重新運(yùn)行所有依賴(notify)

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/96416.html

相關(guān)文章

  • 前方來(lái)報(bào),八月最新資訊--關(guān)于vue2&3最佳文章推薦

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

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

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

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

    摘要:前言非正經(jīng)入門(mén)是相對(duì)正經(jīng)入門(mén)而言的。不過(guò)不要緊,正式學(xué)習(xí)仍需回到正經(jīng)入門(mén)的方式??焖偃腴T(mén)建議先學(xué)會(huì)用拼文寫(xiě)文檔注冊(cè)一個(gè)賬號(hào),把庫(kù)到自己名下,然后用這個(gè)庫(kù)寫(xiě)自己的博客,參見(jiàn)這份介紹。會(huì)用拼文寫(xiě)文章,相當(dāng)于開(kāi)發(fā)已入門(mén)三分之一了。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設(shè)計(jì)要點(diǎn),既作為用戶手冊(cè)的補(bǔ)充,也從更本質(zhì)角度幫助大家理解 Shadow Widget 為什么這...

    dongxiawu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<