根据源码抽离了部分ref 实现简单数据类型双向绑定的原理,不考虑引用类型
1. 先创建RefImpl 实例
1> 通过 get 和 set 关键字可以使函数 value() 触发的方式调整为 .value 的形式触发
2> 通过实例中定义的 __v_isRef 变量判断是否为RefImpl 实例数据
3> dep 用来缓存当前实例的 effect ,用于记录依赖与触发依赖,当数据改变从而刷新视图
class RefImpl {
__v_isRef = true;
dep = new Set();
constructor(value) {
this._value = value;
}
get value() {
// 收集依赖
trackRefValue(this);
return this._value;
}
set value(newValue) {
// 判断值是否发生改变
if (!Object.is(newValue, this._value)) {
this._value = newValue;
// 触发依赖
triggerRefValue(this);
}
}
}
2. 定义 ref 函数
定义 ref 函数 如果是 <script> 中方便直接export 将该方法暴露出去
function isRef(value) {
return value && value.__v_isRef;
}
function ref(value) {
// 用于判断入参是否为RefImfl数据 如果是则直接返回
if (isRef(value)) {
return value;
}
// 否则生成一个 RefImfl 实例
return new RefImpl(value);
}
const flag = ref(true);
3. 定义 ReactiveEffect 实例与 effect函数
class ReactiveEffect {
constructor(fn) {
this.fn = fn;
}
run() {
activeEffect = this;
return this.fn();
}
}
function effect(fn) {
const effect = new ReactiveEffect(fn);
effect.run();
}
effect(() => {
const creatDiv = `<div class='test'>${flag.value ? "是" : "否"}</div>`;
app.innerHTML = creatDiv;
});
4. 收集依赖与触发依赖的函数
// 用于收集依赖
function trackRefValue(ref) {
if (activeEffect) {
ref.dep.add(activeEffect);
}
}
// 用于触发依赖
function triggerRefValue(ref) {
ref.dep.forEach((effect) => {
effect.run();
});
}
5. 全部实现代码(不包含引用数据类型)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ref 简单数据类型监听</title>
</head>
<body>
<div id="app"></div>
</body>
<script>
const app = document.getElementById("app");
let activeEffect; // 缓存执行函数
class RefImpl {
__v_isRef = true;
dep = new Set();
constructor(value) {
this._value = value;
}
get value() {
// 收集依赖
trackRefValue(this);
return this._value;
}
set value(newValue) {
// 判断值是否发生改变
if (!Object.is(newValue, this._value)) {
this._value = newValue;
triggerRefValue(this);
}
}
}
function isRef(value) {
return value && value.__v_isRef;
}
function ref(value) {
if (isRef(value)) {
return value;
}
return new RefImpl(value);
}
const flag = ref(true);
class ReactiveEffect {
constructor(fn) {
this.fn = fn;
}
run() {
activeEffect = this;
return this.fn();
}
}
function effect(fn) {
const effect = new ReactiveEffect(fn);
effect.run();
}
effect(() => {
const creatDiv = `<div class='test'>${flag.value ? "是" : "否"}</div>`;
app.innerHTML = creatDiv;
});
function trackRefValue(ref) {
if (activeEffect) {
ref.dep.add(activeEffect);
}
}
function triggerRefValue(ref) {
ref.dep.forEach((effect) => {
effect.run();
});
}
setInterval(() => {
flag.value = !flag.value;
}, 1000);
</script>
</html>