【Vue】手写响应式原理(依赖收集和依赖更新)

html 复制代码
<!-- index.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>
  </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>
css 复制代码
/* index.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 复制代码
// index.js
var user = {
  name: '张三',
  birth: '2002-11-26',
};

// 收集依赖变化
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;
}

// user.name = '李四'
// 如果我们想要当属性改变时,页面也会跟着自动改变,那么需要自动调用依赖该函数属性的函数(也就是显示姓,显示名的函数)
// 可以想到 Object.defineProperty() 可以实现这个功能
// const internalValue = user.name
// Object.defineProperty(user, 'name', {
//   get: function() {
//     console.log('name 属性被读取')
//     return internalValue
//   },
//   set: function(newValue) {
//     internalValue = newValue;
//     console.log('name 属性被修改')
//     showFirstName();
//     showLastName();
//   },
// })

autorun(showFirstName);
autorun(showLastName);
autorun(showAge);
js 复制代码
// euv.js
/**
 * 观察某个对象的所有属性,改变数据的函数
 * @param {Object} obj
 */
function observe(obj) {
  for (const key in obj) {
    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]();
        }
      },
    });
  }
}
// 将需要运行的函数全部传给 autorun,渲染数据的函数
function autorun(fn) {
  window.__func = fn;
  fn();
  window.__func = null;
}
相关推荐
天天喝旺仔1 分钟前
浏览器渲染原理:从解析 HTML/CSS、构建渲染树到重排重绘与首屏优化
前端·javascript·css·性能优化·html
Days20501 小时前
时间胶囊项目正式发布
javascript·gpt·php
朱 欢 庆1 小时前
el-table-v2 虚拟表格组件
前端·javascript·vue.js
OpsEye1 小时前
企业大模型用量成本该如何监控和管控?
javascript·ai编程
杨利杰YJlio1 小时前
ITSK 万能驱动 26V5:新驱动批量更新
前端·javascript·后端
y = xⁿ2 小时前
关于Agent智能体开发
开发语言·前端·javascript
事圆则缓2 小时前
Flutter 状态管理框架对比(一):先看地图,再选工具
前端·javascript·flutter
IMPYLH2 小时前
HTML 的 <tfoot> 元素
前端·javascript·html
鬼手点金2 小时前
Claude Code示范案例-修复 Bug 工作流
java·服务器·前端·javascript·bug·openclaw
paopaokaka_luck3 小时前
个人综合管理小程序(echarts数据看板、健康数据记录与趋势查看、目标拆解和进度管理、日程待办与番茄钟、习惯打卡、收支统计、个人生活记录、跨模块智能提醒)
前端·javascript·spring·小程序·数据分析·echarts·生活