當前位置:成語大全網 - 書法字典 - Vue如何實現響應式系統?

Vue如何實現響應式系統?

最近深入研究了Vue實現響應性的部分源代碼,記錄了自己的壹些收獲和思考,希望對看這篇文章的人有所幫助。有問題請指出來,大家壹起進步。

什麽是響應式系統?

壹句話:數據變化驅動觀點更新。這樣我們就可以用“數據驅動”的思維來編寫我們的代碼,更加關註業務而不是dom操作。實際上,Vue響應的實現是壹個變化跟蹤和應用的過程。

Vue響應原理

通過數據劫持攔截數據變更;以依賴集合的方式觸發視圖更新。使用es5 Object.defineProperty攔截setter和getter的數據;Getter收集依賴項,setter觸發依賴項更新,而component render成為watcher回調並被添加到相應數據的依賴項中。

發布訂閱

采用發布-訂閱的設計模式,觀察者作為發布者,觀察者作為訂閱者。它們之間沒有直接的交互,通過Dep進行統壹調度。

觀察者負責攔截get、set;get時觸發dep添加依賴,設置時調度dep發布;添加Watcher將觸發訂閱數據的獲取,並將其添加到dep調度中心的訂戶隊列中。

下面的UML類圖是Vue實現響應式功能的類,以及它們之間的引用關系。

方法只包含部分屬性

上圖中的類已經標識的很清楚了,但是仍然需要壹個調用圖來使調用過程更加清晰,如下圖所示。

在響應數據對象中,被劫持的每個鍵的get/set函數關閉Dep調度實例。此圖顯示了密鑰更改期間的數據流。

部分源代碼

數據變更過程中的訂閱/發布模式在上圖中已經很清楚的展示出來了,從中我們已經知道,通過添加壹個watcher,可以訂閱某個數據變更。那麽,如果我們只需要以觀察者的身份訂閱組件渲染,那麽數據驅動視圖的渲染就水到渠成了。Vue就是這麽做的!

以下代碼片段來自Vue.prototype._mount函數。

callHook(虛擬機,“裝載前”)

vm。_watcher =新觀察器(vm,()= & gt{

vm。_更新(虛擬機。_render(),補水)

},noop)

補水=假

//手動裝入實例,調用裝入自身

//在其插入的掛鉤中為呈現創建的子組件調用mounted

如果(vm。$vnode == null) {

vm。_isMounted = true

調用掛鉤(虛擬機,“已安裝”)

}

思考壹些問題

#person分配壹個新對象。新對象中的屬性也有響應嗎?

var vm = new Vue({

埃爾:' #app ',

數據:()= & gt({

人:空

})

})

vm.person = {name: 'zs'}

setTimeout(()= & gt;{

//更改名稱

vm.person.name = 'finally zs '

}, 3000)

回答:有求必應。

原因:因為當Vue劫持設置時,它將再次觀察值。源代碼如下。

函數反應設置器(新值){

/* ...省略壹些代碼*/

//新值將在這裏再次被攔截。

childOb = observe(newVal)

dep.notify()

}

#當我們監聽多級屬性時,上級引用是否會發生變化,從而觸發回調?

var vm = new Vue({

數據:()= & gt({

人物:{姓名:'令狐洋蔥' }

}),

觀察:{

person.name'(val) {

console.log('名稱已更新',val)

}

}

})

vm.person = {}

回答:是的。

原因:當person.name作為表達式傳入Watcher時,會被解析成這樣的函數。

()= & gt{this.vm.person.name}

這將首先觸發person get,然後觸發nameget所以我們配置的回調函數,不僅加到了名字依賴上,也加到了person上。

#那麽最後壹個問題,如果給person分配了壹個新對象,如何對舊對象以及對舊對象的依賴進行垃圾回收?

舊對象的回收:由於舊對象的直接引用只是vue實例上的person,而person已經切換到新的引用,所以舊對象如果沒有被引用,就會被回收。

舊對象上的依賴項dep和觀察器的依賴項仍然存在;但run執行時會調用watcher的get()獲取當前值;新的依賴關系收集將在get中執行,舊的依賴關系將在收集後被清除。

具體源代碼如下:

/**

*評估getter,並重新收集依賴項。

*/

get () {

推送目標(this)

const value = this . getter . call(this.vm,this . VM)

//“觸摸”每個屬性,以便它們都被跟蹤為

//深度觀察的依賴性

如果(this.deep) {

遍歷(值)

}

popTarget()

this.cleanupDeps()

返回值

}

#當我們多次同步修改名稱時,回調函數會多次觸發嗎?

var vm = new Vue({

數據:()= & gt({

人物:{姓名:'令狐洋蔥' }

}),

觀察:{

person.name': (val) {

console.log('名稱已更新:'+ val)

}

}

})

vm.person = {name: 'zs'}

Vm.person.name = '無敵'

回答:不會,因為手表回調函數的執行是異步的,會重復。妳可以通過sync強制配置它同步運行,它會被執行兩次。

自己實現壹個響應式系統

只包含核心函數,具體源代碼可以看這裏。vuejs.org/v2/guide/reactivity.html.