Vue雙向數(shù)據(jù)綁定

Vue 的雙向數(shù)據(jù)綁定采用defineProperty(3.0以前) 以及 發(fā)布訂閱模式來實(shí)現(xiàn)的披坏。
defineProperty 劫持 set 與get揖闸,在set 時(shí) 通過Dep.target 判斷是否要監(jiān)聽鸠蚪,在set時(shí)通知所有訂閱者孙咪。訂閱者判斷新值與舊值是否一致宁改,若不一致就調(diào)用callback 。

defineProperty 劫持 set get

 let app = document.getElementById('app')
    let input = document.createElement('input')
    app.appendChild(input)
    let span = document.createElement('span')
    

    app.appendChild(span)
 
    let object = {}
    Object.defineProperty(object,'a',{
        get:function(){
           return this._a   //返回a
        },
        set:function(value){ // set方法更新視圖
            span.innerHTML = value
            this._a = value
        }
    })
    input.oninput=function(e){
        object.a = e.target.value // 觸發(fā)set方法 
        console.log(object.a)   // 觸發(fā)get方法
    }
   

發(fā)布訂閱模式

 let app = document.getElementById('app')
    function init(name) { // 初始化創(chuàng)建視圖
        this._p = document.createElement('p')
        this._p.innerHTML = name
        app.appendChild(this._p)
        this._input = document.createElement('input')
       
        app.appendChild(this._input)
    }
    function Rmb() { // 發(fā)布者
        this._registers = []  // 存放訂閱者數(shù)組
        this._input = null
        init.call(this, '¥')
        this.bindEvent()   // 綁定方法
        
    }
    Rmb.prototype.regs = function (reg) {  // 訂閱方法愉棱,將訂閱者存入
        this._registers.push(reg)
    }
    Rmb.prototype.bindEvent = function () {   
        let self = this
        self._input.oninput = function () { 
            // 通過 發(fā)布者數(shù)據(jù)改變調(diào)用 訂閱者change 方法
            self._registers.forEach(item => {
                item.change(self._input.value)
            })
        }
    }
    let rmb = new Rmb()
    function FM(name, rate) { //訂閱者
        this._rate = rate
        this._input = null
        init.call(this, name)
        rmb.regs(this)  // 注冊訂閱
    }
    FM.prototype.change = function (value) {
        this._input.value = value * this._rate //計(jì)算
    }
    let waibi1 = new FM('$', 0.3)
    let waibi2 = new FM('日元', 10)

雙向數(shù)據(jù)綁定

首先需要設(shè)置一個(gè)Observer唆铐,用來監(jiān)聽所有屬性,屬性發(fā)生變化奔滑,就告訴Watcher艾岂,Watcher判定是否需要更新,需要一個(gè)消息訂閱中心Dep來實(shí)現(xiàn)統(tǒng)一管理朋其。

  1. 實(shí)現(xiàn)一個(gè)監(jiān)聽器 Observer 王浴,用來劫持并監(jiān)聽所有屬性,如果有變動(dòng)就通知訂閱者
    2.實(shí)現(xiàn)一個(gè)訂閱者Watcher梅猿,可以接收到屬性變化并通知相應(yīng)函數(shù)氓辣,從而更新試圖

實(shí)現(xiàn)Observer

function defineRective(data, key, val) { // 監(jiān)聽 data 的key 
        observer(val) // 遞歸 監(jiān)聽
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                return val
            },
            set: function (newVal) {
                console.log('val: '+val+' newVal: '+newVal)
                val = newVal
               
            }
        })
    }

    function observer(data){
        if(!data || typeof data !== 'object'){
            return 
        }
        //遍歷對象
        Object.keys(data).forEach((key)=>{
            defineRective(data,key,data[key])
        })
    }
    let obj={
        name:'123',
        array:[1,2,3,4],
        oj:{
            '1':'xiaoming',
            '2':'xiaohua'
        }
    }
    observer(obj)
    obj.name = '456'
    obj.array=['1','2','4']
    obj.oj['1']='ddd'

實(shí)現(xiàn)一個(gè)watcher

 // 設(shè)置watcher
    function Watcher(vm,exp,cb){
        this.vm = vm,  // 實(shí)例
        this.exp = exp,  //屬性
        this.cb = cb  // 回調(diào)
        this.value = this.get() 
    }
    Watcher.prototype.get = function(){
        // 將target指向自己
        Dep.target = this
        let value = this.vm.data[this.exp]
        // 釋放 traget
        Dep.target = null
        return value
    }
    // 更新數(shù)據(jù)的狀態(tài)
    Watcher.prototype.update = function(){
        this.run()
    }
    Watcher.prototype.run = function(){
        let value = this.vm.data[this.exp]
        let oldVal = this.value
        if(value !== oldVal){
            this.value = value
            this.cb.call(this.vm,value,oldVal)
        }

    }

