跳到主要内容

模拟实现

2024年03月18日
柏拉文
越努力,越幸运

一、认识


import { effect } from './effect';

/**
* @description: 递归读取 source
*/

function traverse(value, seen = new Set()) {
if (typeof value !== 'object' || value == null || seen.has(value)) {
return;
}
seen.add(value);
for (const k in value) {
traverse(value[k], seen);
}
return value;
}

export function watch(source, cb, options = {}) {
let getter;

if (typeof source === 'function') {
getter = source;
} else {
getter = () => traverse(source);
}

let oldValue, newValue;
/**
* @description: cleanup 用来存储用户注册的过期回调
* 作用: 通过 cleanup 存储用户注册的过期回调, 每当 watch 回调函数执行之前, 会优先执行用户通过 onInvalidate 注册的过期回调。 这样, 用户就有机会在过期回调中将上一次的副作用标记为 过期, 从而以 忽略请求数据的方式 解决竞态问题。
*/
let cleanup;

function onInvalidate(fn) {
cleanup = fn;
}
/**
* @description: 将 scheduler 调度函数作为一个独立的 job 函数
*/
const job = () => {
newValue = effectFn();
if (cleanup) {
cleanup();
}
cb(newValue, oldValue, onInvalidate);
oldValue = newValue;
};

const effectFn = effect(() => getter(), {
lazy: true,
scheduler: () => {
/**
* @description: options.flush 指定 调度函数 执行时机
* @param {*} options
* flush: 'post': 将 job 函数放到微任务队列中执行
*/
if (options.flush === 'post') {
const p = Promise.resolve();
p.then(job);
} else {
job();
}
}
});

/**
* @description: options.immediate 指定 调度函数 立即执行
* @param {*} options
*/
if (options.immediate) {
job();
} else {
oldValue = effectFn();
}
}

function watch(source, cd, options = {}) {}

二、调试


/**
* @description: watch 监听响应式数据
*/
// watch(obj, (newValue, oldValue) => {
// console.log(newValue.c, oldValue.c);
// });

// obj.c++;

/**
* @description: watch 监听 getter 函数
*/

// watch(
// () => obj.c,
// (newValue, oldValue) => {
// console.log(newValue, oldValue);
// }
// );

// obj.c++;

/**
* @description: watch 立即执行回调函数
*/

// watch(
// () => obj.c,
// (newValue, oldValue) => {
// console.log(newValue, oldValue);
// },
// {
// immediate: true
// }
// );

/**
* @description: watch 回调函数执行时机
*/
// watch(
// () => obj.c,
// (newValue, oldValue) => {
// console.log(newValue, oldValue);
// },
// {
// flush: 'sync' // ‘pre’ | 'post' | 'sync'
// }
// );

// obj.c++;

/**
* @description: watch 竞态问题的处理(过期的副作用)
*
* 场景: 依次发送请求 A 和 B, 我们根据请求的发送顺序, 认为请求 B 是后发送的, 请求 B 的数据才是最新的, 请求 A 的返回数据应该视为过期的。
*/

// function request() {
// return new Promise(resolve => {
// setTimeout(() => {
// resolve();
// }, 1000);
// });
// }

// watch(
// () => obj.c,
// async (newValue, oldValue, onInvalidate) => {
// let expired = false;
// onInvalidate(() => {
// expired = true;
// });
// await request();
// if (!expired) {
// console.log(newValue, oldValue);
// }
// }
// );

// obj.c++;

// setTimeout(() => {
// obj.c++;
// }, 200);