vue的數據劫持以及操做數組的坑

TL;DR

  • 給data添加新屬性的時候vm.$set(vm.info,'newKey','newValue')
  • data上面屬性值是數組的時候,須要用數組的方法操做數組,而不能經過index或者length屬性去操做數組,由於監聽不到屬性操做的動做。

安裝和初使用vue

vue是構建用戶界面的漸進式框架。所謂的漸進式:vue + components + vue-router + vuex + vue-cli能夠根據須要選擇相應的功能。javascript

來串命令mkdir vue-apply;cd vue-apply;npm init -y;npm i vuehtml

來一個html文件以下,瀏覽器瞄下~,瀏覽器控制檯vm.msg=0再看下vue

<div id="app">{{msg}}</div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script> let vm = new Vue({ el:'#app', // template加上以後會替換掉#app這個標籤 // template:'<h1>en</h1>', data:{msg:'msg'} }) vm.msg = 'msg' </script>
複製代碼

說說mvvm mvc

mvc實際上是model view Model傳統全部邏輯在controller,難以維護。用戶輸入 => 控制器 => 數據改變,若是數據變了須要獲取dom,操做屬性,再渲染到視圖上。
mvvm實際上是model view viewModel數據變化驅動視圖。數據變了,不須要你獲取dom,而後改變dom的內容。這邊數據變了,vm負責監聽,視圖那邊自動發生變化。最明顯的是不須要document.querySelector之類的了。java

vm的實質

上面說了vm負責讓數據變了,視圖能自動發生變化。這麼神奇魔術背後的原理是Object.defineProperty。其實就是屬性的讀取和設置操做都進行了監聽,當有這樣的操做的時候,進行某種動做。來一個demo玩下。node

// 對obj上面的屬性進行讀取和設置監聽
let obj = {
        name:'huahua',
        age:18
    }
    function observer(obj){
        if(typeof obj === 'object'){
            for (const key in obj) {
                defineReactive(obj,key,obj[key])
            }
        }
    }
    // get的return的值纔是最終你讀取到的值。因此設的值是爲讀取準備的。
    // set傳的參數是設置的值,注意這裏不要有obj.name = newVal 這樣又觸發set監聽,會死循環的。
    function defineReactive(obj,key,value){
        Object.defineProperty(obj,key,{
            get:function(){
                console.log('你在讀取')
                // happy的話這邊能夠value++,這樣你發現讀取的值始終比設置的大一個,由於return就是讀取到的值
                return value
            },
            set:function(newVal){
                console.log('數據更新了')
                value = newVal
            }

        })
    }
    observer(obj)
    obj.age = 2
    console.log(obj.age)

複製代碼

在瀏覽器執行的時候,控制檯隨手也能夠obj.name="hua1"相似的操做,發現都監聽到了。可是若是更深一步,obj.name={firstname:'hua',lastname:'piaoliang'};obj.name.lastname='o'就不能監聽到屬性修改了。由於並無將新的賦值對象監聽其屬性。因此函數須要改進。 須要在defineReactive的第一行加上observer(value)。設置值的時候若是是對象的話,也須要將這個對象數據劫持。同理,set那邊也須要加這行。vue-router

function defineReactive(obj,key,value){
       // 注意這裏!!!!!!!
       observer(value)
        Object.defineProperty(obj,key,{
            get:function(){
                return value
            },
            set:function(newVal){
                // 注意這裏!!!!!!!
                observer(newVal)
                console.log('數據更新了')
                value = newVal
            }

        })
    }

複製代碼

繼續,若是obj.name=[1,2,3];obj.name.push(4)發現又沒有通知了,這是由於Object.defineProperty不支持監聽數組變化。因此須要重寫數組上面的方法。話說,最近看了個文章,理論上也能夠監聽數組,可是性能消耗和收益不成正比,因此,vue就沒去實現了。vuex

// 把數組上大部分方法重寫了,這裏不一一列舉。可是若是你 [1,2].length--,這是捕捉不到的
    let arr = ['push','slice','split']
    arr.forEach(method=>{
        let oldPush = Array.property[method]
        Array.property[method] = function(value){
            console.log('數據更新')
            oldPush.call(this,value)
        }
    })
複製代碼

vue使用的時候注意的坑

正如上面的解釋,vue2.0的底層約莫是這個邏輯,因此使用須要注意的點:vue-cli

  • 由於是一開始就數據劫持了。因此後來若是新綁定屬性,是沒有數據劫持的。若是須要調用 vm.$set(vm.info,'newKey','newValue'),vm是vue的實例。npm

  • 當屬性值是數組,數組變化的時候,跟蹤不到變化。由於數組雖然是對象,可是Object.defineProperty不支持數組,因此vue改寫了數組的全部方法,當調用數組方法的時候,就調動變更事件。可是不能經過屬性或者索引控制數組,好比length,index。數組

  • 總結:data上,綁定全部屬性避免後期加新屬性。若是是數組,只能經過數組方法修改數組。以下例子,控制檯vm.arr--發現視圖並不會變化,vm.arr.push(4)就能變化

<div id="app">{{msg}}{{arr}}</div>
    <script src="node_modules/vue/dist/vue.js"></script>
    <script> let vm = new Vue({ el:'#app', // template加上以後會替換掉#app這個標籤 // template:'<h1>en</h1>', data:{msg:'msg',arr:[1,2,3]} }) vm.msg = 'msg' </script>
複製代碼
相關文章
相關標籤/搜索