<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          vue2和vue3的數(shù)據(jù)綁定原理

          共 3450字,需瀏覽 7分鐘

           ·

          2021-03-25 15:07


          作者:yingmhd

          來源:SegmentFault 思否社區(qū)



          VUE 雙向綁定原理

          首先Vue是個類

          new Vue({
          data(){},
          methods: {}
          })

          實例化Vue的時候要傳入dataVue類內(nèi)部對data進行劫持轉(zhuǎn)換成getter/setter,如何劫持

          vue2 數(shù)據(jù)劫持



          核心方法: Object.defineProperty

          H5方法,所以不兼容IE8以下

          let obj = {},
          value = 1
          Object.defineProperty(obj,'a',{
          get() {
          console.log('這里監(jiān)聽到了數(shù)據(jù)獲取')
          return value
          },
          set(newValue, value) {
          if(newValue !== value) {
          value = newValue
          console.log('這里監(jiān)聽到了數(shù)據(jù)更改')
          }
          }
          })
          console.log(obj.a) // 這里監(jiān)聽到了數(shù)據(jù)獲取 1
          obj.a = 2 // 這里監(jiān)聽到了數(shù)據(jù)更改

          所以再初始化Vue時,對data進行了劫持,每個屬性都通過Object.defineProperty變成了getter/setter,一旦數(shù)據(jù)發(fā)生改變,就會觸發(fā)set,然后去更新view

          let data = {
          name: 'nike',
          info: {
          age: 21
          }
          }
          Object.keys(data).forEach(key=>{
          defineProperty(data, key, data[key])
          })
          function defineProperty(target, key, value) {
          Object.defineProperty(target,key,{
          get() {
          console.log('這里監(jiān)聽到了數(shù)據(jù)獲取')
          return value
          },
          set(newValue, value) {
          if(newValue !== value) {
          value = newValue
          console.log('這里監(jiān)聽到了數(shù)據(jù)更改')
          }
          }
          })
          }
          data.name = 'tom' // 這里監(jiān)聽到了數(shù)據(jù)更改
          data.info.age = 22 // 這里監(jiān)聽到了數(shù)據(jù)獲取(這里沒有觸發(fā)更改,get和set相對立,總要觸發(fā)一個)
          data.info = {age:22} // 這里監(jiān)聽到了數(shù)據(jù)更改

          至于data.info.age = 22為什么沒有觸發(fā)set呢,因為上面的邏輯僅僅是對data下面的一層進行了劫持,而再往下的改變是監(jiān)聽不到的,所以就引出了兩外一個東西

          1. Watch
            watch: {
            info: {
            handler(){},
            deep: true
            }
            }
            此處的deep表示深度監(jiān)聽,這樣就會對該屬性遞歸遍歷并逐一劫持,類似于深拷貝
          2. vue.$set
            從字面意思看,就是手動觸發(fā)
            set

          Object.defineProperty有一個bug,就是無法監(jiān)聽數(shù)組(因為數(shù)組沒key

          let data = {
          name: [],
          }
          Object.keys(data).forEach(key=>{
          defineProperty(data, key, data[key])
          })
          function defineProperty(target, key, value) {
          Object.defineProperty(target,key,{
          get() {
          console.log('這里監(jiān)聽到了數(shù)據(jù)獲取')
          return value
          },
          set(newValue, value) {
          if(newValue !== value) {
          value = newValue
          console.log('這里監(jiān)聽到了數(shù)據(jù)更改')
          }
          }
          })
          }
          data.name.push('nike') // 這里監(jiān)聽到了數(shù)據(jù)獲取

          為了解決這個問題,Vue對數(shù)組的方法進行了重寫

          // 重寫push
          let oldPush = Array.prototype.push
          Array.prototype.push = function() {
          console.log('這里觸發(fā)view更新')
          oldPush.call(this,...arguments)
          }

          vue3 數(shù)據(jù)劫持



          很明顯,Object.defineProperty有一些缺陷,不僅要遍歷data逐個劫持,還不能監(jiān)聽到數(shù)組的改變,所以VUE3使用了ES6Proxy
          Proxy字面理解代理,就跟經(jīng)紀人一樣,一旦與某個明星data綁定,那么這個明星想干嘛就得先通過代理

          let data = {
          msg: {
          a: 10
          },
          arr: [1, 2, 3]
          }
          let handler = {
          get(target, key) {
          // 懶監(jiān)聽,去獲取的時候才監(jiān)聽對象里面的對象,而不是直接遞歸循環(huán)監(jiān)聽
          console.log('獲取key: ' + key)
          if (typeof target[key] === 'object' && target[key] !== null) {
          return new Proxy(target[key], handler)
          }
          return Reflect.get(target, key)
          },
          set(target, key, value) {
          let oldValue = target[key]
          console.log('更新key: ' + key)
          if (oldValue !== value) {
          // 通知view更新
          }
          return Reflect.set(target, key, value)
          }
          }
          let proxy = new Proxy(data, handler)
          proxy.arr.push(4)

          輸出結(jié)果

          為什么每次都有length,其實Proxy的監(jiān)聽數(shù)組實現(xiàn)是把數(shù)組變成了一個類數(shù)組對象而已

          let arr = {
          '0': 1,
          '1': 2,
          length: 2
          }

          Proxy除了get,set還有deleteProperty/apply/getOwnPropertyDescriptor等等12個方法,恰好與Reflect對應,所以在這些方法里面可以實現(xiàn)攔截器

          set(target, key, value) {
          if(key[0] === '_') {
          throw new Error('這是私有變量,不能更改')
          }
          return Reflect.set(target, key, value)
          }




          點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,掃描下方”二維碼“或在“公眾號后臺回復“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 23
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.欧美污 | 日逼逼视频| 内射影视| 国内精品久久久久久久星 | 特级毛片内射 |