◆

Vue3 keep-alive 下 watch 只在当前路由触发的方法

2023-12-19 09:480

场景

项目中使用了 keepalive,但经常会在不同的路由页面去监听一些全局状态管理的数据,或者是监听了路由相关的数据, 但是由于使用了 keepalive,在当前路由页面访问后,又离开,watch 并不会卸载掉,也就是,如果监听的数据发生了变化, 在非当前的路由页面,watch 也会触发,大多数情况下,这种副作用是不需要的。

解决方法

思路很简单,因为用到了 keepalive,所以我们主要在 onActivated, onDeactivated 生命周期中,启动与停止 watch

代码实现如下,类型先用 any 替代

/**
 * @description watch 监听,离开页面暂停,进入页面重新激活监听
 * @param sources
 * @param cb
 * @param options
 */
export function useCurrentPageWatch(
  source: any,
  cb: any,
  options: WatchOptions = {},
): void {
  let stopWatching: WatchStopHandle | null;

  // 创建一个开启 wacth 监听的函数
  const startWatching = () => {
    // 利用 watch 返回的 stop 函数,进行停止监听
    stopWatching = watch(source, cb, options);
  };

  // 停止现有的 watch,以防之前的 watch 仍在运行
  const stopWatchingIfNeeded = () => {
    if (stopWatching) {
      stopWatching();
      stopWatching = null;
    }
  };

  // 页面激活时启动 watch
  onActivated(() => {
    stopWatchingIfNeeded();
    startWatching();
  });

  // 页面失活时停止 watch
  onDeactivated(() => {
    stopWatchingIfNeeded();
  });
}

逻辑写完后,还要给函数添加上对应的 ts 类型,由于我们的函数还是依赖于 watch,所以我们的函数类型是和 watch 一致的,完整版代码如下

import type {
  WatchCallback,
  WatchOptions,
  WatchSource,
  WatchStopHandle,
  wacth,
} from 'vue';

type MapSources<T, Immediate> = {
  [K in keyof T]: T[K] extends WatchSource<infer V>
    ? Immediate extends true
      ? V | undefined
      : V
    : T[K] extends object
      ? Immediate extends true
        ? T[K] | undefined
        : T[K]
      : never;
};

type MultiWatchSources = (WatchSource<unknown> | object)[];

/**
 * @description watch 监听,离开页面暂停,进入页面重新激活监听
 * @param sources
 * @param cb
 * @param options
 */
export function useCurrentPageWatch<
  T extends MultiWatchSources,
  Immediate extends Readonly<boolean> = false,
>(
  sources: [...T],
  cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
  options?: WatchOptions,
): void;

export function useCurrentPageWatch<
  T extends Readonly<MultiWatchSources>,
  Immediate extends Readonly<boolean> = false,
>(
  source: T,
  cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
  options?: WatchOptions,
): void;

export function useCurrentPageWatch<
  T,
  Immediate extends Readonly<boolean> = false,
>(
  source: WatchSource<T>,
  cb: WatchCallback<T, Immediate extends true ? T | undefined : T>,
  options?: WatchOptions,
): void;

export function useCurrentPageWatch<
  T extends object,
  Immediate extends Readonly<boolean> = false,
>(
  source: T,
  cb: WatchCallback<T, Immediate extends true ? T | undefined : T>,
  options?: WatchOptions,
): void;

export function useCurrentPageWatch(
  source: any,
  cb: any,
  options: WatchOptions = {},
): void {
  let stopWatching: WatchStopHandle | null;

  const startWatching = () => {
    stopWatching = watch(source, cb, options);
  };

  const stopWatchingIfNeeded = () => {
    if (stopWatching) {
      stopWatching();
      stopWatching = null;
    }
  };

  // 页面激活时启动 watch
  onActivated(() => {
    stopWatchingIfNeeded(); // 停止现有的 watch,以防之前的 watch 仍在运行
    startWatching();
  });

  // 页面失活时停止 watch
  onDeactivated(() => {
    stopWatchingIfNeeded();
  });
}
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEVue3 keep-alive 下 watch 只在当前路由触发的方法加载中