Stories

Detail Return Return

vue數據響應式的原理(通俗易懂)附源碼 - Stories Detail

通言:以前都是看網上別人的關於vue數據響應式原理理解,都是長篇大論的,不是很好理解,不能有效概括。直到學習了某位老師的課程我恍然大悟。得出結論:數據響應式就是指數據的改變以後通知函數的執行。
講一下實現的邏輯過程:
首先
js代碼:

var user = {
  name: '合約路',
  birth: '2002-5-7',
};
// 顯示姓氏
function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
}

// 顯示名字
function showLastName() {
  document.querySelector('#lastName').textContent = '名:' + user.name.slice(1);
}

// 顯示年齡
function showAge() {
  var birthday = new Date(user.birth);
  var today = new Date();
  today.setHours(0), today.setMinutes(0), today.setMilliseconds(0);
  thisYearBirthday = new Date(
    today.getFullYear(),
    birthday.getMonth(),
    birthday.getDate()
  );
  var age = today.getFullYear() - birthday.getFullYear();
  if (today.getTime() < thisYearBirthday.getTime()) {
    age--;
  }
  document.querySelector('#age').textContent = '年齡:' + age;
}
showFirstName();
showLastName();
showAge();

-------------截至-------------
//上面函數執行完形成初始頁面

user.name = 'sag'; //不能做到直接更新頁面


showFirstName();//依賴函數
showLastName();//依賴函數

上面代碼我們可以看出,傳統模式下改變數據不能直接更新頁面,而調用下面方法才能讓頁面進行重新渲染。數據的更新依賴於函數的執行,這裏我們稱上面兩個調用的函數為依賴函數。
其實這樣寫不好,邏輯看起來太分散(不利於維護)。

搞明白這個問題其實就明白了數據響應式的原理了。

方法:通過Object.defineProperty對數據進行劫持。下面以name為例來進行數據劫持。

var internalValue = obj.name;
Object.defineProperty(obj,'name',{
      get(){//訪問該屬性應該返回的值
        return internalValue
      },
      set(v){
        //這裏可以理解為設置數據以後的回調函數。
        internalValue = v;
        showFirstName();
        showLastName();
      }
})

這樣寫看起來還是不好,我一旦對象屬性多起來了,難道我要寫100個這個玩意麼?
再優化,我設置一個監聽函數,遍歷對象下所有屬性然後添加

function observe(obj){
  for(const key in obj){
    let internalValue = obj[key];
    let funcs = []; //用於依賴函數存儲
    Object.defineProperty(obj,key,{
      get(){
        //abc表示對應的事件進行存儲
        funcs.push(abc); // 每次定義的時候將受依賴函數放到數組中
        return internalValue
      },
      set(v){
        internalValue = v;
        for (var i = 0; i < funcs.length; i++) {
          funcs[i]();//挨個執行
        }
      }
    })
  }
}

上述一個問題就出現了,我去哪裏以及什麼時機拿到函數名字來執行呢?

//自己註冊對應函數,存儲到window全局變量下,並初始化執行,最後重置這個屬性。
function autorun(fn){
  window.__func = fn;//註冊
  fn(); //觸發對應函數
  window.__func = null;
}
//設此時fn = showFirstName

function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
//user.name 會觸發它對應的object.defineProperty() GET函數。
}

    get(){
        //user.name表示對應的事件進行存儲
        funcs.push(“showFirstName”);
      },

以此得到完整代碼

/**
observe.js
 * 觀察某個對象的所有屬性
 * @param {Object} obj
 */
function observe(obj){
  for(const key in obj){
    let internalValue = obj[key];
    let funcs = []; //用於函數存儲
    Object.defineProperty(obj,key,{
      get(){
        //  依賴收集,記錄:是哪個函數在用我
        if (window.__func && !funcs.includes(window.__func)) {
          funcs.push(window.__func);
        }
        return internalValue
      },
      set(v){
        internalValue = v;
        // 派發更新,運行:執行用我的函數
        for (var i = 0; i < funcs.length; i++) {
          funcs[i]();
        }
      }
    })
  }
}
function autorun(fn){
  window.__func = fn;
  fn();
  window.__func = null;
}

//入口函數
var user = {
  name: '合約路',
  birth: '2002-5-7',
};
observe(user); // 觀察
// 顯示姓氏
function showFirstName() {
  document.querySelector('#firstName').textContent = '姓:' + user.name[0];
}

// 顯示名字
function showLastName() {
  document.querySelector('#lastName').textContent = '名:' + user.name.slice(1);
}

// 顯示年齡
function showAge() {
  var birthday = new Date(user.birth);
  var today = new Date();
  today.setHours(0), today.setMinutes(0), today.setMilliseconds(0);
  thisYearBirthday = new Date(
    today.getFullYear(),
    birthday.getMonth(),
    birthday.getDate()
  );
  var age = today.getFullYear() - birthday.getFullYear();
  if (today.getTime() < thisYearBirthday.getTime()) {
    age--;
  }
  document.querySelector('#age').textContent = '年齡:' + age;
}
autorun(showFirstName);
autorun(showLastName);
autorun(showAge);

額外的 雙向綁定原理

<input type="text" oninput="user.name = this.value" />
<input type="date" onchange="user.birth = this.value" />
//對於表單元素來講 數據的改變也會導致視圖更新。

完結,下一章講一講vue3.0數據響應式方面的乾貨
項目完整地址:
https://github.com/xuxiaoyang883/vue_data_reactive

user avatar hard_heart_603dd717240e2 Avatar nznznz Avatar 54r9rxzy Avatar b_a_r_a_n Avatar happy2332333 Avatar cynthia_59675eba1a2ee Avatar hu_qi Avatar tonyyoung Avatar 79px Avatar wojiaocuisite Avatar dengzhanyong Avatar minnanitkong Avatar
Favorites 51 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.