如果你有讀過(guò)Vue的源碼,或者有了解過(guò)Vue的響應(yīng)原理,那么你一定知道Object.defineProperty(), 那么你也應(yīng)該知道,Vue 2.x里,是通過(guò) 遞歸 + 遍歷 data 對(duì)象來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的監(jiān)控的, 你可能還會(huì)知道,我們使用的時(shí)候,直接通過(guò)數(shù)組的下標(biāo)給數(shù)組設(shè)置值,不能實(shí)時(shí)響應(yīng),是因?yàn)?code>Object.defineProperty() 無(wú)法監(jiān)控到數(shù)組下標(biāo)的變化,而我們平常所用的數(shù)組方法 push , pop , shift , unshift , splice , sort , reverse
, 其實(shí)不是真正的數(shù)組方法,而是被修改過(guò)的,這些都是因?yàn)?code> Object.defineProperty() 提供的能力有限,無(wú)法做到完美。
創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)欽北,十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
網(wǎng)上看過(guò)很多關(guān)于Vue的源碼解讀或者實(shí)現(xiàn)一個(gè)簡(jiǎn)易版的Vue的教程,還都是用 Object.defineProperty (大概是為兼容性考慮吧), 而 Object.defineProperty() 確實(shí)存在諸多限制, 據(jù)說(shuō)Vue的3.x版本會(huì)改用Proxy,那么今天我們就先來(lái)嘗嘗鮮,用Proxy實(shí)現(xiàn)一個(gè)簡(jiǎn)單版的Vue
proxy 介紹
Proxy 用于修改某些操作的默認(rèn)行為,等同于在語(yǔ)言層面做出修改,所以屬于一種“元編程”(meta programming),即對(duì)編程語(yǔ)言進(jìn)行編程。
Proxy 可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層“攔截”,外界對(duì)該對(duì)象的訪問(wèn),都必須先通過(guò)這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪問(wèn)進(jìn)行過(guò)濾和改寫(xiě)。Proxy 這個(gè)詞的原意是代理,用在這里表示由它來(lái)“代理”某些操作,可以譯為“代理器”。
以上引用內(nèi)容來(lái)自阮一峰的es6教程的Proxy章節(jié) 原文地址請(qǐng)戳這里。
我們來(lái)看看如何用Proxy去定義一個(gè)監(jiān)聽(tīng)數(shù)據(jù)的函數(shù)
定義 observe
_observe (data){ var that = this // 把代理器返回的對(duì)象存到 this.$data 里面 this.$data = new Proxy(data, { set(target,key,value){ // 利用 Reflect 還原默認(rèn)的賦值操作 let res = Reflect.set(target,key,value) // 這行就是監(jiān)控代碼了 that.handles[key].map(item => {item.update()}) return res } }) }
當(dāng)觸發(fā)set的時(shí)候,就會(huì)執(zhí)行 that.handles[key].map(item => {item.update()})
,這句代碼的作用就是執(zhí)行 該屬性名下的所有 "監(jiān)視器"
那么,監(jiān)視器怎么來(lái)的呢? 請(qǐng)繼續(xù)看以下代碼
定義 compile
_compile (root){ const nodes = Array.prototype.slice.call(root.children) let data = this.$data nodes.map(node => { // 如果不是末尾節(jié)點(diǎn),就遞歸 if(node.children.length > 0) this._complie(node) // 處理 v-bind 指令 if(node.hasAttribute('v-bind')) { let key = node.getAttribute('v-bind') this._pushWatcher(new Watcher(node,'innerHTML',data,key)) } // 處理 v-model 指令 if(node.hasAttribute('v-model')) { let key = node.getAttribute('v-model') this._pushWatcher(new Watcher(node,'value',data,key)) node.addEventListener('input',() => {data[key] = node.value}) } // 處理 v-click 指令 if(node.hasAttribute('v-click')) { let methodName = node.getAttribute('v-click') let mothod = this.$methods[methodName].bind(data) node.addEventListener('click',mothod) } }) }
上面這段代碼,看起來(lái)很長(zhǎng),可是實(shí)際上,只做了意見(jiàn)很簡(jiǎn)單的事情, 就是 "編譯" html 模板 ,把有 v-bind 、 v-model 、 v-click 都給加上對(duì)應(yīng)的 通知 和 監(jiān)控
1.通知就是 this._pushWatcher(...)
, 相當(dāng)于是安裝一個(gè)監(jiān)聽(tīng)器,這樣只要 this.$data 有發(fā)生 set 操作的話,就會(huì)執(zhí)行 this._pushWatcher 括號(hào)里面?zhèn)鞯暮瘮?shù),來(lái)通知節(jié)點(diǎn)更新數(shù)據(jù)
2.監(jiān)控就是 node.addEventListener(...)
監(jiān)聽(tīng)相應(yīng)的事件,然后執(zhí)行對(duì)應(yīng)的 watcher 或者 methods
this._pushWatcher 又做了什么呢?
_pushWatcher (watcher) { if (!this._binding[watcher.key]) this._binding[watcher.key] = [] this._binding[watcher.key].push(watcher) }
這個(gè)就更簡(jiǎn)單了,如果 this._binding[watcher.key]
為空,就初始化,然后向里面添加一個(gè) 監(jiān)聽(tīng)器
最后,我們?cè)賮?lái)看看,監(jiān)聽(tīng)器是怎么實(shí)現(xiàn)的
定義 Watcher
class Watcher { constructor (node,attr,data,key) { this.node = node this.attr = attr this.data = data this.key = key } update () { this.node[this.attr] = this.data[this.key] } }
Watcher 是一個(gè)類,只有一個(gè)方法,就是更新數(shù)據(jù),怎么知道要更新哪個(gè)節(jié)點(diǎn),更新為什么數(shù)據(jù)呢? 在實(shí)例化(new)的時(shí)候,傳的參數(shù)就是定義這些的
這樣,我們就實(shí)現(xiàn)初步的雙向綁定了,整個(gè)代碼大概只有50行。其實(shí)還可以更少, 但是更少的話,就是繼續(xù)閹割功能了(雖然目前實(shí)現(xiàn)的也是嚴(yán)重閹割版的), 但是我覺(jué)得實(shí)現(xiàn)這些,剛好可以不多不少幫我我們理解vue的本質(zhì)。
最后
本文最終實(shí)現(xiàn)代碼已經(jīng)放在 github 上,想要直接看效果的同學(xué),可以上去直接copy,運(yùn)行。
如果覺(jué)得本文對(duì)您有用,請(qǐng)給本文的github 加個(gè)star,萬(wàn)分感謝
另外, github 上還有其他一些關(guān)于前端的教程和組件,有興趣的童鞋可以看看,你們的支持就是我最大的動(dòng)力。
總結(jié)
以上所述是小編給大家介紹的使用proxy實(shí)現(xiàn)一個(gè)更優(yōu)雅的vue,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!