通言:以前都是看网上别人的关于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


阳哥
14 声望0 粉丝

一个code爱好者,一个户外运动的爱好者,一个喜欢音乐的爱好者。