溫馨提示×

溫馨提示×

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

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

怎么理解vue2.0響應式架構

發(fā)布時間:2021-11-17 15:03:54 來源:億速云 閱讀:170 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“怎么理解vue2.0響應式架構”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么理解vue2.0響應式架構”吧!

講data 下面所有屬性變?yōu)閛bservable

來來來先看代碼吧

class Vue {       constructor(options) {         this.$options = options         this._data = options.data         observer(options.data, this._update)         this._update()       }       _update(){         this.$options.render()       }     }       function observer(value, cb){       Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))     }      function defineReactive(obj, key, val, cb) {       Object.defineProperty(obj, key, {         enumerable: true,         configurable: true,         get: ()=>{},         set:newVal=> {           cb()         }       })     }      var demo = new Vue({       el: '#demo',       data: {         text: 123,       },       render(){         console.log("我要render了")       }     })       setTimeout(function(){        demo._data.text = 444      }, 3000)

為了好演示我們只考慮最簡單的情況,如果看了vue 源碼分析之如何實現(xiàn) observer 和  watcher可能就會很好理解,不過沒關系,我們?nèi)詢烧Z再說說,這段代碼要實現(xiàn)的功能就是將

var demo = new Vue({      el: '#demo',      data: {        text: 123,      },      render(){        console.log("我要render了")      }    })

中data 里面所有的屬性置于 observer,然后data里面的屬性,比如 text  以改變,就引起_update()函數(shù)調(diào)用進而重新渲染,是怎樣做到的呢,我們知道其實就是賦值的時候就要改變對吧,當我給data下面的text 賦值的時候 set  函數(shù)就會觸發(fā),這個時候 調(diào)用_update 就ok了,但是

setTimeout(function(){       demo._data.text = 444     }, 3000)

demo._data.text沒有demo.text用著爽,沒關系,我們加一個代理

_proxy(key) {       const self = this       Object.defineProperty(self, key, {         configurable: true,         enumerable: true,         get: function proxyGetter () {           return self._data[key]         },         set: function proxySetter (val) {           self._data[key] = val         }       })     }

然后在Vue的constructor加上下面這句

Object.keys(options.data).forEach(key => this._proxy(key))

***步先說到這里,我們會發(fā)現(xiàn)一個問題,data中任何一個屬性的值改變,都會引起

_update的觸發(fā)進而重新渲染,屬性這顯然不夠精準啊

第二步,詳細闡述***步為什么不夠精準

比如考慮下面代碼

new Vue({      template: `        <div>          <section>            <span>name:</span> {{name}}          </section>          <section>            <span>age:</span> {{age}}          </section>        <div>`,      data: {        name: 'js',        age: 24,        height: 180      }    })     setTimeout(function(){      demo.height = 181    }, 3000)

template里面只用到了data上的兩個屬性name和age,但是當我改變height的時候,用***步的代碼,會不會觸發(fā)重新渲染?會!,但其實不需要觸發(fā)重新渲染,這就是問題所在!!

第三步,上述問題怎么解決

簡單說說虛擬 DOM

首先,template***都是編譯成render函數(shù)的(具體怎么做,就不展開說了,以后我會說的),然后render  函數(shù)執(zhí)行完就會得到一個虛擬DOM,為了好理解我們寫寫最簡單的虛擬DOM

function VNode(tag, data, children, text) {       return {         tag: tag,         data: data,         children: children,         text: text       }     }      class Vue {       constructor(options) {         this.$options = options         const vdom = this._update()         console.log(vdom)       }       _update() {         return this._render.call(this)       }       _render() {         const vnode = this.$options.render.call(this)         return vnode       }       __h__(tag, attr, children) {         return VNode(tag, attr, children.map((child)=>{           if(typeof child === 'string'){             return VNode(undefined, undefined, undefined, child)           }else{             return child           }         }))       }       __toString__(val) {         return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);       }     }       var demo = new Vue({       el: '#demo',       data: {         text: "before",       },       render(){         return this.__h__('div', {}, [           this.__h__('span', {}, [this.__toString__(this.text)])         ])       }     })

我們運行一下,他會輸出

