前端MVVM實(shí)戰(zhàn)-數(shù)據(jù)代理

前言

終于能抽點(diǎn)時間出來繼續(xù)寫這個系列的文章了(其實(shí)是因?yàn)閼楔q(╯^╰)╮),接下來幾節(jié)會開始進(jìn)行實(shí)戰(zhàn)(隨性的更新),還有請看完我之前的幾篇文章再來看實(shí)戰(zhàn),不然有些代碼會看的很蒙。

我會進(jìn)行簡單的包括數(shù)據(jù)代理綁定以及模板解析功能的實(shí)現(xiàn),當(dāng)然和VUE的源碼還是有很大的區(qū)別的,只是去試著實(shí)現(xiàn)一下原理,雖然簡單但是會讓你體會到寫框架你上你也行的感覺┗( ▔, ▔ )┛。

目標(biāo):

  • 數(shù)據(jù)代理
  • 模板解析
  • 解析雙括號
  • v-on綁定事件
  • v-text綁定事件
  • v-class綁定事件
  • v-html綁定事件
  • v-model綁定事件
  • 數(shù)據(jù)綁定
  • 數(shù)據(jù)攔截
  • 訂閱發(fā)布

大部分代碼都是ES5,并且盡量每一行代碼都寫上注釋,方便大家都能看懂

數(shù)據(jù)代理

先來看看vue是怎么處理的

var vm = new Vue({
    el:'#title',
    data:{
        a:'1'
    }
})

vue是遵守MVVM模式的,當(dāng)改變a的數(shù)據(jù)時,view上的a數(shù)據(jù)也會跟著改變,再來看看vue的實(shí)例,在最外層的對象和_data對象上都綁定的有一個a數(shù)據(jù),把鼠標(biāo)放在(...)上時,會顯示一個invoke property getter的提示,意思是這個數(shù)值會通過getter來進(jìn)行獲取,在實(shí)例初始化時data里面定義的數(shù)據(jù)被存儲到了_data對象里面,至于為什么,后面會講到

往下看在這里有兩個函數(shù)分別對應(yīng)a數(shù)據(jù)的代理set和代理get函數(shù)

console.log(vm.a) //其實(shí)是在調(diào)用a的代理getter函數(shù)
vm.a=2 // 其實(shí)在調(diào)用a的代理setter函數(shù)

在這里有一個疑問,明明把a(bǔ)數(shù)據(jù)定義在data對象里面的,但為什么我們可以通過vm直接對a進(jìn)行讀寫操作呢?

這就是所謂的數(shù)據(jù)代理,通過vue對象對data里面的數(shù)據(jù)進(jìn)行代理操作,所有的數(shù)據(jù)獲取和設(shè)置都由vue的實(shí)例去操作。

接下來開始模仿實(shí)現(xiàn)數(shù)據(jù)代理

var mv = new MvvmVue({
    el:'#title',
    data:{
        a:'1'
    }
})

然后新建一個dataProxy.js文件,目錄如下

新建一個MvvmVue對象,并模仿vue存放數(shù)據(jù)

// dataProxy.js
function MvvmVue(options) {
    this.$options = options // 得到傳過來的配置,并存到$options對象
    var data = this._data = this.$options.data // 得到配置里面的data對象,并存到_data
    var _self = this // 保存this對象
    // 遍歷屬性對象JSON
    Object.keys(data).forEach(function (key) {
        // 實(shí)現(xiàn)屬性代理
        ...
    })
}

上面的代碼都很好理解,比較重要的一點(diǎn)就是把初始化的data存儲到了_data里面。
數(shù)據(jù)都存放完畢,將需要代理的數(shù)據(jù),也就是傳入到MvvmVue對象里的數(shù)據(jù),使用Object.keys的方式循環(huán)調(diào)用代理函數(shù)來進(jìn)行數(shù)據(jù)代理

// dataProxy.js
function MvvmVue(options) {
    this.$options = options // 得到傳過來的配置,并存到$options對象
    var data = this._data = this.$options.data // 得到配置里面的data對象,并存到_data
    var _self = this // 保存this對象
    // 遍歷屬性對象JSON
    Object.keys(data).forEach(function (key) {
        // 實(shí)現(xiàn)屬性代理
        _self._proxy(key) // 調(diào)用_proxy函數(shù),進(jìn)行數(shù)據(jù)代理
    })
}

_proxy函數(shù)的實(shí)現(xiàn),這里說一下,在ES6/7之前JS沒有私有屬性和函數(shù)的說法,所以一般默認(rèn)名字前加 _ 線的為私有屬性或函數(shù)。

// dataProxy.js
//這里使用原型鏈添加函數(shù)
MvvmVue.prototype._proxy = function (key) {
    var _self = this // 保存this對象,即MvvmVue對象
    // 這里用到了之前我重點(diǎn)說的defineProperty函數(shù),數(shù)據(jù)代理就全靠它來實(shí)現(xiàn)
    // 將需要代理的對象名添加到對象實(shí)例上,并定義得到方式
    Object.defineProperty(_self, key, {
        configurable: false, // 不能再重新定義
        enumerable: true, // 可以枚舉
        // 當(dāng)讀取對象此屬性值時自動調(diào)用, 將函數(shù)返回的值作為屬性值, this為實(shí)例對象
        // 如實(shí)例去獲取data中的某屬性時
        get() { //getter
            // 返回的是實(shí)例內(nèi)部_data中的對象值,拿取得是_data中的數(shù)據(jù)
            return _self._data[key]
        },
        // 當(dāng)修改了對象的當(dāng)前屬性值時自動調(diào)用, 監(jiān)視當(dāng)前屬性值的變化, 修改相關(guān)的屬性, this為實(shí)例對象
        // 如實(shí)例去設(shè)置data中的某屬性值時
        set(newValue) { //setter
            // 設(shè)置的是實(shí)例內(nèi)部_data中的對象值
            _self._data[key] = newValue
        }
    })
}

這個時候就已經(jīng)有了數(shù)據(jù)代理,當(dāng)我們對數(shù)據(jù)進(jìn)行取值或設(shè)置值得時候都會走getter和setter函數(shù),如下

console.log(mv.a) //其實(shí)是在調(diào)用a的代理getter函數(shù)
mv.a=2 // 其實(shí)在調(diào)用a的代理setter函數(shù)

如果還不夠直觀的話,最佳觀察方式是打開F12,開發(fā)者模式下在代碼上打斷點(diǎn),然后跟著一步一步走,你就會很清楚代碼的運(yùn)行順序了

其他文章導(dǎo)航:

前端MVVM理論-MVC和MVP
前端MVVM理論-MVVM
前端MVVM實(shí)戰(zhàn)-常用的幾個方法和屬性
前端MVVM實(shí)戰(zhàn)-數(shù)據(jù)代理
前端MVVM實(shí)戰(zhàn)-模板解析之雙括號解析
前端MVVM實(shí)戰(zhàn)-模板解析之事件指令和一般指令
前端MVVM實(shí)戰(zhàn)-數(shù)據(jù)綁定(一)
前端MVVM實(shí)戰(zhàn)-數(shù)據(jù)綁定(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容