update关联一个视图的时候特别慢_实现一个简单的Vue.js
原文轉(zhuǎn)自 https://const_white.gitee.io/gitee-blog/blog/vue/mini-vue/
Vue響應(yīng)式原理
圖片引自 孟思行 - 圖解 Vue 響應(yīng)式原理
乞丐版 mini-vue
實(shí)現(xiàn)mini-vue之前,先看看官網(wǎng)的描述。在Vue官網(wǎng),深入響應(yīng)式原理中,是這樣說明的:
每個組件實(shí)例都對應(yīng)一個?watcher?實(shí)例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù) property 記錄為依賴。之后當(dāng)依賴項(xiàng)的 setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。
起步
技術(shù)原因,這里不做Virtual DOM、render部分,而選擇直接操作DOM
簡單來說,mini vue在創(chuàng)建Vue實(shí)例時
實(shí)現(xiàn)代碼都添加了詳細(xì)的注釋,無毒無害,可放心查看
Vue類
class?Vue?{??constructor(options)?{
????//?1.?保存?options的數(shù)據(jù)
????this.$options?=?options?||?{}
????this.$data?=?options.data?||?{}
????this.$el?=?typeof?options.el?===?'string'???document.querySelector(options.el)?:?options.el
????//?2.?為方便調(diào)用(vm.msg),把?data中的成員轉(zhuǎn)換成?getter和?setter,并注入到?Vue實(shí)例中
????this._proxyData(this.$data)
????//?3.?調(diào)用?Observer類,監(jiān)聽數(shù)據(jù)的變化
????new?Observer(this.$data)
????//?4.?調(diào)用?compiler類,解析指令和插值表達(dá)式
????new?Compiler(this)
??}
??_proxyData(data)?{
????Object.keys(data).forEach(key?=>?{
??????Object.defineProperty(this,?key,?{
????????enumerable:?true,
????????configurable:?true,
????????get()?{
??????????return?data[key]
????????},
????????set(newValue)?{
??????????if?(newValue?===?data[key])?{
????????????return
??????????}
??????????data[key]?=?newValue
????????}
??????})
????})
??}
}
Observer類
class?Observer?{??constructor(data)?{
????this.walk(data)
??}
??//?遍歷?data($data)中的屬性,把屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
??walk(data)?{
????if?(!data?||?typeof?data?!==?'object')?{
??????return
????}
????Object.keys(data).forEach((key)?=>?{
??????this.defineReactive(data,?key,?data[key])
????})
??}
??//?定義響應(yīng)式數(shù)據(jù)
??defineReactive(obj,?key,?value)?{
????const?that?=?this
????//?負(fù)責(zé)收集依賴并發(fā)送通知
????let?dep?=?new?Dep()
????//?利用遞歸使深層(內(nèi)部)屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
????this.walk(value)
????Object.defineProperty(obj,?key,?{
??????enumerable:?true,
??????configurable:?true,
??????get()?{
????????//?收集依賴
????????Dep.target?&&?dep.addSub(Dep.target)
????????return?value
??????},
??????set(newValue)?{
????????if?(value?===?newValue)?{
??????????return
????????}
????????value?=?newValue
????????//?如果新設(shè)置的值為對象,也轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
????????that.walk(newValue)
????????//?發(fā)送通知
????????dep.notify()
??????}
????})
??}
}
Compiler類
class?Compiler?{??constructor(vm)?{
????this.vm?=?vm
????this.el?=?vm.$el
????this.compiler(this.el)
??}
??//?編譯模板,處理文本節(jié)點(diǎn)和元素節(jié)點(diǎn)
??compiler(el)?{
????const?childNodes?=?el.childNodes
????Array.from(childNodes).forEach(node?=>?{
??????//?處理文本節(jié)點(diǎn)
??????if?(this.isTextNode(node))?{
????????this.compilerText(node)
??????}?else?if?(this.isElementNode(node))?{
????????//?處理元素節(jié)點(diǎn)
????????this.compilerElement(node)
??????}
??????//?判斷 node節(jié)點(diǎn)是否有子節(jié)點(diǎn)。如果有,遞歸調(diào)用 compile
??????if?(node.childNodes.length)?{
????????this.compiler(node)
??????}
????})
??}
??//?編譯元素節(jié)點(diǎn),處理指令
??compilerElement(node)?{
????//?遍歷所有屬性節(jié)點(diǎn)
????Array.from(node.attributes).forEach(attr?=>?{
??????//?判斷是否?v-開頭指令
??????let?attrName?=?attr.name
??????if?(this.isDirective(attrName))?{
????????//?為了更優(yōu)雅的處理不同方法,減去指令中的?v-
????????attrName?=?attrName.substr(2)
????????const?key?=?attr.value
????????this.update(node,?key,?attrName)
??????}
????})
??}
??//?執(zhí)行對應(yīng)指令的方法
??update(node,?key,?attrName)?{
????let?updateFn?=?this[attrName?+?'Updater']
????//?存在指令才執(zhí)行對應(yīng)方法
????updateFn?&&?updateFn.call(this,?node,?this.vm[key],?key)
??}
??//?處理?v-text指令
??textUpdater(node,?value,?key)?{
????node.textContent?=?value
????//?創(chuàng)建?Watcher對象,當(dāng)數(shù)據(jù)改變時更新視圖
????new?Watcher(this.vm,?key,?(newValue)?=>?{
??????node.textContent?=?newValue
????})
??}
??//?處理?v-model指令
??modelUpdater(node,?value,?key)?{
????node.value?=?value
????//?創(chuàng)建?Watcher對象,當(dāng)數(shù)據(jù)改變時更新視圖
????new?Watcher(this.vm,?key,?(newValue)?=>?{
??????node.value?=?newValue
????})
????//?雙向綁定
????node.addEventListener('input',?()?=>?{
??????this.vm[key]?=?node.value
????})
??}
??//?編譯文本節(jié)點(diǎn),處理插值表達(dá)式
??compilerText(node)?{
????const?reg?=?/\{\{(.+?)\}\}/
????let?value?=?node.textContent
????if?(reg.test(value))?{
??????//?只考慮一層的對象,如 data.msg =?'hello world',不考慮嵌套的對象。且假設(shè)只有一個插值表達(dá)式。
??????const?key?=?RegExp.$1.trim()
??????node.textContent?=?value.replace(reg,?this.vm[key])
??????//?創(chuàng)建?Watcher對象,當(dāng)數(shù)據(jù)改變時更新視圖
??????new?Watcher(this.vm,?key,?(newValue)?=>?{
????????node.textContent?=?newValue
??????})
????}
??}
??//?判斷元素屬性是否屬于指令
??isDirective(attrName)?{
????return?attrName.startsWith('v-')
??}
??//?判斷節(jié)點(diǎn)是否屬于文本節(jié)點(diǎn)
??isTextNode(node)?{
????return?node.nodeType?===?3
??}
??//?判斷節(jié)點(diǎn)書否屬于元素節(jié)點(diǎn)
??isElementNode(node)?{
????return?node.nodeType?===?1
??}
}
Dep類
class?Dep?{??constructor()?{
????this.subs?=?[]
??}
??//?添加觀察者
??addSub(sub)?{
????if?(sub?&&?sub.update)?{
??????this.subs.push(sub)
????}
??}
??//?發(fā)送通知
??notify()?{
????this.subs.forEach(sub?=>?{
??????sub.update()
????})
??}
}
Watcher類
class?Watcher?{??constructor(vm,?key,?cb)?{
????this.vm?=?vm
????//?data中的屬性名
????this.key?=?key
????//?回調(diào)函數(shù)負(fù)責(zé)更新視圖
????this.cb?=?cb
????//?把?watcher對象記錄到?Dep類的靜態(tài)屬性?target中
????Dep.target?=?this
????//?觸發(fā)?get方法,在?get方法中會調(diào)用?addSub
????this.oldValue?=?vm[key]
????Dep.target?=?null
??}
??//?當(dāng)數(shù)據(jù)發(fā)生變化的時候更新視圖
??update()?{
????const?newValue?=?this.vm[this.key]
????//?數(shù)據(jù)沒有發(fā)生變化直接返回
????if?(this.oldValue?===?newValue)?{
??????return
????}
????//?更新視圖
????this.cb(newValue)
??}
}
完整版思維導(dǎo)圖
對于數(shù)組的監(jiān)聽
這里直接把數(shù)組的每一項(xiàng)都添加上了getter和setter,所以vm.items[1] = 'x'也是響應(yīng)式的。
Vue中為什么沒這樣做呢?參考 為什么vue沒有提供對數(shù)組屬性的監(jiān)聽
總結(jié)
以上是生活随笔為你收集整理的update关联一个视图的时候特别慢_实现一个简单的Vue.js的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jieba结巴分词--关键词抽取_结巴中
- 下一篇: vue中的uri_浅谈vue-resou