@vue/reactivity@3.0.0-alpha.0
这里先看的是最早期的代码,结构会更容易读些,下面的文章不是直接看源码,而是由 vue reativeity 的出口 api 来进入,试着实现该接口(并非玩去实现)
demo.html
<script type="module">
import { reactive, effect } from "./simple-reactivity.js";
const root = document.querySelector("#root");
const btnName = document.querySelector("#btn_name");
const btnAge = document.querySelector("#btn_age");
const inputName = document.querySelector("#input_name");
const user = reactive({
name: "张三",
age: 27,
});
inputName.value = user.name;
effect(() => {
root.innerHTML = `<h1>${user.name}---${user.age}</h1>`;
});
effect(() => {
console.log(user.age);
});
inputName.oninput = function (event) {
user.name = event.target.value;
};
btnAge.onclick = function () {
user.age += 1;
};
</script>
<div id="root"></div>
<input type="text" id="input_name" placeholder="名字" />
<button id="btn_age">年龄 + 1</button>
我们这里要实现的是 reactive 和 effect,虽然我们直接在 vue core 中没有 effect 接口,但是 watch,computed 内都是使用了 effect,所以我们直接关注 effect(类似 react effect 的效果)
reactive
const user = reactive({
name: "张三",
age: 27,
});
vue2 中响应式的实现依靠对象的描述(defineProperties)
get
作为该属性的 getter 函数,如果没有 getter 则为undefined
。函数返回值将被用作属性的值。默认为 undefined
set
作为属性的 setter 函数,如果没有 setter 则为undefined
。函数将仅接受参数赋值给该属性的新值。默认为 undefined
vue3 使用了 Proxy(Reflect, Map, Set, WeakMap, WeakSet)需要先了解一下
ok, 开始
先想一下,reactive 的类型(typescript)
function reactive<T extends object>(target: T): ReactiveTarget
function get(target, key, receiver) {
// track() 做些拦截
return Reflect.get(target, key, receiver);
}
function set(target, key, value, receiver) {
// trigger() 做些拦截
return Reflect.set(target, key, value, receiver);
}
function reactive(target) {
return new Proxy(target, {
get,
set,
});
}
为了提高性能,我们同一个 target,没必要每次都去创建 proxy,可以搞个缓存嘛
const rawToReactive = new WeakMap(); // taret: reactiveTarget
const reactiveToRaw = new WeakMap(); // reactiveTarget: target
function isObject(val) {
return val != null && typeof val === "object";
}
export function reactive(target) {
return createReactiveObject(target, rawToReactive, reactiveToRaw, {
get,
set,
});
}
function createReactiveObject(target, toProxy, toRaw, handlers) {
if (!isObject(target)) {
// 不是对象不处理
return target;
}
let observed = toProxy.get(target);
if (observed != undefined) {
return observed;
}
if (toRaw.has(target)) {
return target;
}
observed = new Proxy(target, handlers);
toProxy.set(target, observed);
toRaw.set(observed, target);
return observed;
}
其实这里并没有完成是响应,我们只是将 target 创建了一个 proxy,返回。而具体的拦截执行是 reactiveTarget 的使用
const user = reactive({
name: "张三",
age: 27,
});
user.name; // get => "张三"
user.age = user.age + 1; // set => true; 28
ok,这里我们先不用处理了,从数据来说,这以及是我们要的结果了,而之后的响应是和执行有关了,也就是 set,get 等 handlers 中如何拦截等操作
effect
function effect()