完整代碼

function defineRective(data, key, val) { // 監(jiān)聽 data 的key 
        observer(val) // 遞歸 監(jiān)聽
        let dep = new Dep()
        Object.defineProperty(data, key, {
            enumerable: true,
            configurable: true,
            get: function () {
                // 在這里判斷是否添加一個(gè)訂閱者
                if(Dep.target){
                    dep.addSub(Dep.target)
                }
                return val
            },
            set: function (newVal) {
                val = newVal
                dep.notify() // 有更新 就發(fā)布
            }
        })
    }

    function observer(data){
        if(!data || typeof data !== 'object'){
            return 
        }
        //遍歷對象
        Object.keys(data).forEach((key)=>{
            defineRective(data,key,data[key])
        })
    }
   
    function Dep(){
        this.subs = [] // 維護(hù)一個(gè)訂閱者數(shù)組
    }
    Dep.prototype.addSub = function(sub){ // 
        this.subs.push(sub) 
    }
    // 發(fā)布方法
    Dep.prototype.notify = function(){
        this.subs.forEach((sub)=>{
            sub.update()
        }) // 收到消息更新sub
    }
    
    Dep.target = null

    // 設(shè)置watcher
    function Watcher(vm,exp,cb){
        this.vm = vm,  // 實(shí)例
        this.exp = exp,  //屬性
        this.cb = cb  // 回調(diào)
        this.value = this.get() 
    }
    Watcher.prototype.get = function(){
        // 將target指向自己
        Dep.target = this
        let value = this.vm.data[this.exp]
        // 釋放 traget
        Dep.target = null
        return value
    }
    // 更新數(shù)據(jù)的狀態(tài)
    Watcher.prototype.update = function(){
        this.run()
    }
    Watcher.prototype.run = function(){
        let value = this.vm.data[this.exp]
        let oldVal = this.value
        if(value !== oldVal){
            this.value = value
            this.cb.call(this.vm,value,oldVal)
        }

    }
    // 將observer 與watcher 關(guān)聯(lián)
    function SelfVue(data,el,exp){
        this.data = data
        observer(data)
        el.innerHTML = this.data[exp]
        new Watcher(this,exp,function(value){
            el.innerHTML = value
        })
    }
    let ele = document.getElementById('app')
    let selfVue = new SelfVue({name:'myVue',},ele,'name')
    window.setTimeout(function(){
        selfVue.data.name='Hello World!'
    },2000)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市袱蚓,隨后出現(xiàn)的幾起案子钞啸,更是在濱河造成了極大的恐慌,老刑警劉巖喇潘,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件体斩,死亡現(xiàn)場離奇詭異,居然都是意外死亡颖低,警方通過查閱死者的電腦和手機(jī)絮吵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忱屑,“玉大人蹬敲,你說我怎么就攤上這事≥航洌” “怎么了粱栖?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長脏毯。 經(jīng)常有香客問我闹究,道長,這世上最難降的妖魔是什么食店? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任渣淤,我火速辦了婚禮,結(jié)果婚禮上吉嫩,老公的妹妹穿的比我還像新娘价认。我一直安慰自己,他們只是感情好自娩,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布用踩。 她就那樣靜靜地躺著渠退,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脐彩。 梳的紋絲不亂的頭發(fā)上碎乃,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天,我揣著相機(jī)與錄音惠奸,去河邊找鬼梅誓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佛南,可吹牛的內(nèi)容都是我干的梗掰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嗅回,長吁一口氣:“原來是場噩夢啊……” “哼及穗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绵载,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拥坛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后尘分,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猜惋,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年培愁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霹粥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钉凌。...
    茶點(diǎn)故事閱讀 40,444評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拯刁,死狀恐怖窃肠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情私股,我是刑警寧澤摹察,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站倡鲸,受9級特大地震影響供嚎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峭状,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一克滴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧优床,春花似錦劝赔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杂伟。三九已至,卻和暖如春仍翰,著一層夾襖步出監(jiān)牢的瞬間赫粥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工歉备, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傅是,地道東北人匪燕。 一個(gè)月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓蕾羊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親帽驯。 傳聞我的和親對象是個(gè)殘疾皇子龟再,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容