摘要:接下來,我們就一起深入了解的數(shù)據(jù)響應(yīng)式原理,搞清楚響應(yīng)式的實(shí)現(xiàn)機(jī)制?;卣{(diào)函數(shù)只是打印出新的得到的新的值,由執(zhí)行后生成。及異步更新相信讀過前文,你應(yīng)該對響應(yīng)式原理有基本的認(rèn)識。
前言
Vue.js 的核心包括一套“響應(yīng)式系統(tǒng)”。
“響應(yīng)式”,是指當(dāng)數(shù)據(jù)改變后,Vue 會(huì)通知到使用該數(shù)據(jù)的代碼。例如,視圖渲染中使用了數(shù)據(jù),數(shù)據(jù)改變后,視圖也會(huì)自動(dòng)更新。
舉個(gè)簡單的例子,對于模板:
{{ name }}
創(chuàng)建一個(gè) Vue 組件:
var vm = new Vue({ el: "#root", data: { name: "luobo" } })
代碼執(zhí)行后,頁面上對應(yīng)位置會(huì)顯示:luobo。
如果想改變顯示的名字,只需要執(zhí)行:
vm.name = "tang"
這樣頁面上就會(huì)顯示修改后的名字了,并不需要去手動(dòng)修改 DOM 更新數(shù)據(jù)。
接下來,我們就一起深入了解 Vue 的數(shù)據(jù)響應(yīng)式原理,搞清楚響應(yīng)式的實(shí)現(xiàn)機(jī)制。
基本概念Vue 的響應(yīng)式,核心機(jī)制是 觀察者模式。
數(shù)據(jù)是被觀察的一方,發(fā)生改變時(shí),通知所有的觀察者,這樣觀察者可以做出響應(yīng),比如,重新渲染然后更新視圖。
我們把依賴數(shù)據(jù)的觀察者稱為 watcher,那么這種關(guān)系可以表示為:
data -> watcher
數(shù)據(jù)可以有多個(gè)觀察者,怎么記錄這種依賴關(guān)系呢?
Vue 通過在 data 和 watcher 間創(chuàng)建一個(gè) dep 對象,來記錄這種依賴關(guān)系:
data - dep -> watcher
dep 的結(jié)構(gòu)很簡單,除了唯一標(biāo)識屬性 id,另一個(gè)屬性就是用于記錄所有觀察者的 subs:
id - number
subs - [Watcher]
再來看 watcher。
Vue 中 watcher 的觀察對象,確切來說是一個(gè)求值表達(dá)式,或者函數(shù)。這個(gè)表達(dá)式或者函數(shù),在一個(gè) Vue 實(shí)例的上下文中求值或執(zhí)行。這個(gè)過程中,使用到數(shù)據(jù),也就是 watcher 所依賴的數(shù)據(jù)。用于記錄依賴關(guān)系的屬性是 deps,對應(yīng)的是由 dep 對象組成的數(shù)組,對應(yīng)所有依賴的數(shù)據(jù)。而表達(dá)式或函數(shù),最終會(huì)作為求值函數(shù)記錄到 getter 屬性,每次求值得到的結(jié)果記錄在 value 屬性:
vm - VueComponent
deps - [Dep]
getter - function
value - *
另外,還有一個(gè)重要的屬性 cb,記錄回調(diào)函數(shù),當(dāng) getter 返回的值與當(dāng)前 value 不同時(shí)被調(diào)用:
cb - function
我們通過示例來整理下 data、dep、watcher 的關(guān)系:
var vm = new Vue({ data: { name: "luobo", age: 18 } }) var userInfo = function () { return this.name + " - " + this.age } var onUserInfoChange = function (userInfo) { console.log(userInfo) } vm.$watch(userInfo, onUserInfoChange)
上面代碼首先創(chuàng)建了一個(gè)新的 Vue 實(shí)例對象 vm,包含兩個(gè)數(shù)據(jù)字段:name、age。對于這兩個(gè)字段,Vue 會(huì)分別創(chuàng)建對應(yīng)的 dep 對象,用于記錄依賴該數(shù)據(jù)的 watcher。
然后定義了一個(gè)求值函數(shù) userInfo,注意,這個(gè)函數(shù)會(huì)在對應(yīng)的 Vue 示例上下文中執(zhí)行,也就是說,執(zhí)行時(shí)的 this 對應(yīng)的就是 vm。
回調(diào)函數(shù) onUserInfoChange 只是打印出新的 watcher 得到的新的值,由 userInfo 執(zhí)行后生成。
通過 vm.$watch(userInfo, onUserInfoChange),將 vm、getter、cb 集成在一起創(chuàng)建了新的 watcher。創(chuàng)建成功后,watcher 在內(nèi)部已經(jīng)記錄了依賴關(guān)系,watcher.deps 中記錄了 vm 的 name、age 對應(yīng)的 dep 對象(因?yàn)? userInfo 中使用了這兩個(gè)數(shù)據(jù))。
接下來,我們修改數(shù)據(jù):
vm.name = "tang"
執(zhí)行后,控制臺會(huì)輸出:
tang - 18
同樣,如果修改 age 的值,也會(huì)最終觸發(fā) onUserInfoChange 打印出新的結(jié)果。
用個(gè)簡單的圖來整理下上面的關(guān)系:
vm.name -- dep1 vm.age -- dep2 watcher.deps --> [dep1, dep2]
修改 vm.name 后,dep1 通知相關(guān)的 watcher,然后 watcher 執(zhí)行 getter,得到新的 value,再將新的 value 傳給 cb:
vm.name -> dep1 -> watcher -> getter -> value -> cb
可能你也注意到了,上面例子中的 userInfo,貌似就是計(jì)算屬性的作用嘛:
var vm = new Vue({ data: { name: "luobo", age: 18 }, computed: { userInfo() { return this.name + " - " + this.age } } })
其實(shí),計(jì)算屬性在內(nèi)部也是基于 watcher 實(shí)現(xiàn)的,每個(gè)計(jì)算屬性對應(yīng)一個(gè) watcher,其 getter 也就是計(jì)算屬性的聲明函數(shù)。
不過,計(jì)算屬性對應(yīng)的 watcher 與直接通過 vm.$watch() 創(chuàng)建的 watcher 略有不同,畢竟如果沒有地方使用到這個(gè)計(jì)算屬性,數(shù)據(jù)改變時(shí)都重新進(jìn)行計(jì)算會(huì)有點(diǎn)浪費(fèi),這個(gè)在本文后面會(huì)講到。
上面描述了 data、dep、watcher 的關(guān)系,但是問題來了,這種依賴關(guān)系是如何建立的呢?數(shù)據(jù)改變后,又是如何通知 watcher 的呢?
接下來我們深入 Vue 源碼,搞清楚這兩個(gè)問題。
建立依賴關(guān)系Vue 源碼版本 v2.5.13,文中摘錄的部分代碼為便于分析進(jìn)行了簡化或改寫。
響應(yīng)式的核心邏輯,都在 Vue 項(xiàng)目的 “vue/src/core/observer” 目錄下面。
我們還是先順著前面示例代碼來捋一遍,首先是 Vue 實(shí)例化過程:
var vm = new Vue(/* ... */)
跟將傳入的 data 進(jìn)行響應(yīng)式初始化相關(guān)的代碼,在 “vue/src/core/instance/state.js” 文件中:
observer/state.js#L149
// new Vue() -> ... -> initState() -> initData() observe(data)
函數(shù) observe() 的目的是讓傳入的整個(gè)對象成為響應(yīng)式的,它會(huì)遍歷對象的所有屬性,然后執(zhí)行:
observer/index.js#L64
// observe() -> new Observer() -> observer.walk() defineReactive(obj, key, value)
defineReactive() 就是用于定義響應(yīng)式數(shù)據(jù)的核心函數(shù)。它主要做的事情包括:
新建一個(gè) dep 對象,與當(dāng)前數(shù)據(jù)對應(yīng)
通過 Object.defineProperty() 重新定義對象屬性,配置屬性的 set、get,從而數(shù)據(jù)被獲取、設(shè)置時(shí)可以執(zhí)行 Vue 的代碼
OK,先到這里,關(guān)于 Vue 實(shí)例化告一段落。
需要要注意的是,傳入 Vue 的 data 的所有屬性,會(huì)被代理到新創(chuàng)建的 Vue 實(shí)例對象上,這樣通過 vm.name 進(jìn)行操作的其實(shí)就是 data.name,這也是借助 Object.defineProperty() 實(shí)現(xiàn)的。
再來看 watcher 的創(chuàng)建過程:
vm.$watch(userInfo, onUserInfoChange)
上述代碼執(zhí)行后,會(huì)調(diào)用:
instance/state.js#L346
// Vue.prototype.$watch() new Watcher(vm, expOrFn, cb, options)
也就是:
new Watcher(vm, userInfo, onUserInfoChange, {/* 略 */})
在 watcher 對象創(chuàng)建過程中,除了記錄 vm、getter、cb 以及初始化各種屬性外,最重要的就是調(diào)用了傳入的 getter 函數(shù):
observer/watcher.js#L103
// new Watcher() -> watcher.get() value = this.getter.call(vm, vm)
在 getter 函數(shù)的執(zhí)行過程中,獲取讀取需要的數(shù)據(jù),于是觸發(fā)了前面通過 defineReactive() 配置的 get 方法:
if (Dep.target) { dep.depend() }
這是做什么呢?
回到 watcher.get() 方法,在執(zhí)行 getter 函數(shù)的前后,分別有如下代碼:
pushTarget(this) // ... value = this.getter.call(vm, vm) // ... popTarget()
pushTarget() 將當(dāng)前 watcher 設(shè)置為 Dep.target,這樣在執(zhí)行到 vm.name 進(jìn)一步執(zhí)行對應(yīng)的 get 方法時(shí),Dep.target 的值就是這里的 watcher,然后通過 dep.depend() 就建立了依賴關(guān)系。
dep.depend() 執(zhí)行的邏輯就比較好推測了,將 watcher(通過 Dep.target 引用到)記錄到 dep.subs 中,將 dep 記錄到 watcher.deps 中 —— 依賴關(guān)系建立了!
然后來看建立的依賴關(guān)系是如何使用的。
數(shù)據(jù)變更同步繼續(xù)前面的例子,執(zhí)行如下代碼時(shí):
vm.name = "tang"
會(huì)觸發(fā)通過 defineReactive() 配置的 set 方法,如果數(shù)據(jù)改變,那么:
// defineReactive() -> set() dep.notify()
通過 dep 對象來通知所有的依賴方法,于是 dep 遍歷內(nèi)部的 subs 執(zhí)行:
// dep.notify() watcher.update()
這樣 watcher 就被通知到了,知道了數(shù)據(jù)改變,從而繼續(xù)后續(xù)的處理。這里先不展開。
到這里,基本就搞清楚響應(yīng)式的基本機(jī)制了,整理一下:
通過 Object.defineProperty() 替換配置對象屬性的 set、get 方法,實(shí)現(xiàn)“攔截”
watcher 在執(zhí)行 getter 函數(shù)時(shí)觸發(fā)數(shù)據(jù)的 get 方法,從而建立依賴關(guān)系
寫入數(shù)據(jù)時(shí)觸發(fā) set 方法,從而借助 dep 發(fā)布通知,進(jìn)而 watcher 進(jìn)行更新
這樣再看 Vue 官方的圖就比較好理解了:
圖片來源:https://vuejs.org/v2/guide/re...計(jì)算屬性
上圖中左側(cè)是以組件渲染(render)作為 getter 函數(shù)來演示響應(yīng)式過程的,這其實(shí)就是 RenderWatcher 這種特殊類型 watcher 的作用機(jī)制,后面還會(huì)再講。
本文前面提到過計(jì)算屬性,在 Vue 中也是作為 watcher 進(jìn)行處理的。計(jì)算屬性(ComputedWatcher)特殊的地方在于,它其實(shí)沒有 cb(空函數(shù)),只有 getter,并且它的值只在被使用時(shí)才計(jì)算并緩存。
什么意思呢?
首先,ComputedWatcher 在創(chuàng)建時(shí),不會(huì)立即執(zhí)行 getter(lazy 選項(xiàng)值為 false),這樣一開始 ComputedWatcher 并沒有和使用到的數(shù)據(jù)建立依賴關(guān)系。
計(jì)算屬性在被“get”時(shí),首先執(zhí)行預(yù)先定義的 ComputedGetter 函數(shù),這里有一段特殊邏輯:
instance/state.js#L238
function computedGetter () { if (watcher.dirty) { watcher.evaluate() } if (Dep.target) { watcher.depend() } return watcher.value }
首先判斷 watcher 是不是 dirty 狀態(tài),什么意思呢?
計(jì)算屬性對應(yīng)的 watcher 初始創(chuàng)建的時(shí)候,并沒有執(zhí)行 getter,這個(gè)時(shí)候就會(huì)設(shè)置 dirty 為 true,這樣當(dāng)前獲取計(jì)算屬性的值的時(shí)候,會(huì)執(zhí)行 getter 得到 value,然后標(biāo)記 dirty 為 false。這樣后續(xù)再獲取計(jì)算屬性的值,不需要再計(jì)算(執(zhí)行 getter),直接就能返回緩存的 value。
另外,計(jì)算屬性的 watcher 在執(zhí)行 watcher.evaluate() 是,進(jìn)一步調(diào)用 watcher.get(),從而進(jìn)行依賴收集。而依賴的數(shù)據(jù)在改變后,會(huì)通知計(jì)算屬性的 watcher,但是 watcher 只是標(biāo)記自身為 dirty,而不計(jì)算。這樣的好處是可以減小開銷,只在有地方需要計(jì)算屬性的值時(shí)才執(zhí)行計(jì)算。
如果依賴的數(shù)據(jù)發(fā)生變更,計(jì)算屬性只是標(biāo)記 dirty 為 true,會(huì)不會(huì)有問題呢?
解決這個(gè)問題的是上面代碼的這一部分:
if (Dep.target) { watcher.depend() }
也就是說,如果當(dāng)前有在收集依賴的 watcher,那么當(dāng)前計(jì)算屬性的 watcher 會(huì)間接地通過 watcher.depend() 將依賴關(guān)系“繼承”給這個(gè) watcher(watcher.depend() 內(nèi)部是對每個(gè) watcher.deps 記錄的 dep 執(zhí)行 dep.depend() 從而讓依賴數(shù)據(jù)與當(dāng)前的 watcher 建立依賴關(guān)系)。
所以,依賴數(shù)據(jù)改變,依賴計(jì)算屬性的 watcher 會(huì)直接得到通知,再來獲取計(jì)算屬性的值的時(shí)候,計(jì)算屬性才進(jìn)行計(jì)算求值。
所以,依賴計(jì)算屬性的 watcher 可以視為依賴 watcher 的 watcher。這樣的 watcher 在 Vue 中最常見不過,那就是 RenderWatcher。
RenderWatcher 及異步更新相信讀過前文,你應(yīng)該對 Vue 響應(yīng)式原理有基本的認(rèn)識。那么 Vue 是如何將其運(yùn)用到視圖更新中的呢?答案就是這里要講的 RenderWatcher。
RenderWatcher 首先是 watcher,只不過和計(jì)算屬性對應(yīng)的 ComputedWatcher 類似,它也有些特殊的行為。
RenderWatcher 的創(chuàng)建,在函數(shù) mountComponent 中:
// Vue.prototype.$mount() -> mountComponent() let updateComponent = () => { vm._update(vm._render(), hydrating) } new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
核心代碼就在這里了。這個(gè) watcher 就是 Vue 實(shí)例對象唯一的 RenderWatcher,在 watcher 構(gòu)造函數(shù)中,會(huì)記錄到 vm._watcher 上(普通 watcher 只會(huì)記錄到 vm._watchers 數(shù)組中)。
這個(gè) watcher 也會(huì)在創(chuàng)建的最后執(zhí)行 watcher.get(),也就是執(zhí)行 getter 收集依賴的過程。而在這里,getter 就是 updateComponent,也就是說,執(zhí)行了渲染+更新 DOM!并且,這個(gè)過程中使用到的數(shù)據(jù)也被收集了依賴關(guān)系。
那么,理所當(dāng)然地,在 render() 中使用到數(shù)據(jù),發(fā)生改變,自然會(huì)通知到 RenderWatcher,從而最終更新視圖!
不過,這里會(huì)有個(gè)疑問:如果進(jìn)行多次數(shù)據(jù)修改,那么豈不是要頻繁執(zhí)行 DOM 更新?
這里就涉及到 RenderWatcher 的特殊功能了:異步更新。
結(jié)合前面內(nèi)容,我們知道數(shù)據(jù)更新后,依賴該數(shù)據(jù)的 watcher 會(huì)執(zhí)行 watcher.update(),這個(gè)在前文中沒有展開,現(xiàn)在我們來看下這個(gè)方法:
observer/watcher.js#L161
if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) }
第一種情況,lazy 為 true,也就是計(jì)算屬性,上一節(jié)已經(jīng)提到過,只是標(biāo)記 dirty 為 true,并不立即計(jì)算,不再贅述。sync 為 true 的情況,這里也不管,不過看起來也很簡單,就是立即執(zhí)行計(jì)算嘛。
最后的情況,就是這里 RenderWatcher 的場景,并不立即執(zhí)行,也不是像計(jì)算屬性那樣標(biāo)記為 dirty 就完了,而是放到了一個(gè)隊(duì)列中。
這個(gè)隊(duì)列是干什么的呢?
相關(guān)代碼在 observer/scheduler.js 中,簡單來說,就是實(shí)現(xiàn)了異步更新。
理解其實(shí)現(xiàn),首先要對瀏覽器的事件循環(huán)(Event Loop)機(jī)制有一定了解。如果你對事件循環(huán)機(jī)制不是很了解,可以看下面這篇文章:
JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop - 阮一峰
事件循環(huán)機(jī)制其實(shí)有點(diǎn)復(fù)雜,但只有理解事件循環(huán),才能對這里 Vue 異步更新的方案有深入的認(rèn)識。
基于事件循環(huán)機(jī)制,RenderWatcher 將其 getter,也就是 updateComponent 函數(shù)異步執(zhí)行,并且,多次觸發(fā)
RenderWatcher 的 update(),最終也只會(huì)執(zhí)行一次 updateComponent,這樣也就解決了性能問題。
不過,隨之而來的新問題是,修改完數(shù)據(jù),不能直接反應(yīng)到 DOM 上,而是要等異步更新執(zhí)行過后才可以,這也是為什么 Vue 提供了 nextTick() 接口,并且要求開發(fā)者將對 DOM 的操作放到 nextTick() 回調(diào)中執(zhí)行的原因。
Vuex、Vue-Router再來看 Vue 套裝中的 Vuex、Vue-Router,它們也是基于 Vue 的響應(yīng)式機(jī)制實(shí)現(xiàn)功能。
先來看 Vuex,代碼版本 v3.0.1。
Vuex在應(yīng)用了 Vuex 的應(yīng)用中,所有組件都可以通過 this.$store 來引用到全局的 store,并且在使用了 store 的數(shù)據(jù)后,還能在數(shù)據(jù)改變后得到同步,這其實(shí)就是響應(yīng)式的應(yīng)用了。
首先看 this.$store 的實(shí)現(xiàn),這個(gè)其實(shí)是通過全局 mixin 實(shí)現(xiàn),代碼在:
src/mixin.js#L26
this.$store = options.store || options.parent.$store
這樣在每個(gè)組件的 beforeCreate 時(shí),會(huì)執(zhí)行 $store 屬性的初始化。
而 store 數(shù)據(jù)的響應(yīng)式處理,則是通過實(shí)例化一個(gè) Vue 對象實(shí)現(xiàn):
src/store.js#L251
// new Store() -> resetStoreVM() store._vm = new Vue({ data: { $$state: state }, computed // 對應(yīng) store.getters })
結(jié)合前文的介紹,這里就很好理解了。因?yàn)?state 以及處理為響應(yīng)式數(shù)據(jù),而 getters 也創(chuàng)建為計(jì)算屬性,所以對這些數(shù)據(jù)的使用,就建立依賴關(guān)系,從而可以響應(yīng)數(shù)據(jù)改變了。
Vue-RouterVue-Router 中,比較重要的數(shù)據(jù)是 $route,即當(dāng)前的頁面路由數(shù)據(jù),在路由改變的時(shí)候,需要替換展示不同組件(router-view 組件實(shí)現(xiàn))。
vm.$route 實(shí)踐上是來自 Vue.prototype,但其對應(yīng)的值,最終對應(yīng)到的是 router.history.current。
結(jié)合前面的分析,這里的 history.current 肯定得是響應(yīng)式數(shù)據(jù),所以,來找下對其進(jìn)行初始化的地方,其實(shí)是在全局 mixin 的 beforeCreate 這里:
v2.8.1/src/install.js#L27
// beforeCreate Vue.util.defineReactive(this, "_route", this._router.history.current)
這樣 this._route 就是響應(yīng)式的了,那么如果頁面路由改變,又是如何修改這里的 _route 的呢?
答案在 VueRouter 的 init() 這里:
history.listen(route => { this.apps.forEach((app) => { app._route = route }) })
一個(gè) router 對象可能和多個(gè) vue 實(shí)例對象(這里叫作 app)關(guān)聯(lián),每次路由改變會(huì)通知所有的實(shí)例對象。
再來看使用 vm.$route 的地方,也就是 VueRouter 的兩個(gè)組件:
兩個(gè)組件都是在 render() 中,與 $route 建立了依賴關(guān)系,根據(jù) route 的值進(jìn)行渲染。這里具體過程就不展開了,感興趣可以看下相關(guān)源碼(v2.8.1/src/components),原理方面在 RenderWatcher 一節(jié)已經(jīng)介紹過。
實(shí)踐:watch-it了解了以上這么多,也想自己試試,把 Vue 響應(yīng)式相關(guān)的核心邏輯剝離出來,做一個(gè)單純的數(shù)據(jù)響應(yīng)式的庫。由于只關(guān)注數(shù)據(jù),所以在剝離過程中,將與 Vue 組件/實(shí)例對象相關(guān)的部分都移除了,包括 watcher.vm 也不再需要,這樣 watcher.getter 計(jì)算時(shí)不再指定上下文對象。
感興趣,想直接看代碼的,可以前往 luobotang/watch-it。
watch-it 只包括數(shù)據(jù)響應(yīng)式相關(guān)的功能,暴露了4個(gè)接口:
defineReactive(obj, key, val):為對象配置一個(gè)響應(yīng)式數(shù)據(jù)屬性
observe(obj):將一個(gè)數(shù)據(jù)對象配置為響應(yīng)式,內(nèi)部對所有的屬性執(zhí)行 defineReactive
defineComputed(target, key, userDef):為對象配置一個(gè)計(jì)算屬性,內(nèi)部創(chuàng)建了 watcher
watch(fn, cb, options):監(jiān)聽求值函數(shù)中數(shù)據(jù)改變,變化時(shí)調(diào)用 cb,內(nèi)部創(chuàng)建了 watcher
來看一個(gè)使用示例:
const { observe, watch } = require("@luobotang/watch-it") const data = { name: "luobo", age: 18 } observe(data) const userInfo = function() { return data.name + " - " + data.age } watch(userInfo, (value) => console.log(value))
這樣,當(dāng)數(shù)據(jù)修改時(shí),通過會(huì)打印出新的 userInfo 的值。
去除虛擬 DOM,只通過響應(yīng)式機(jī)制,我還構(gòu)建了一個(gè)簡單的 Vue,并實(shí)現(xiàn)了一個(gè) DEMO:
watch-it/example/
源碼在這里:
luobotang/watch-it/example/vue.js
總結(jié)OK,以上就是有關(guān) Vue 響應(yīng)式原理的全部了,當(dāng)然,只是我的理解和實(shí)踐。
在梳理和寫下這些內(nèi)容的過程中,我收獲很多,也希望內(nèi)容能夠?qū)δ阌兴鶐椭?/p>
水平有限,錯(cuò)漏難免,歡迎指出。
最后,感謝閱讀!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97431.html
摘要:在讀取訪問器屬性時(shí),就會(huì)調(diào)用函數(shù),該函數(shù)負(fù)責(zé)返回有效的值在寫入訪問器屬性時(shí),會(huì)調(diào)用函數(shù)并傳入新值,該函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù),但是這兩個(gè)函數(shù)不一定非要同時(shí)存在。 前言 Vue最明顯的特性之一便是它的響應(yīng)式系統(tǒng),其數(shù)據(jù)模型即是普通的 JavaScript 對象。而當(dāng)你讀取或?qū)懭胨鼈儠r(shí),視圖便會(huì)進(jìn)行響應(yīng)操作。文章簡要闡述下其實(shí)現(xiàn)原理,如有錯(cuò)誤,還請不吝指正。個(gè)人博客鏈接:hiybm.cn ...
摘要:所以我今后打算把每一個(gè)內(nèi)容分成白話版和源碼版。有什么錯(cuò)誤的地方,感謝大家能夠指出響應(yīng)式系統(tǒng)我們都知道,只要在實(shí)例中聲明過的數(shù)據(jù),那么這個(gè)數(shù)據(jù)就是響應(yīng)式的。什么是響應(yīng)式,也即是說,數(shù)據(jù)發(fā)生改變的時(shí)候,視圖會(huì)重新渲染,匹配更新為最新的值。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 V...
寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】Props - 源碼版 今天記錄 Props 源碼流程,哎,這東西,就算是研究過了,也真是會(huì)隨著時(shí)間慢慢忘記的。 幸好我做...
摘要:問題為什么修改即可觸發(fā)更新和的關(guān)聯(lián)關(guān)系官方介紹的官網(wǎng)文檔,對響應(yīng)式屬性的原理有一個(gè)介紹。因此本文在源碼層面,對響應(yīng)式原理進(jìn)行梳理,對關(guān)鍵步驟進(jìn)行解析。 描述 ?我們通過一個(gè)簡單的 Vue應(yīng)用 來演示 Vue的響應(yīng)式屬性: html: {{message}} js: let vm = new Vue({ el: #ap...
摘要:響應(yīng)式原理之之前簡單介紹了和類的代碼和作用,現(xiàn)在來介紹一下類和。對于數(shù)組,響應(yīng)式的實(shí)現(xiàn)稍有不同。不存在時(shí),說明不是響應(yīng)式數(shù)據(jù),直接更新。如果對象是響應(yīng)式的,確保刪除能觸發(fā)更新視圖。 Vue響應(yīng)式原理之Observer 之前簡單介紹了Dep和Watcher類的代碼和作用,現(xiàn)在來介紹一下Observer類和set/get。在Vue實(shí)例后再添加響應(yīng)式數(shù)據(jù)時(shí)需要借助Vue.set/vm.$se...
閱讀 2481·2021-11-19 09:59
閱讀 2005·2019-08-30 15:55
閱讀 938·2019-08-29 13:30
閱讀 1342·2019-08-26 10:18
閱讀 3091·2019-08-23 18:36
閱讀 2394·2019-08-23 18:25
閱讀 1168·2019-08-23 18:07
閱讀 441·2019-08-23 17:15