淺談vue響應式原理及發布訂閱模式和觀察者模式

一.Vue響應式原理

首先要了解幾個概念:

數據響應式:數據模型僅僅是普通的Javascript對象,而我們修改數據時,視圖會進行更新,避免了繁瑣的DOM操作,提高開發效率。

雙向綁定:數據改變,視圖改變,數據也隨之改變,我們可以使用v-model在表單上創建雙向數據綁定。

數據驅動是Vue最獨特的特性之一:開發過程中僅需要關注數據本身,不需要關心數據是如何渲染到視圖。

vue2.X中的響應式原理是基於defineProperty,兼容IE8以上版本,核心原理程式碼如下:

              let data={
         msg:'hello',
         count:10
          }
        let vm={}
        proxyData(data)
        function proxyData(data){
            Object.keys(data).forEach(key=>{
                Object.defineProperty(vm,key,{
                    enumerable:true,
                    configurable:true,
                    writeable:true,
                    //獲取值的時候執行
                    get(){
                        console.log('get:',key,data[key])
                        return data[key]
                    },
                    //設置值的時候執行
                    set(newValue){
                        data[key]=newValue
                        console.log('set:',key,newValue)
                        document.querySelector('#app').textContent=data[key]
                    }
                })
            })
        }
         vm.msg //獲取(get方法) hello
         vm.msg='hello World' //設置新屬性值並渲染到頁面(set方法)
         vm.msg //hello World  

vue3.X中的響應式原理是基於Proxy,直接監聽對象,而非屬性,ES6中新增,IE不支援,性能由瀏覽器優化,性能比defineProperty要好,程式碼的話相比較defineProperty要簡潔一些,對於多個屬性的值不需要進行循環遍歷處理。

                let data={
            msg:'hello',
            count:0
        }
        
        //模擬 Vue 實例
        let vm=new Proxy(data,{
            //執行代理行為的函數
            //當訪問 vm 的成員會執行
            get(target,key){
                console.log('get,key:',key,target[key])
                return target[key]
            },
            set(target,key,newValue){
                console.log('set,key:',key,newValue)
                if(target[key] === newValue){
                    return 
                }
                target[key]=newValue
                document.querySelector("#app").textContent=target[key]
            }
        })
        //測試
        vm.msg='Hello World'
        console.log(vm.msg)

二.發布訂閱模式和觀察者模式

1.發布/訂閱模式
這個概念有些抽象,下面舉個例子說明下,家長比較關心孩子成績,天天問孩子成績出來沒,假設可以到孩子所在班級去訂閱孩子成績,一旦考試成績出來,相當於觸發了一個事件,最後有班級的老師以簡訊的形式通知給家長,

不需要天天問孩子成績出來沒,家長就是事件的訂閱者,老師是事件的發布者,孩子所在的班級可以假想成一個事件的中心。vue中的自定義事件都是基於發布/訂閱模式的。下面模擬下發布訂閱模式的運行機制:

//事件觸發器
        class EventEmitter(){
            constructor(){
                // 初始化對象{ 'click':[fn1,fn2],'change':[fn] }
                this.subs=Object.create(null)
            }
            //註冊事件
            $on(eventType,handler){
                this.subs[eventType] = this.subs[eventType] || []
                this.subs[eventType].push(handler)
            }
            //觸發事件
            $emit(eventType){
                if(this.subs[eventType]){
                    this.subs[eventType].forEach(handler => {
                        handler()
                    })
                }
            }
        }
        //測試
        
        let em =new EventEmitter()
        
        em.$on('click',()=>{
            console.log('click1')
        })
        em.$on('click',()=>{
            console.log('click2')
        })
        em.$emit('click') //列印結果 click1,click2

二.觀察者模式

觀察者模式和訂閱模式的區別是沒有事件中心,只有發布者和訂閱者,並且發布者需要知道訂閱者的存在.

概念:

觀察者 –Watcher

update():當事件發生時,具體要做的事情。

發布者 –Dep

subs數組:存儲所有的觀察者

addSub():添加觀察者

notify():當事件發生,調用所有觀察者的update()方法

//發布者-目標
        class Dep{
            constructor() {
                //記錄所有的訂閱者
                this.subs=[]
            }
            //添加訂閱者
            addsub(sub){
                if(sub && sub.update){
                    this.subs.push(sub)
                }
            }
            //發布通知
            notify(){
                this.subs.forEach(sub=>{
                    sub.update()
                })
            }
        }
        //訂閱者-觀察者
        class Watcher{
            update(){
                console.log('update')
            }
        }
        //測試
        let dep=new Dep()
        let watcher=new Watcher()
        dep.addsub(watcher)
        dep.notify() //列印結果 update

總結:

觀察者模式是由具體目標調度,比如當事件觸發,Dep就會去調用觀察者的方法,所以觀察者的訂閱者和發布者之間是存在依賴的。

發布訂閱模式由統一調度中心調用,因此發布者和訂閱者不需要知道對方的存在。