Vue 双向绑定实现

vue2通过 Object.defineProperty
vue3 通过 Proxy
核心代码Vue2
/**
 * 观察某个对象的所有属性
 * @param {Object} obj
 */
function observe(obj) {
  for (const key in obj) {
    // 循环为每一个对象属性添加get set 方法
    let internalValue = obj[key];
    let funcs = [];
    Object.defineProperty(obj, key, {
      get: function () {
        //  依赖收集,记录:是哪个函数在用我
        if (window.__func && !funcs.includes(window.__func)) {
          funcs.push(window.__func);
        }
        return internalValue;
      },
      set: function (val) {
        internalValue = val;
        // 派发更新,运行:执行用我的函数
        for (var i = 0; i < funcs.length; i++) {
          funcs[i]();
        }
      },
    });
  }
}

function autorun(fn) {
  window.__func = fn;
  fn();
  window.__func = null;
}
Vue3
/**
 * 观察某个对象的所有属性
 * @param {Object} obj
 */
function observe(obj) {
  let funcs = {};
  return new Proxy(obj, {
    get: function (obj, key) {
      //  依赖收集,记录:是哪个函数在用我
      !funcs[key] && (funcs[key] = []);
      if (window.__func && !funcs[key].includes(window.__func)) {
        funcs[key].push(window.__func);
      }
      return obj[key];
    },
    set: function (obj, key, val) {
      obj[key] = val;
      // 派发更新,运行:执行用我的函数
      for (var i = 0; i < funcs[key].length; i++) {
        funcs[key][i]();
      }
      return obj[key];
    },
  });
}

function autorun(fn) {
  window.__func = fn;
  fn();
  window.__func = null;
}
完整例子
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <div class="card">
      <p id="firstName"></p>
      <p id="lastName"></p>
      <p id="age"></p>
    </div>
    <input type="text" oninput="user.name = this.value" />
    <input type="date" onchange="user.birth = this.value" />
    <script src="./euv.js"></script>
    <script src="./index.js"></script>
  </body>
</html>
var user = {
  name: '克劳德',
  birth: '1997-5-25',
};

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);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容