<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      在vue2中,什么是雙向綁定,為什么vue3要進行優化?

      一、什么是雙向綁定

      我們先從單向綁定切入單向綁定非常簡單,就是把Model綁定到View,當我們用JavaScript代碼更新Model時,View就會自動更新雙向綁定就很容易聯想到了,在單向綁定的基礎上,用戶更新了ViewModel的數據也自動被更新了,這種情況就是雙向綁定舉個栗子

       當用戶填寫表單時,View的狀態就被更新了,如果此時可以自動更新Model的狀態,那就相當于我們把ModelView做了雙向綁定關系圖如下

      二、雙向綁定的原理是什么

      我們都知道 Vue 是數據雙向綁定的框架,雙向綁定由三個重要部分構成

      • 數據層(Model):應用的數據及業務邏輯
      • 視圖層(View):應用的展示效果,各類UI組件
      • 業務邏輯層(ViewModel):框架封裝的核心,它負責將數據與視圖關聯起來

      而上面的這個分層的架構方案,可以用一個專業術語進行稱呼:MVVM這里的控制層的核心功能便是 “數據雙向綁定” 。自然,我們只需弄懂它是什么,便可以進一步了解數據綁定的原理

      理解ViewModel

      它的主要職責就是:

      • 數據變化后更新視圖
      • 視圖變化后更新數據

      當然,它還有兩個主要部分組成

      • 監聽器(Observer):對所有數據的屬性進行監聽
      • 解析器(Compiler):對每個元素節點的指令進行掃描跟解析,根據指令模板替換數據,以及綁定相應的更新函數

      三、實現雙向綁定

      我們還是以Vue為例,先來看看Vue中的雙向綁定流程是什么的

      1. new Vue()首先執行初始化,對data執行響應化處理,這個過程發生Observe
      2. 同時對模板執行編譯,找到其中動態綁定的數據,從data中獲取并初始化視圖,這個過程發生在Compile
      3. 同時定義?個更新函數和Watcher,將來對應數據變化時Watcher會調用更新函數
      4. 由于data的某個key在?個視圖中可能出現多次,所以每個key都需要?個管家Dep來管理多個Watcher
      5. 將來data中數據?旦發生變化,會首先找到對應的Dep,通知所有Watcher執行更新函數

      流程圖如下:

      實現

      先來一個構造函數:執行初始化,對data執行響應化處理

      class Vue {  
        constructor(options) {  
          this.$options = options;  
          this.$data = options.data;  
              
          // 對data選項做響應式處理  
          observe(this.$data);  
              
          // 代理data到vm上  
          proxy(this);  
              
          // 執行編譯  
          new Compile(options.el, this);  
        }  
      }  

      data選項執行響應化具體操作

      function observe(obj) {  
        if (typeof obj !== "object" || obj == null) {  
          return;  
        }  
        new Observer(obj);  
      }  
        
      class Observer {  
        constructor(value) {  
          this.value = value;  
          this.walk(value);  
        }  
        walk(obj) {  
          Object.keys(obj).forEach((key) => {  
            defineReactive(obj, key, obj[key]);  
          });  
        }  
      }  

      編譯Compile

      對每個元素節點的指令進行掃描跟解析,根據指令模板替換數據,以及綁定相應的更新函數

      class Compile {  
        constructor(el, vm) {  
          this.$vm = vm;  
          this.$el = document.querySelector(el);  // 獲取dom  
          if (this.$el) {  
            this.compile(this.$el);  
          }  
        }  
        compile(el) {  
          const childNodes = el.childNodes;   
          Array.from(childNodes).forEach((node) => { // 遍歷子元素  
            if (this.isElement(node)) {   // 判斷是否為節點  
              console.log("編譯元素" + node.nodeName);  
            } else if (this.isInterpolation(node)) {  
              console.log("編譯插值?本" + node.textContent);  // 判斷是否為插值文本 {{}}  
            }  
            if (node.childNodes && node.childNodes.length > 0) {  // 判斷是否有子元素  
              this.compile(node);  // 對子元素進行遞歸遍歷  
            }  
          });  
        }  
        isElement(node) {  
          return node.nodeType == 1;  
        }  
        isInterpolation(node) {  
          return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);  
        }  
      }  

      依賴收集

      視圖中會用到data中某key,這稱為依賴。同?個key可能出現多次,每次都需要收集出來用?個Watcher來維護它們,此過程稱為依賴收集多個Watcher需要?個Dep來管理,需要更新時由Dep統?通知

      實現思路

      1. defineReactive時為每?個key創建?個Dep實例
      2. 初始化視圖時讀取某個key,例如name1,創建?個watcher1
      3. 由于觸發name1getter方法,便將watcher1添加到name1對應的Dep中
      4. name1更新,setter觸發時,便可通過對應Dep通知其管理所有Watcher更新
      // 負責更新視圖  
      class Watcher {  
        constructor(vm, key, updater) {  
          this.vm = vm  
          this.key = key  
          this.updaterFn = updater  
        
          // 創建實例時,把當前實例指定到Dep.target靜態屬性上  
          Dep.target = this  
          // 讀一下key,觸發get  
          vm[key]  
          // 置空  
          Dep.target = null  
        }  
        
        // 未來執行dom更新函數,由dep調用的  
        update() {  
          this.updaterFn.call(this.vm, this.vm[this.key])  
        }  
      }  

      聲明Dep

      class Dep {  
        constructor() {  
          this.deps = [];  // 依賴管理  
        }  
        addDep(dep) {  
          this.deps.push(dep);  
        }  
        notify() {   
          this.deps.forEach((dep) => dep.update());  
        }  
      }  

      創建watcher時觸發getter

      class Watcher {  
        constructor(vm, key, updateFn) {  
          Dep.target = this;  
          this.vm[this.key];  
          Dep.target = null;  
        }  
      }  

      依賴收集,創建Dep實例

      function defineReactive(obj, key, val) {  
        this.observe(val);  
        const dep = new Dep();  
        Object.defineProperty(obj, key, {  
          get() {  
            Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher實例  
            return val;  
          },  
          set(newVal) {  
            if (newVal === val) return;  
            dep.notify(); // 通知dep執行更新方法  
          },  
        });  
      }  

      四.vue3.0里為什么要用 Proxy API 替代 defineProperty API ?

      1、Object.defineProperty

      定義:Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,并返回此對象

      為什么能實現響應式

      通過defineProperty 兩個屬性,getset

      • get

      屬性的 getter 函數,當訪問該屬性時,會調用此函數。執行時不傳入任何參數,但是會傳入 this 對象(由于繼承關系,這里的this并不一定是定義該屬性的對象)。該函數的返回值會被用作屬性的值

      • set

      屬性的 setter 函數,當屬性值被修改時,會調用此函數。該方法接受一個參數(也就是被賦予的新值),會傳入賦值時的 this 對象。默認為 undefined

      下面通過代碼展示:

      定義一個響應式函數defineReactive

      function update() {
          app.innerText = obj.foo
      }
      
      function defineReactive(obj, key, val) {
          Object.defineProperty(obj, key, {
              get() {
                  console.log(`get ${key}:${val}`);
                  return val
              },
              set(newVal) {
                  if (newVal !== val) {
                      val = newVal
                      update()
                  }
              }
          })
      }

      調用defineReactive,數據發生變化觸發update方法,實現數據響應式

      const obj = {}
      defineReactive(obj, 'foo', '')
      setTimeout(()=>{
          obj.foo = new Date().toLocaleTimeString()
      },1000)

      在對象存在多個key情況下,需要進行遍歷

      function observe(obj) {
          if (typeof obj !== 'object' || obj == null) {
              return
          }
          Object.keys(obj).forEach(key => {
              defineReactive(obj, key, obj[key])
          })
      }

      如果存在嵌套對象的情況,還需要在defineReactive中進行遞歸

      function defineReactive(obj, key, val) {
          observe(val)
          Object.defineProperty(obj, key, {
              get() {
                  console.log(`get ${key}:${val}`);
                  return val
              },
              set(newVal) {
                  if (newVal !== val) {
                      val = newVal
                      update()
                  }
              }
          })
      }

      當給key賦值為對象的時候,還需要在set屬性中進行遞歸

      set(newVal) {
          if (newVal !== val) {
              observe(newVal) // 新值是對象的情況
              notifyUpdate()
          }
      }

      上述例子能夠實現對一個對象的基本響應式,但仍然存在諸多問題

      現在對一個對象進行刪除與添加屬性操作,無法劫持到

      const obj = {
          foo: "foo",
          bar: "bar"
      }
      observe(obj)
      delete obj.foo // no ok
      obj.jar = 'xxx' // no ok

      當我們對一個數組進行監聽的時候,并不那么好使了

      const arrData = [1,2,3,4,5];
      arrData.forEach((val,index)=>{
          defineProperty(arrData,index,val)
      })
      arrData.push() // no ok
      arrData.pop()  // no ok
      arrDate[0] = 99 // ok

      可以看到數據的api無法劫持到,從而無法實現數據響應式,

      所以在Vue2中,增加了setdelete API,并且對數組api方法進行一個重寫

      還有一個問題則是,如果存在深層的嵌套對象關系,需要深層的進行監聽,造成了性能的極大問題

      小結

      • 檢測不到對象屬性的添加和刪除
      • 數組API方法無法監聽到
      • 需要對每個屬性進行遍歷監聽,如果嵌套對象,需要深層監聽,造成性能問題

      2、proxy

      Proxy的監聽是針對一個對象的,那么對這個對象的所有操作會進入監聽操作,這就完全可以代理所有屬性了

      ES6系列中,我們詳細講解過Proxy的使用,就不再述說了

      下面通過代碼進行展示:

      定義一個響應式方法reactive

      function reactive(obj) {
          if (typeof obj !== 'object' && obj != null) {
              return obj
          }
          // Proxy相當于在對象外層加攔截
          const observed = new Proxy(obj, {
              get(target, key, receiver) {
                  const res = Reflect.get(target, key, receiver)
                  console.log(`獲取${key}:${res}`)
                  return res
              },
              set(target, key, value, receiver) {
                  const res = Reflect.set(target, key, value, receiver)
                  console.log(`設置${key}:${value}`)
                  return res
              },
              deleteProperty(target, key) {
                  const res = Reflect.deleteProperty(target, key)
                  console.log(`刪除${key}:${res}`)
                  return res
              }
          })
          return observed
      }

      測試一下簡單數據的操作,發現都能劫持

      const state = reactive({
          foo: 'foo'
      })
      // 1.獲取
      state.foo // ok
      // 2.設置已存在屬性
      state.foo = 'fooooooo' // ok
      // 3.設置不存在屬性
      state.dong = 'dong' // ok
      // 4.刪除屬性
      delete state.dong // ok

      再測試嵌套對象情況,這時候發現就不那么 OK 了

      const state = reactive({
          bar: { a: 1 }
      })
      
      // 設置嵌套對象屬性
      state.bar.a = 10 // no ok

      如果要解決,需要在get之上再進行一層代理

      function reactive(obj) {
          if (typeof obj !== 'object' && obj != null) {
              return obj
          }
          // Proxy相當于在對象外層加攔截
          const observed = new Proxy(obj, {
              get(target, key, receiver) {
                  const res = Reflect.get(target, key, receiver)
                  console.log(`獲取${key}:${res}`)
                  return isObject(res) ? reactive(res) : res
              },
          return observed
      }

      3、總結

      Object.defineProperty只能遍歷對象屬性進行劫持

      function observe(obj) {
          if (typeof obj !== 'object' || obj == null) {
              return
          }
          Object.keys(obj).forEach(key => {
              defineReactive(obj, key, obj[key])
          })
      }

      Proxy直接可以劫持整個對象,并返回一個新對象,我們可以只操作新的對象達到響應式目的

      function reactive(obj) {
          if (typeof obj !== 'object' && obj != null) {
              return obj
          }
          // Proxy相當于在對象外層加攔截
          const observed = new Proxy(obj, {
              get(target, key, receiver) {
                  const res = Reflect.get(target, key, receiver)
                  console.log(`獲取${key}:${res}`)
                  return res
              },
              set(target, key, value, receiver) {
                  const res = Reflect.set(target, key, value, receiver)
                  console.log(`設置${key}:${value}`)
                  return res
              },
              deleteProperty(target, key) {
                  const res = Reflect.deleteProperty(target, key)
                  console.log(`刪除${key}:${res}`)
                  return res
              }
          })
          return observed
      }

      Proxy可以直接監聽數組的變化(pushshiftsplice

      const obj = [1,2,3]
      const proxtObj = reactive(obj)
      obj.psuh(4) // ok

      Proxy有多達13種攔截方法,不限于applyownKeysdeletePropertyhas等等,這是Object.defineProperty不具備的

      正因為defineProperty自身的缺陷,導致Vue2在實現響應式過程需要實現其他的方法輔助(如重寫數組方法、增加額外setdelete方法)

      // 數組重寫
      const originalProto = Array.prototype
      const arrayProto = Object.create(originalProto)
      ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(method => {
        arrayProto[method] = function () {
          originalProto[method].apply(this.arguments)
          dep.notice()
        }
      });
      
      // set、delete
      Vue.set(obj,'bar','newbar')
      Vue.delete(obj),'bar')

      Proxy 不兼容IE,也沒有 polyfilldefineProperty 能支持到IE9

      如果對您有所幫助,歡迎您點個關注,我會定時更新技術文檔,大家一起討論學習,一起進步。

       

      posted @ 2024-04-29 18:03  林恒  閱讀(311)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 中文字幕精品无码一区二区| 伊人久久大香线蕉综合影院| 玛多县| 视频免费完整版在线播放| 色爱综合另类图片av| 先锋影音男人av资源| 亚洲精品久久国产高清小说| 成人无码午夜在线观看| 中文字幕午夜福利片午夜福利片97| 国产不卡av一区二区| 亚洲av伊人久久综合性色| 思思热在线视频精品| 亚洲精品成a人在线观看| 男女爽爽无遮挡午夜视频| 人妻色综合网站| 亚洲精品午夜精品| 亚洲欧美日韩成人综合一区| 巴彦县| 日本精品极品视频在线| 亚洲国产精品午夜福利| 国产精品午夜无码AV天美传媒| 国产成人午夜精品福利| 绝顶丰满少妇av无码| 中文字幕在线看视频一区二区三区 | 国产一区二三区日韩精品| 亚洲av无码片在线播放| 亚洲少妇一区二区三区老| 日韩国产成人精品视频| 亚洲精品一区二区麻豆| 国产精品国产三级国产专i| 成人国产精品中文字幕| 国产精品成人高潮av| 久久综合97丁香色香蕉 | 日韩熟女乱综合一区二区| 最近免费中文字幕大全| 久久久久久久久18禁秘| 九九热在线免费视频播放| 天天摸天天碰天天添| 国产偷国产偷亚洲高清午夜| 亚洲精品男男一区二区| 亚洲精品蜜桃久久久久久|