Skip to content

解除上一次watch ​

本节目标:理解“解除上一次watch”的核心思路,并能把它用于实际开发或面试表达。 当用户在输入框中输入内容的时候,我们要根据输入的内容返回ajax结果;

如果watch输入的内容,输入框的内容一变化就可以访问接口,渲染页面可能会出现什么问题呢? 光加上防抖不够; 因为每次请求返回的时间是不一样的,可能后面的请求先返回,显示了后面的结果;

实现: 给watch的第二个参数即调度函数传入第三个参数:onCleanup,每次执行本次调度函数的时候,会执行上一次watch的onCleanup;

const state = reactive({ flag: true, name: 'jw', age: 30 })
let i = 2000;
function getData(timer) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(timer)
        }, timer);
    })
}
watch(() => state.age, async (newValue, oldValue, onCleanup) => {
    let clear = false;
    onCleanup(() => {
        clear = true;
    })
    i -= 1000;
    let r = await getData(i); // 第一次执行1s后渲染1000, 第二次执行0s后渲染0, 最终应该是0
    if (!clear) { document.body.innerHTML = r; }
}, { flush: 'sync' });
state.age = 31;
state.age = 32;
// 第一次调用watch的时候传入一个回调, 第二次调用watch的时候执行上一次传入的回调
在上例中:
// 1) 第一次调用watch的时候用户注入一个取消的回调
// 2) 第二次调用watch的时候会执行第一次用户注入的回调
// 3) 第三次调用watch会执行第二次注入的回调
// 后面的watch触发会将上次watch中的clear 置为true
export function watch(source, cb) {
  let getter;
  if (isReactive(source)) {
    getter = () => traversal(source)
  } else if (isFunction(source)) {
    getter = source
  } else {
    return
  }
  let cleanup;
  const onCleanup = (fn) => {
    cleanup = fn; // 保存用户的函数
  }
  let oldValue;
  const job = () => {
    if (cleanup) cleanup(); // 下一次watch开始触发上一次watch的清理
    const newValue = effect.run();
    cb(newValue, oldValue, onCleanup);
    oldValue = newValue
  }
  // 在effect中访问属性就会依赖收集
  const effect = new ReactiveEffect(getter, job);
  oldValue = effect.run();
}

本站总访问 — 次