本篇內(nèi)容主要講解“怎么理解vue2.0響應(yīng)式架構(gòu)”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“怎么理解vue2.0響應(yīng)式架構(gòu)”吧!
創(chuàng)新互聯(lián)公司:從2013年創(chuàng)立為各行業(yè)開(kāi)拓出企業(yè)自己的“網(wǎng)站建設(shè)”服務(wù),為上1000家公司企業(yè)提供了專(zhuān)業(yè)的網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁(yè)設(shè)計(jì)和網(wǎng)站推廣服務(wù), 按需搭建網(wǎng)站由設(shè)計(jì)師親自精心設(shè)計(jì),設(shè)計(jì)的效果完全按照客戶(hù)的要求,并適當(dāng)?shù)奶岢龊侠淼慕ㄗh,擁有的視覺(jué)效果,策劃師分析客戶(hù)的同行競(jìng)爭(zhēng)對(duì)手,根據(jù)客戶(hù)的實(shí)際情況給出合理的網(wǎng)站構(gòu)架,制作客戶(hù)同行業(yè)具有領(lǐng)先地位的。
講data 下面所有屬性變?yōu)閛bservable
來(lái)來(lái)來(lái)先看代碼吧
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)
為了好演示我們只考慮最簡(jiǎn)單的情況,如果看了vue 源碼分析之如何實(shí)現(xiàn) observer 和 watcher可能就會(huì)很好理解,不過(guò)沒(méi)關(guān)系,我們?nèi)詢(xún)烧Z(yǔ)再說(shuō)說(shuō),這段代碼要實(shí)現(xiàn)的功能就是將
var demo = new Vue({ el: '#demo', data: { text: 123, }, render(){ console.log("我要render了") } })
中data 里面所有的屬性置于 observer,然后data里面的屬性,比如 text 以改變,就引起_update()函數(shù)調(diào)用進(jìn)而重新渲染,是怎樣做到的呢,我們知道其實(shí)就是賦值的時(shí)候就要改變對(duì)吧,當(dāng)我給data下面的text 賦值的時(shí)候 set 函數(shù)就會(huì)觸發(fā),這個(gè)時(shí)候 調(diào)用_update 就ok了,但是
setTimeout(function(){ demo._data.text = 444 }, 3000)
demo._data.text沒(méi)有demo.text用著爽,沒(méi)關(guān)系,我們加一個(gè)代理
_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))
***步先說(shuō)到這里,我們會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,data中任何一個(gè)屬性的值改變,都會(huì)引起
_update的觸發(fā)進(jìn)而重新渲染,屬性這顯然不夠精準(zhǔn)啊
第二步,詳細(xì)闡述***步為什么不夠精準(zhǔn)
比如考慮下面代碼
new Vue({ template: `name: {{name}} age: {{age}} `, data: { name: 'js', age: 24, height: 180 } }) setTimeout(function(){ demo.height = 181 }, 3000)template里面只用到了data上的兩個(gè)屬性name和age,但是當(dāng)我改變height的時(shí)候,用***步的代碼,會(huì)不會(huì)觸發(fā)重新渲染?會(huì)!,但其實(shí)不需要觸發(fā)重新渲染,這就是問(wèn)題所在!!
第三步,上述問(wèn)題怎么解決
簡(jiǎn)單說(shuō)說(shuō)虛擬 DOM
首先,template***都是編譯成render函數(shù)的(具體怎么做,就不展開(kāi)說(shuō)了,以后我會(huì)說(shuō)的),然后render 函數(shù)執(zhí)行完就會(huì)得到一個(gè)虛擬DOM,為了好理解我們寫(xiě)寫(xiě)最簡(jiǎn)單的虛擬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)]) ]) } })我們運(yùn)行一下,他會(huì)輸出
{ tag: 'div', data: {}, children:[ { tag: 'span', data: {}, children: [ { children: undefined, data: undefined, tag: undefined, text: '' // 正常情況為 字符串 before,因?yàn)槲覀優(yōu)榱搜菔揪筒粚?xiě)代理的代碼,所以這里為空 } ] } ] }這就是 虛擬最簡(jiǎn)單虛擬DOM,tag是html 標(biāo)簽名,data 是包含諸如 class 和 style 這些標(biāo)簽上的屬性,childen就是子節(jié)點(diǎn),關(guān)于虛擬DOM就不展開(kāi)說(shuō)了。
回到開(kāi)始的問(wèn)題,也就是說(shuō),我得知道,render 函數(shù)里面依賴(lài)了vue實(shí)例里面哪些變量(只考慮render 就可以,因?yàn)閠emplate 也會(huì)是幫你編譯成render)。敘述有點(diǎn)拗口,還是看代碼吧
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ù)里其實(shí)只依賴(lài)text,并沒(méi)有依賴(lài) name和 age,所以,我們只要text改變的時(shí)候
我們自動(dòng)觸發(fā) render 函數(shù) 讓它生成一個(gè)虛擬DOM就ok了(剩下的就是這個(gè)虛擬DOM和上個(gè)虛擬DOM做比對(duì),然后操作真實(shí)DOM,只能以后再說(shuō)了),那么我們正式考慮一下怎么做
第三步,'touch' 拿到依賴(lài)
回到最上面那張圖,我們知道data上的屬性設(shè)置defineReactive后,修改data 上的值會(huì)觸發(fā) set。
那么我們?nèi)ata上值是會(huì)觸發(fā) get了。
對(duì),我們可以在上面做做手腳,我們先執(zhí)行一下render,我們看看data上哪些屬性觸發(fā)了get,我們豈不是就可以知道 render 會(huì)依賴(lài)data 上哪些變量了。
然后我么把這些變量做些手腳,每次這些變量變的時(shí)候,我們就觸發(fā)render。
上面這些步驟簡(jiǎn)單用四個(gè)子概括就是 計(jì)算依賴(lài)。
(其實(shí)不僅是render,任何一個(gè)變量的改別,是因?yàn)閯e的變量改變引起,都可以用上述方法,也就是computed 和 watch 的原理,也是mobx的核心)
***步,
我們寫(xiě)一個(gè)依賴(lài)收集的類(lèi),每一個(gè)data 上的對(duì)象都有可能被render函數(shù)依賴(lài),所以每個(gè)屬性在defineReactive
時(shí)候就初始化它,簡(jiǎn)單來(lái)說(shuō)就是這個(gè)樣子的
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, { // 省略 }) }然后,當(dāng)執(zhí)行render 函數(shù)去'touch'依賴(lài)的時(shí)候,依賴(lài)到的變量get就會(huì)被執(zhí)行,然后我們就可以把這個(gè) render 函數(shù)加到 subs 里面去了。
當(dāng)我們,set 的時(shí)候 我們就執(zhí)行 notify 將所有的subs數(shù)組里的函數(shù)執(zhí)行,其中就包含render 的執(zhí)行。
至此就完成了整個(gè)圖,好我們將所有的代碼展示出來(lái)
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)我們看一下運(yùn)行結(jié)果
好我們解釋一下 Dep.target 因?yàn)槲覀兊脜^(qū)分是,普通的get,還是在查找依賴(lài)的時(shí)候的get,
所有我們?cè)诓檎乙蕾?lài)時(shí)候,我們將
function watch(vm, exp, cb){ Dep.target = cb return exp() }Dep.target 賦值,相當(dāng)于 flag 一下,然后 get 的時(shí)候
get: () => { if (Dep.target) { dep.add(Dep.target) } return val },到此,相信大家對(duì)“怎么理解vue2.0響應(yīng)式架構(gòu)”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!
新聞標(biāo)題:怎么理解vue2.0響應(yīng)式架構(gòu)
網(wǎng)頁(yè)URL:http://weahome.cn/article/psphgi.html