vue視圖響應(yīng)式更新詳細(xì)介紹
概述
前面兩篇文章已經(jīng)實(shí)現(xiàn)了對(duì)數(shù)據(jù)的變化的監(jiān)聽(tīng)以及模板語(yǔ)法編譯初始化,但是當(dāng)數(shù)據(jù)變化時(shí),視圖還不能夠跟隨數(shù)據(jù)實(shí)時(shí)更新。本文就在之前的基礎(chǔ)上介紹下視圖響應(yīng)式更新部分。
思路
統(tǒng)一封裝更新函數(shù)
待數(shù)據(jù)發(fā)生改變時(shí)調(diào)用對(duì)應(yīng)的更新函數(shù)
這里思考個(gè)問(wèn)題:
在何時(shí)注冊(cè)這個(gè)更新函數(shù)?
如何找到對(duì)應(yīng)的更新函數(shù)?
第一步統(tǒng)一封裝更新函數(shù)
基于上篇文章compile的部分,將數(shù)據(jù)初始化的部分統(tǒng)一封裝起來(lái)。
compileText (n) { // 獲取表達(dá)式 // n.textContent = this.$vm[RegExp.$1] // n.textContent = this.$vm[RegExp.$1.trim()] this.update(n, RegExp.$1.trim(), 'text') } text (node, exp) { this.update(node, exp, 'text') // node.textContent = this.$vm[exp] || exp } html (node, exp) { this.update(node, exp, 'html') // node.innerHTML = this.$vm[exp] }
很容易寫(xiě)出update方法:
每個(gè)指令都有對(duì)應(yīng)的[dir]Updater管理器,用于在公共的update函數(shù)里調(diào)用去在相應(yīng)視圖渲染數(shù)據(jù)。
update (node, exp, dir) { // 第一步: 初始化值 const fn = this[dir + 'Updater'] fn && fn(node, this.$vm[exp]) } textUpdater (node, val) { node.textContent = val } htmlUpdater (node, val) { node.innerHTML = val }
第二步監(jiān)聽(tīng)并觸發(fā)視圖更新
分析可知,每個(gè)模板渲染初始化的過(guò)程都需要對(duì)數(shù)據(jù)進(jìn)行監(jiān)聽(tīng),并注冊(cè)監(jiān)聽(tīng)函數(shù),因此在上述的update函數(shù)中添加更新邏輯。
update (node, exp, dir) { // 第一步: 初始化值 const fn = this[dir + 'Updater'] fn && fn(node, this.$vm[exp]) // 第二步: 更新 new Watcher(this.$vm, exp, val => { fn && fn(node, val) }) }
創(chuàng)建Watcher類(lèi):
// 監(jiān)聽(tīng)器:負(fù)責(zé)依賴(lài)更新 class Watcher { constructor (vm, key, updateFn) { this.vm = vm this.key = key this.updateFn = updateFn } update () { // 綁定作用域?yàn)閠his.vm,并且將this.vm[this.key]作為值傳進(jìn)去 this.updateFn.call(this.vm, this.vm[this.key]) } }
此時(shí)我們已經(jīng)完成了更新函數(shù)的功能,需要做的就是在數(shù)據(jù)發(fā)生改變的時(shí)候,主動(dòng)調(diào)用下對(duì)應(yīng)的update函數(shù)。
簡(jiǎn)單測(cè)試下:聲明一個(gè)全局的watchers數(shù)組。在每次Watcher的構(gòu)造函數(shù)中都往watchers中push一下,那么我們就可以再Object.defineProperty()的set方法中去遍歷所有的watchers,調(diào)用update方法。
淺試一下:
const watchers = [] class Watcher { constructor (vm, key, updateFn) { this.vm = vm this.key = key this.updateFn = updateFn watchers.push(this) } update () { this.updateFn.call(this.vm, this.vm[this.key]) } } function defineReactive (obj, key, val) { // 遞歸 // val如果是個(gè)對(duì)象,就需要遞歸處理 observe(val) const dep = new Dep() Object.defineProperty(obj, key, { get () { Dep.target && dep.addDep(Dep.target) return val }, set (newVal) { if (newVal !== val) { val = newVal // 新值如果是對(duì)象,仍然需要遞歸遍歷處理 observe(newVal) //暴力的寫(xiě)法,讓一個(gè)人干事指揮所有人動(dòng)起來(lái)(不管你需不需要更新,全給我更新一遍) watchers.forEach(watch => { watch.update() }) } } }) }
此時(shí)頁(yè)面視圖已經(jīng)可以根據(jù)數(shù)據(jù)的變而發(fā)生相應(yīng)的更新了。
引入Dep管家
只觸發(fā)需要更新的函數(shù)
上述的寫(xiě)法過(guò)于暴力,數(shù)據(jù)量一旦稍微大點(diǎn)就會(huì)嚴(yán)重影響性能。vue內(nèi)部引入了Dep這個(gè)大管家的概念來(lái)進(jìn)行依賴(lài)收集,統(tǒng)一管理所有的watcher。只讓需要干活的watcher去update。
class Dep { constructor () { this.deps = [] } addDep (dep) { this.deps.push(dep) } notify () { this.deps.forEach(dep => dep.update()) } }
每個(gè)data中的key對(duì)應(yīng)一個(gè)dep就行,所以選擇在Object.defineProperty的getter函數(shù)中進(jìn)行依賴(lài)收集。在watcher中觸發(fā)依賴(lài)收集
class Watcher { constructor (vm, key, updateFn) { this.vm = vm this.key = key this.updateFn = updateFn // 觸發(fā)依賴(lài)收集,使用一個(gè)靜態(tài)變量target去保存對(duì)應(yīng)的Watcher Dep.target = this // 主動(dòng)訪問(wèn)vm[key],觸發(fā)一下getter this.vm[this.key] Dep.target = null } update () { // 綁定作用域?yàn)閠his.vm,并且將this.vm[this.key]作為值傳進(jìn)去 this.updateFn.call(this.vm, this.vm[this.key]) } }
收集依賴(lài),創(chuàng)建Dep實(shí)例
function defineReactive (obj, key, val) { observe(val) const dep = new Dep() Object.defineProperty(obj, key, { get () { Dep.target && dep.addDep(Dep.target) return val }, set (newVal) { if (newVal !== val) { val = newVal observe(newVal) dep.notify() } } }) }
至此,我們一個(gè)簡(jiǎn)版的Vue就實(shí)現(xiàn)了。這里還沒(méi)有涉及到虛擬dom得概念,以后介紹。
實(shí)現(xiàn)下語(yǔ)法糖v-model
v-model雖然很像使用了雙向數(shù)據(jù)綁定的 Angular 的 ng-model,但是 Vue 是單項(xiàng)數(shù)據(jù)流,v-model 只是語(yǔ)法糖而已。
// 最簡(jiǎn)形式,省略了value的顯式綁定,省略了oninput的顯式事件監(jiān)聽(tīng),是第二句代碼的語(yǔ)法糖形式 <input v-model="sth" /> <input v-bind:value="sth" v-on:input="sth = $event.target.value" /> //第二句代碼的簡(jiǎn)寫(xiě)形式 <input :value="sth" @input="sth = $event.target.value" />
分析一下其就是在內(nèi)部實(shí)現(xiàn)了v-bind:value=“” 和@input。
model (node, exp) { node.value = this.$vm[exp] node.addEventListener('input', (e) => { this.$vm[exp] = e.target.value }) }
到此這篇關(guān)于vue視圖響應(yīng)式更新詳細(xì)介紹的文章就介紹到這了,更多相關(guān)vue響應(yīng)式更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue Element前端應(yīng)用開(kāi)發(fā)之前端API接口的封裝
對(duì)整個(gè)系統(tǒng)來(lái)說(shuō),一般會(huì)有很多業(yè)務(wù)對(duì)象,而每個(gè)業(yè)務(wù)對(duì)象的API接口又有很多。我們這個(gè)VUE+Element 前端應(yīng)用就是針對(duì)ABP框架的業(yè)務(wù)對(duì)象,因此前端的業(yè)務(wù)對(duì)象接口也是比較統(tǒng)一的,那么可以考慮在前端中對(duì)后端API接口調(diào)用進(jìn)行封裝,引入ES6的方式進(jìn)行前端API的抽象簡(jiǎn)化。2021-05-05vue實(shí)現(xiàn)Input輸入框模糊查詢(xún)方法
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)Input輸入框模糊查詢(xún)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10Vue3 + Vue-PDF 實(shí)現(xiàn)PDF 文件在線預(yù)覽實(shí)戰(zhàn)
這篇文章主要介紹了Vue3 + Vue-PDF 實(shí)現(xiàn)PDF 文件在線預(yù)覽實(shí)戰(zhàn),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06vue 彈窗時(shí) 監(jiān)聽(tīng)手機(jī)返回鍵關(guān)閉彈窗功能(頁(yè)面不跳轉(zhuǎn))
這篇文章主要介紹了vue 彈窗時(shí) 監(jiān)聽(tīng)手機(jī)返回鍵關(guān)閉彈窗功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值(頁(yè)面不跳轉(zhuǎn)) ,需要的朋友可以參考下2019-05-05