Skip to content

vue 响应式原理

此代码是手写了 vue 如何实现响应式的,并且封装了 vue 的逻辑

代码

html
<!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>

      <input type="text" onclick="user.name=this.value" />
    </div>
    <!-- <input type="text" oninput="user.name = this.value" />
  <input type="date" onchange="user.birth = this.value" /> -->
    <script src="./vue.js"></script>
    <script src="./index.js"></script>
  </body>
</html>
css
.card {
  width: 300px;
  border: 2px solid rgb(74, 125, 142);
  border-radius: 10px;
  font-size: 2em;
  padding: 0 20px;
  margin: 0 auto;
  background: lightblue;
  color: #333;
}
js
var user = {
  name: "袁进",
  birth: "2002-5-7",
};

observe(user); // 观察/监听整个对象

/**更新DOM的操作 */
// 显示姓氏
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;
}

//记录更新DOM的操作给window,方便getter监听
autoRun(showFirstName);
autoRun(showLastName);
autoRun(showAge);
/* 
步骤梳理:
当数据修改之后,user对象相当于修改了,但是视图肯定是要进行同步的。
那么同步视图的时候,一般都是需要获取DOM,然后更新,获取dom的时候就会访问到新修改的数据,就会触发getter
触发getter之后,就需要让getter记录一下这个更新DOM操作的函数,然后在setter中执行这些更新Dom的函数,从而就实现了响应式。
*/

user.name = "Yan";
// showFirstName();
// showLastName();
// showAge();
js
/**
 * 观察者--观察/监听某个对象的属性
 * @param {Object} obj
 */
function observe(obj) {
  Object.keys(obj).forEach((key) => {
    let intervalValue = obj[key];
    const funcs = []; //记录访问属性的函数
    Object.defineProperty(obj, key, {
      get() {
        //--依赖搜集:把更新DOM的函数记录下来  记录:哪个函数在用我,即在更新DOM的函数中访问了该属性

        //该函数如果内部访问两次某个属性,应该只执行一次该函数
        if (window.__func && !funcs.includes(window.__func)) {
          funcs.push(window.__func);
        }

        return intervalValue;
      },
      set(val) {
        intervalValue = val;
        //--派发更新:执行由getter记录下的更新DOM的函数    运行:执行用我的函数,一般为更新DOM
        funcs.forEach((func) => {
          func();
        });
      },
    });
  });
}

/**
 * 自动执行该函数--记录更新DOM的函数到window,方便getter中记录
 * @param {Function} fn
 */
function autoRun(fn) {
  window.__func = fn;
  fn();
  window.__func = null;
}

MIT License