{       tag: 'div',       data: {},       children:[         {           tag: 'span',           data: {},           children: [             {               children: undefined,               data: undefined,               tag: undefined,               text: '' // 正常情況為 字符串 before,因為我們?yōu)榱搜菔揪筒粚懘淼拇a,所以這里為空             }           ]         }       ]     }

這就是 虛擬最簡單虛擬DOM,tag是html 標簽名,data 是包含諸如 class 和 style  這些標簽上的屬性,childen就是子節(jié)點,關于虛擬DOM就不展開說了。

回到開始的問題,也就是說,我得知道,render 函數(shù)里面依賴了vue實例里面哪些變量(只考慮render 就可以,因為template  也會是幫你編譯成render)。敘述有點拗口,還是看代碼吧

var demo = new Vue({       el: '#demo',       data: {         text: "before",         name: "123",         age: 23       },       render(){         return this.__h__('div', {}, [           this.__h__('span', {}, [this.__toString__(this.text)])         ])       }     })

就像這段代碼,render 函數(shù)里其實只依賴text,并沒有依賴 name和 age,所以,我們只要text改變的時候

我們自動觸發(fā) render 函數(shù)  讓它生成一個虛擬DOM就ok了(剩下的就是這個虛擬DOM和上個虛擬DOM做比對,然后操作真實DOM,只能以后再說了),那么我們正式考慮一下怎么做

第三步,'touch' 拿到依賴

回到最上面那張圖,我們知道data上的屬性設置defineReactive后,修改data 上的值會觸發(fā) set。

那么我們?nèi)ata上值是會觸發(fā) get了。

對,我們可以在上面做做手腳,我們先執(zhí)行一下render,我們看看data上哪些屬性觸發(fā)了get,我們豈不是就可以知道 render 會依賴data  上哪些變量了。

然后我么把這些變量做些手腳,每次這些變量變的時候,我們就觸發(fā)render。

上面這些步驟簡單用四個子概括就是 計算依賴。

(其實不僅是render,任何一個變量的改別,是因為別的變量改變引起,都可以用上述方法,也就是computed 和 watch  的原理,也是mobx的核心)

***步,

我們寫一個依賴收集的類,每一個data 上的對象都有可能被render函數(shù)依賴,所以每個屬性在defineReactive

時候就初始化它,簡單來說就是這個樣子的

class Dep {       constructor() {         this.subs = []       }       add(cb) {         this.subs.push(cb)       }       notify() {         console.log(this.subs);         this.subs.forEach((cb) => cb())       }     }     function defineReactive(obj, key, val, cb) {       const dep = new Dep()       Object.defineProperty(obj, key, {         // 省略       })     }

然后,當執(zhí)行render 函數(shù)去'touch'依賴的時候,依賴到的變量get就會被執(zhí)行,然后我們就可以把這個 render 函數(shù)加到 subs  里面去了。

當我們,set 的時候 我們就執(zhí)行 notify 將所有的subs數(shù)組里的函數(shù)執(zhí)行,其中就包含render 的執(zhí)行。

至此就完成了整個圖,好我們將所有的代碼展示出來

function VNode(tag, data, children, text) {      return {        tag: tag,        data: data,        children: children,        text: text      }    }     class Vue {      constructor(options) {        this.$options = options        this._data = options.data        Object.keys(options.data).forEach(key => this._proxy(key))        observer(options.data)        const vdom = watch(this, this._render.bind(this), this._update.bind(this))        console.log(vdom)      }      _proxy(key) {        const self = this        Object.defineProperty(self, key, {          configurable: true,          enumerable: true,          get: function proxyGetter () {            return self._data[key]          },          set: function proxySetter (val) {            self._data.text = val          }        })      }      _update() {        console.log("我需要更新");        const vdom = this._render.call(this)        console.log(vdom);      }      _render() {        return this.$options.render.call(this)      }      __h__(tag, attr, children) {        return VNode(tag, attr, children.map((child)=>{          if(typeof child === 'string'){            return VNode(undefined, undefined, undefined, child)          }else{            return child          }        }))      }      __toString__(val) {        return val == null ? '' : typeof val === 'object' ? JSON.stringify(val, null, 2) : String(val);      }    }     function observer(value, cb){      Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))    }     function defineReactive(obj, key, val, cb) {      const dep = new Dep()      Object.defineProperty(obj, key, {        enumerable: true,        configurable: true,        get: ()=>{          if(Dep.target){            dep.add(Dep.target)          }          return val        },        set: newVal => {          if(newVal === val)            return          val = newVal          dep.notify()        }      })    }    function watch(vm, exp, cb){      Dep.target = cb      return exp()    }     class Dep {      constructor() {        this.subs = []      }      add(cb) {        this.subs.push(cb)      }      notify() {        this.subs.forEach((cb) => cb())      }    }    Dep.target = null      var demo = new Vue({      el: '#demo',      data: {        text: "before",      },      render(){        return this.__h__('div', {}, [          this.__h__('span', {}, [this.__toString__(this.text)])        ])      }    })       setTimeout(function(){       demo.text = "after"     }, 3000)

我們看一下運行結果

怎么理解vue2.0響應式架構

好我們解釋一下 Dep.target 因為我們得區(qū)分是,普通的get,還是在查找依賴的時候的get,

所有我們在查找依賴時候,我們將

function watch(vm, exp, cb){       Dep.target = cb       return exp()     }

Dep.target 賦值,相當于 flag 一下,然后 get 的時候

get: () => {           if (Dep.target) {             dep.add(Dep.target)           }           return val         },

到此,相信大家對“怎么理解vue2.0響應式架構”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內(nèi)容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

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

vue
AI