Last updated on
Vue3 实现
template

编译时

运行时



import vue from "rollup-plugin-vue";
import styles from "rollup-plugin-styles";
export default {
plugins: [
vue({
preprocessStyles: true,
}),
styles(
{
mode: "extract",
// ... or with relative to output dir/output file's basedir (but not outside of it)
mode: ["extract", "awesome-bundle.css"],
}
),
],
input: "index.vue",
output: {
format: "esm",
file: "dist/index.js",
},
};
reactivity
it("Proxy & Reflect ", () => {
const target = {
message1: "hello",
message2: "everyone",
get message3() {
return this.message1 + this.message2;
},
set xx(x) {
this.message1 = x;
},
};
const handler = {
get(target, key, receiver) {
console.log("read", key, target === receiver);
// Reflect 处理调用对象的基本方法
return Reflect.get(target, key, receiver);
},
set(target, property, value, receiver) {
return Reflect.set(target, property, value, receiver);
},
};
const proxy = new Proxy(target, handler);
proxy.message3;
proxy.message1 = "2";
expect(target.message1).toBe("2");
});
https://vitest.dev/guide/debugging
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Current Test File",
"autoAttachChildProcesses": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"args": ["run", "${relativeFile}"],
"smartStep": true,
"console": "integratedTerminal"
}
]
}
it("should observe basic properties", () => {
let dummy;
// init start
const counter = reactive({ num: 0 });
effect(() => (dummy = counter.num));
// init end
expect(dummy).toBe(0);
counter.num = 7;
expect(dummy).toBe(7);
});
初始化
effect(() => (dummy = counter.num)); -> const _effect = new ReactiveEffect(fn); -> _effect.run(); -> activeEffect = this as any; // # 很重要!! -> const result = this.fn(); -> () => (dummy = counter.num) -> counter.num -> return function get(target, key, receiver) { -> track(target, "get", key); -> trackEffects(dep); -> dep.add(activeEffect); (activeEffect as any).deps.push(dep);
初始化的目的是形成 targetMap 全局的 WeekMap
Map<Target extends object, Map<string | symbol, Set<ReactiveEffect>>>
{
[{num: 0}]: {
"num": [[new ReactiveEffect(() => (dummy = counter.num))]]
}
}
export function effect(fn, options = {}) {
const _effect = new ReactiveEffect(fn);
// 把用户传过来的值合并到 _effect 对象上去
// 缺点就是不是显式的,看代码的时候并不知道有什么值
extend(_effect, options);
_effect.run();
// 把 _effect.run 这个方法返回
// 让用户可以自行选择调用的时机(调用 fn)
const runner: any = _effect.run.bind(_effect);
runner.effect = _effect;
return runner;
}
export class ReactiveEffect {
active = true;
deps = [];
public onStop?: () => void;
constructor(public fn, public scheduler?) {
console.log("创建 ReactiveEffect 对象");
}
run() {
console.log("run");
// 运行 run 的时候,可以控制 要不要执行后续收集依赖的一步
// 目前来看的话,只要执行了 fn 那么就默认执行了收集依赖
// 这里就需要控制了
// 是不是收集依赖的变量
// 执行 fn 但是不收集依赖
if (!this.active) {
return this.fn();
}
// 执行 fn 收集依赖
// 可以开始收集依赖了
shouldTrack = true;
// 执行的时候给全局的 activeEffect 赋值
// 利用全局属性来获取当前的 effect
activeEffect = this as any; // # 很重要!!
// 执行用户传入的 fn
console.log("执行用户传入的 fn");
const result = this.fn();
触发变化
counter.num = 7; -> return function set(target, key, value, receiver) { -> trigger(target, "set", key); -> triggerEffects(createDep(effects)); -> effect.run(); -> const result = this.fn();