日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

update关联一个视图的时候特别慢_实现一个简单的Vue.js

發(fā)布時間:2024/7/23 vue 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 update关联一个视图的时候特别慢_实现一个简单的Vue.js 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文轉(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í)例時

  • Vue類負(fù)責(zé)把data中的屬性注入到Vue實(shí)例,并調(diào)用Observer類和Compiler類。
  • Observer類負(fù)責(zé)數(shù)據(jù)劫持,把每一個data轉(zhuǎn)換成getter和setter。其核心原理是通過Object.defineProperty實(shí)現(xiàn)。
  • Compiler類負(fù)責(zé)解析指令和插值表達(dá)式(更新視圖的方法)。
  • Dep類負(fù)責(zé)收集依賴、添加觀察者模式。通知data對應(yīng)的所有觀察者Watcher來更新視圖。在Observer類把每一個data轉(zhuǎn)換成getter和setter時,會創(chuàng)建一個Dep實(shí)例,用來負(fù)責(zé)收集依賴并發(fā)送通知。在每一個data中在getter中收集依賴。在setter中通知依賴,既通知所有Watcher實(shí)例新視圖。
  • Watcher類負(fù)責(zé)數(shù)據(jù)更新后,使關(guān)聯(lián)視圖重新渲染。
  • 實(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。