响应式系统的核心就是一个 WeakMap --- Map --- Set 的数据结构。
track 收集依赖(涉及到了es6的三种数据结构即WeakMap,Map,Set) trigger 触发依赖(拿出所有依赖,每一个依赖就是一个副作用函数,所以直接调用即可) effect副作用函数(执行每一个回调函数)
<!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>mini vue3.X</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
<script>
function isObject(data) {
return data && typeof data === 'object';
}
//全局变量表示依赖
let activeEffect;
//存储依赖的数据结构
let targetMap = new WeakMap();
/**
*
track方法就是用来收集依赖的。我们用es6的weakMap数据结构来存储依赖,
然后为了简便化用一个全局变量来表示依赖
每一个依赖又是一个map结构,每一个map存储一个副作用函数即effect函数
收集依赖就这么简单,需要注意的是,这里涉及到了es6的三种数据结构即WeakMap,Map,Set
*/
function track(target, key) {
//拿到依赖
let depsMap = targetMap.get(target);
// 如果依赖不存在则初始化
if (!depsMap) targetMap.set(target, (depsMap = new Map()));
//拿到具体的依赖,是一个set结构
let dep = depsMap.get(key);
if (!dep) depsMap.set(key, (dep = new Set()));
//如果没有依赖,则存储再set数据结构中
if (!dep.has(activeEffect)) dep.add(activeEffect)
}
//trigger方法很明显就是拿出所有依赖,每一个依赖就是一个副作用函数,所以直接调用即可
function trigger(target, key) {
let depsMap = targetMap.get(target);
//存储依赖的数据结构都拿不到,则代表没有依赖,直接返回
if (!depsMap) return;
depsMap.get(key).forEach(e => e && e());
}
//副作用函数的作用也很简单,就是执行每一个回调函数。所以该方法有2个参数,
//第一个是回调函数,第二个则是一个配置对象
function effect(fn, options = {}) {
const __effect = function (...args) {
activeEffect = __effect;
return fn(...args);
}
//配置对象有一个lazy属性,用于computed计算属性的实现,因为计算属性是懒加载的,也就是延迟执行
//也就是说如果不是一个计算属性的回调函数,则立即执行副作用函数
if (!options.lazy) {
__effect();
}
return __effect;
}
function computed(fn) {
// 只考虑函数的情况
// 延迟计算 const c = computed(() => `${ count.value}!`)
let __computed;
//可以看到computed就是一个添加了lazy为true的配置对象的副作用函数
const run = effect(fn, { lazy: true });
__computed = {
//get 访问器
get value() {
return run();
}
}
return __computed;
}
/**
* 对象 { count:0 }
* @param {*} data
* @returns
*/
function reactive(data) {
if (!isObject(data)) return;
return new Proxy(data, {
get(target, key, receiver) {
//反射api
const ret = Reflect.get(target, key, receiver);
//收集依赖
track(target, key);
return isObject(ret) ? reactive(ret) : ret;
},
set(target, key, val, receiver) {
Reflect.set(target, key, val, receiver);
//触发依赖
trigger(target, key);
return true;
},
deleteProperty() {
const ret = Reflect.deleteProperty(target, key, receiver);
trigger(target, key);
return ret;
}
})
}
/**
* 基本类型
* const count = ref(0);
* count.value++
*/
function ref(target) {
let value = target;
const obj = {
get value() {
//收集依赖
track(obj, 'value');
return value;
},
set value(newValue) {
if (value === newValue) return;
value = newValue;
//触发依赖
trigger(obj, 'value');
}
}
return obj;
}
function mount(instance, el) {
effect(function () {
instance.$data && update(instance, el);
});
//setup返回的数据就是实例上的数据
instance.$data = instance.setup();
//这里的update实际上就是编译函数
update(instance, el);
}
// compile编译的实现很水
function update(instance, el) {
el.innerHTML = instance.render();
}
</script>
<script>
const App = {
$data: null,
setup() {
let count = ref(0);
let time = reactive({ second: 0 });
let com = computed(() => `${count.value + time.second}`);
setInterval(() => {
time.second++;
}, 1000);
setInterval(() => {
count.value++;
}, 2000);
return {
count,
time,
com
}
},
render() {
return `
<h1>How reactive?</h1>
<p>this is reactive work:${this.$data.time.second}</p>
<p>this is ref work:${this.$data.count.value}</p>
<p>this is computed work:${this.$data.com.value}</p>
`
}
}
mount(App, document.querySelector("#app"));
</script>