数据响应式: 当数据发生改变的时候, 会自动重新运行依赖该数据的函数
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<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">
<div class="wrap">
<p id="firstname">姓:</p>
<p id="lastname">名:</p>
<p id="age">年龄:</p>
</div>
<div class="ipt">
<input type="text" onchange="user.name = this.value" placeholder="请输入姓名" />
<input type="date" onchange="user.birth = this.value" placeholder="选择出生日期" />
</div>
</div>
<script src="./euv.js"></script>
<script src="./index.js"></script>
</body>
</html>
index.css
* {
margin: 0;
padding: 0;
}
input {
outline: none;
padding: 5px;
border-radius: 5px;
border: none;
}
.card {
margin: 30px auto;
padding: 20px;
width: 350px;
background-color: #1989fa;
color: #fff;
border-radius: 10px;
}
.card p {
line-height: 2;
}
.card .ipt {
margin-top: 10px;
}
index.js
let user = {
name: '张三',
birth: '2000-01-01',
}
// 观察器
observe(user)
function showFirstName() {
const firstDom = document.querySelector('#firstname')
firstDom.textContent = '姓: ' + user.name[0]
}
function showLastName() {
const lastDom = document.querySelector('#lastname')
lastDom.textContent = '名: ' + user.name.slice(1)
}
function showAge() {
const ageDom = document.querySelector('#age')
const now = new Date().getFullYear()
const diff = now - new Date(user.birth).getFullYear()
ageDom.textContent = '年龄: ' + diff
}
// 该函数会将依赖属性的函数 传递给观察器的依赖收集
autorun(showFirstName)
autorun(showLastName)
autorun(showAge)
// user.name = '李四'
// 问题: 当数据发生改变时界面不会自动更新?
// - 需要重新调用所依赖的函数
// showFirstName()
// showLastName()
// user.birth = '2020-01-01'
// 问题: 当数据发生改变时界面不会自动更新?
// - 需要重新调用所依赖的函数
// showAge()
// Object.defineProperty() 对数据进行劫持
// Vue 数据发生改变时自动调用依赖该属性的函数
// let internalName = user.name
// Object.defineProperty(user, 'name', {
// get() {
// return internalName
// },
// set(val) {
// internalName = val
// showFirstName()
// showLastName()
// },
// })
// user.name = '王五'
euv.js
/**
* 观察某个对象的所有属性
* @param {Object} obj
*/
function observe(obj) {
for (const key in obj) {
// 初始值
let internalName = obj[key]
// 将依赖属性的函数存放到一个数组中
// 防止重复收集 使用 Set
let funcs = new Set()
Object.defineProperty(obj, key, {
get() {
// 依赖收集 记录: 哪些函数用到了该属性
if (window.__fun) {
funcs.add(window.__fun)
}
return internalName
},
set(val) {
internalName = val
// 派发更新 运行: 执行用到该属性的函数
Array.from(funcs).forEach((fun) => fun())
},
})
}
}
/**
* 得到当前依赖属性的函数
* @param {Function} fun
*/
function autorun(fun) {
window.__fun = fun
fun()
window.__fun = null
}
// 数据响应式: 当数据发生变化的时候, 自动运行一些相关函数