vue中怎么實(shí)現(xiàn)觀察者模式,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
成都創(chuàng)新互聯(lián)主要從事做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)和龍,10余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220
1 響應(yīng)式原理
讓我們先從相應(yīng)式原理開始。我們可以通過Object.defineProterty()來自定義Object的getter和setter 從而達(dá)到我們的目的。
代碼如下
function observe(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: ()=>{ /*....依賴收集等....*/ /*Github:https://github.com/answershuto*/ return val }, set:newVal=> { val = newVal; cb();/*訂閱者收到消息的回調(diào)*/ } }) } class Vue { constructor(options) { this._data = options.data; observe(this._data, options.render) } } let app = new Vue({ el: '#app', data: { text: 'text', text2: 'text2' }, render(){ console.log("render"); } })
通過observe函數(shù)對(duì)app.data上的每一個(gè)key和value都設(shè)定getter和setter。當(dāng)value改變的時(shí)候觸發(fā)setter,就會(huì)觸發(fā)render這個(gè)函數(shù)。響應(yīng)式的目的就達(dá)成,如果是視圖更新的話我們通過監(jiān)聽dom的input事件來觸發(fā)數(shù)據(jù)更新
但是現(xiàn)在我們只有在改變vue._data.text的時(shí)候才會(huì)觸發(fā)他們的setter,但是我想偷懶,只改變vue.text就能觸發(fā)到setter怎么做呢?
我們使用代理的方法
_proxy.call(this, options.data);/*構(gòu)造函數(shù)中*/ /*代理*/ function _proxy (data) { const that = this; Object.keys(data).forEach(key => { Object.defineProperty(that, key, { configurable: true, enumerable: true, get: function proxyGetter () { return that._data[key]; }, set: function proxySetter (val) { that._data[key] = val; } }) }); }
依賴收集
讓我們?cè)賮砜纯聪旅娴拇a
new Vue({ template: `<div> <span>text1:</span> {{text1}} <span>text2:</span> {{text2}} <div>`, data: { text1: 'text1', text2: 'text2', text3: 'text3' } });
當(dāng)你的text3變化的時(shí)候,實(shí)際上text3并沒有被渲染,但是也會(huì)觸發(fā)一次render函數(shù),這顯然是不對(duì)的。所以我們需要收集依賴。
我們只需要在初始化的時(shí)候渲染一遍,那所有渲染所依賴的數(shù)據(jù)都會(huì)被觸發(fā)getter,這時(shí)候我們只要把這個(gè)數(shù)據(jù)放到一個(gè)列表里就好啦!
我們先來認(rèn)識(shí)一下Dep(dependencies)這個(gè)類,下圖是一個(gè)最簡(jiǎn)單的Dep類。我們可以把他理解為發(fā)布者(這點(diǎn)很重要!!)
class Dep { constructor () { this.subs = []; } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } /*Github:https://github.com/answershuto*/ notify () { // stabilize the subscriber list first const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } function remove (arr, item) { if (arr.length) { const index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } }
我們每次觸發(fā)getter的時(shí)候,只要把觸發(fā)的對(duì)象放到dep.sub里面就好啦!
但是現(xiàn)在問題來了,我們用什么來裝這個(gè)觸發(fā)的'對(duì)象',也可以說式訂閱者呢?
我們使用Watcher這個(gè)類
class Watcher { constructor (vm, expOrFn, cb, options) { this.cb = cb; this.vm = vm; /*在這里將觀察者本身賦值給全局的target,只有被target標(biāo)記過的才會(huì)進(jìn)行依賴收集*/ Dep.target = this; /*Github:https://github.com/answershuto*/ /*觸發(fā)渲染操作進(jìn)行依賴收集*/ this.cb.call(this.vm); } update () { this.cb.call(this.vm); } }
vm即是vue實(shí)例, expOrFn就是{{a+b}}里面的a+b, cb就是回調(diào)函數(shù)就是return a+b, options是一些配置項(xiàng)。
Vue在第一次渲染列表的時(shí)候如果碰到{{xxx}}這樣的表達(dá)式,就會(huì)new Watcher()。解析里面的函數(shù),然后把當(dāng)前的watcher實(shí)例賦給Dep.target(Dep.target是全局的,一次性只能有一個(gè)存在,因?yàn)閂ue一次只處理一個(gè)依賴)。然后執(zhí)行回調(diào)函數(shù)。(這里看似是執(zhí)行回調(diào)函數(shù)渲染,其實(shí)又觸發(fā)了一次getter,然后就會(huì)把當(dāng)前的依賴添加到sub里去)
接下來開始依賴收集
class Vue { constructor(options) { this._data = options.data; observer(this._data, options.render); let watcher = new Watcher(this, ); } } function defineReactive (obj, key, val, cb) { /*在閉包內(nèi)存儲(chǔ)一個(gè)Dep對(duì)象*/ const dep = new Dep(); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: ()=>{ if (Dep.target) { /*Watcher對(duì)象存在全局的Dep.target中*/ dep.addSub(Dep.target); } }, set:newVal=> { /*只有之前addSub中的函數(shù)才會(huì)觸發(fā)*/ dep.notify(); } }) } Dep.target = null; //防止依賴重復(fù)添加
這兒我們通過示例來講解
<template> <div> {{a+b}} </div> <div> {{a-c}} </div> </template> <script> let app = new Vue( { data :{ a: 1, b: 2, c: 3 } })
我們編譯到{{a+b}},會(huì)去實(shí)例化一個(gè)對(duì)應(yīng)的Watcher對(duì)象,Watcher的構(gòu)造函數(shù)中有這么一句
this.cb.call(this.vm);this.cb指的是function(){return a+b};this.vm指的是這個(gè)vue對(duì)象,這樣就會(huì)觸發(fā)vue.a和vue.b的getter方法,a,b都有自己的dep對(duì)象,我們通過Dep.target將這個(gè)Watcher對(duì)象就加到dep的subs數(shù)組中去了,當(dāng)我們變更a或者b是就會(huì)觸發(fā)setter,進(jìn)而觸發(fā)subs數(shù)組中的update方法,視圖中的a+b就會(huì)更新
有個(gè)小知識(shí)點(diǎn):我們新建一個(gè)屬性對(duì)象時(shí)必須通過Vue.set的方法去實(shí)現(xiàn),而不能直接通過=實(shí)現(xiàn),這樣會(huì)檢測(cè)不到,因?yàn)槲覀冊(cè)诔跏蓟瘯r(shí)就通過defineProperty重構(gòu)了這個(gè)對(duì)象屬性的getter和setter方法,新建的屬性則沒有所以不會(huì)被檢測(cè)到
下圖為Vue框架在數(shù)據(jù)初始化中使用觀察者模式的示意圖:
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。
新聞名稱:vue中怎么實(shí)現(xiàn)觀察者模式
網(wǎng)站路徑:http://www.chinadenli.net/article18/gpesdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、小程序開發(fā)、做網(wǎng)站、動(dòng)態(tài)網(wǎng)站、企業(yè)建站、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)