91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

通過圖帶你深入了解vue的響應式原理

發布時間:2020-10-11 01:38:08 來源:腳本之家 閱讀:254 作者:laihuamin 欄目:web開發

前言

如果自己去實現數據驅動的模式,如何解決一下幾個問題:

  • 通過什么手段去知道我的數據變了?
  • 通過什么東西去同步更新視圖?

數據劫持——obvserver

我們需要知道數據的獲取和改變,數據劫持是最基礎的手段。在Obeserver中,我們可以看到代碼如下:

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// ...
},
set: function reactiveSetter (newVal) {
// ...
}
})

通過Object.defineProperty這個方法,我們可以在數據發生改變或者獲取的時候,插入一些自定義操作。同理,vue也是在這個方法中做依賴收集和派發更新的。

綁定和更新視圖——watcher

從初始化開始,我們渲染視圖的時候,便會生成一個watcher,他是監視視圖中參數變化以及更新視圖的。代碼如下:

// 在mount的生命鉤子中
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)

當然,我們可以保留疑問:

  • watcher是怎么去更新視圖的
  • 數據又是怎么和watcher聯動起來的

具體的綁定和更新的流程,我們到后續的依賴收集中講解。

我們先來講講響應式系統中涉及到的設計模式。

發布訂閱模式

在發布訂閱模式中,發布者和訂閱者之間多了一個發布通道;一方面從發布者接收事件,另一方面向訂閱者發布事件;訂閱者需要從事件通道訂閱事件

以此避免發布者和訂閱者之間產生依賴關系

通過圖帶你深入了解vue的響應式原理

vue的響應式流程

vue的響應式系統借鑒了數據劫持和發布訂閱模式。

通過圖帶你深入了解vue的響應式原理

Vue用Dep作為一個中間者,解藕了Observer和Watcher之間的關系,使得兩者的職能更加明確。

那具體是如何來完成依賴收集和訂閱更新的呢?

依賴收集過程

依賴收集的流程

舉個例子

<div id="app">
{{ message }}
{{ message1 }}
<input type="text" v-model="message">
<div @click="changeMessage">改變message</div> 
</div>
var app = new Vue({
el: '#app',
data: {
message: '1',
message1: '2',
},
methods: {
changeMessage() {
this.message = '2'
}
},
watch: {
message: function(val) {
this.message1 = val
}
}
})

依賴收集流程圖:

通過圖帶你深入了解vue的響應式原理

如何看懂這個依賴收集流程?關鍵在watcher代碼中:

get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
// 省略
} finally {
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}

調用的這個this.getter有兩種,一種是key值的getter方法,還有一種是expOrFn,比如mounted中傳入的updateComponent。

如何防止重復收集

我們不妨想想什么才算是重復收集了?

筆者想到一種情況:就是dep數組中,出現了多個一樣的watcher。

比如renderWatch就容易被重復收集,因為我們在html模版中,會重復使用data中的某個變量。那他是如何去重的呢?

1、只有watch在執行get時,觸發的取數操作,才會被收集

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
// ...
}
return value
},
set: function reactiveSetter (newVal) {
// ...
dep.notify()
}
})

當只有Dep.target這個存在的時候才進行依賴收集。Dep.target這個值只有在watcher執行get方法的時候才會存在。

2、在dep.depend的時候會判斷watch的id

depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}

我們會發現,在depend過程中,會有一個newDepIds去記錄已經存入的dep的id,當一個watcher已經被該dep存過時,便不再會進行依賴收集操作。

派發更新過程

收集流程講完了,不妨在聽聽更新流程。

訂閱更新的流程

老例子

<div id="app">
{{ message }}
{{ message1 }}
<input type="text" v-model="message">
<div @click="changeMessage">改變message</div> 
</div>
var app = new Vue({
el: '#app',
data: {
message: '1',
message1: '2',
},
methods: {
changeMessage() {
this.message = '3'
}
},
watch: {
message: function(val) {
this.message1 = val
}
}
})

依賴收集的最終結果:

通過圖帶你深入了解vue的響應式原理

當觸發click事件的時候,便會觸發訂閱更新流程。

訂閱更新流程圖:

通過圖帶你深入了解vue的響應式原理

當renderWatch執行更新的時候,回去調用beforeUpdate生命鉤子,然后執行patch方法,進行視圖的變更。

如何防止重復更新

如何去防止重復更新呢?renderWatch會被很多dep進行收集,如果視圖多次渲染,會造成性能問題。

其實問題的關在在于——queueWatcher

在queueWatcher中有兩個操作:去重和異步更新。

function queueWatcher (watcher) {
const id = watcher.id
if (has[id] == null) {
has[id] = true
queue.push(watcher)
// ...
if (!waiting) {
waiting = true
// ...
nextTick(flushSchedulerQueue)
}
}
}

其實queueWatcher很簡單,將所有watch收集到一個數組當中,然后去重。

這樣至少可以避免renderWatch頻繁更新。

比如上述例子中的,message和message1都有一個renderWatch,但是只會執行一次。

異步更新也可以保證當一個事件結束之后,才會觸發視圖層的更新,也能防止renderWatch重復更新

結尾

文章講述了響應式流程的原因,代碼細節并未深入,

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

英德市| 武清区| 兰溪市| 沈丘县| 且末县| 江源县| 杭锦旗| 廊坊市| 五家渠市| 景泰县| 新营市| 金沙县| 连城县| 伽师县| 汉寿县| 荃湾区| 武宁县| 淳化县| 宁德市| 林州市| 共和县| 淮南市| 双桥区| 衡阳市| 湖北省| 恩平市| 鱼台县| 浏阳市| 庆云县| 龙南县| 武川县| 兰坪| 中江县| 象州县| 东阳市| 阿巴嘎旗| 常熟市| 离岛区| 苍梧县| 东乡县| 灵石县|