编程 Vue 3 中的 Watch 实现及最佳实践

2024-11-18 22:18:40 +0800 CST views 602

Vue 3 中的 Watch 实现及最佳实践

在 Vue 3 中,watchwatchEffectonWatcherCleanup 是非常重要的 API,用于监听响应式数据的变化和处理副作用。本文将详细介绍它们的实现原理、使用方法以及最佳实践。

概述

watchwatchEffect 是 Vue 3 中用于监听响应式数据变化并执行副作用的两个主要 API,而 onWatcherCleanup 则用于在 watcher 被清理时执行清理逻辑。它们在处理异步操作、数据变化响应等场景中非常有用。

源码解析

文件位置

核心代码在 core/packages/runtime-core/src/apiWatch.ts 中实现。

关键代码示例与注释

apiWatch.ts

import { ReactiveEffect, track, trigger } from '@vue/reactivity';
import { queuePreFlushCb } from './scheduler';
import { EMPTY_OBJ, isFunction, isObject } from '@vue/shared';

// 定义 watch 函数
export function watch(source, cb, options?) {
  return doWatch(source, cb, options);
}

// 定义 watchEffect 函数
export function watchEffect(effect, options?) {
  return doWatch(effect, null, options);
}

// 核心的 doWatch 函数
function doWatch(source, cb, { immediate, deep, flush, onTrack, onTrigger } = EMPTY_OBJ) {
  let getter;
  if (isFunction(source)) {
    getter = source; // 如果 source 是函数,直接作为 getter
  } else {
    getter = () => source; // 否则创建一个返回 source 的函数
  }

  let cleanup;
  const onCleanup = (fn) => {
    cleanup = effect.onStop = () => {
      fn(); // 注册清理函数
    };
  };

  const job = () => {
    if (cleanup) {
      cleanup(); // 执行清理函数
    }
    if (cb) {
      cb(); // 执行回调函数
    } else {
      effect.run(); // 运行副作用
    }
  };

  const effect = new ReactiveEffect(getter, job);
  if (cb) {
    if (immediate) {
      job(); // 立即执行
    } else {
      effect.run(); // 否则运行副作用
    }
  } else {
    effect.run(); // 运行副作用
  }

  return () => {
    effect.stop(); // 停止副作用
  };
}

函数内部关键流程

  1. 定义 getter

    • 如果 source 是函数,则直接作为 getter
    • 否则,创建一个返回 source 的函数作为 getter
  2. 定义清理函数
    使用 onCleanup 注册清理函数,在副作用停止时执行。

  3. 定义 job 函数
    job 函数中,先执行清理函数,然后执行回调函数或运行副作用。

  4. 创建 ReactiveEffect 实例
    使用 getterjob 创建 ReactiveEffect 实例。

  5. 执行副作用或回调
    根据 immediate 选项决定是否立即执行 job 或运行副作用。

  6. 返回停止函数
    返回一个函数,用于停止副作用。

API 使用方式与参数

watch

watch 用于监听响应式数据的变化,并在变化时执行回调函数。

参数

  • source:要监听的响应式数据或 getter 函数。
  • cb:数据变化时执行的回调函数。
  • options:可选参数对象,包括 immediatedeepflushonTrackonTrigger

示例代码

<template>
  <div>
    <input v-model="question" placeholder="Ask a question" />
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const question = ref('');
    const answer = ref('Questions usually contain a question mark. ;-)');

    // 监听 question 的变化
    watch(question, (newQuestion, oldQuestion) => {
      if (newQuestion.includes('?')) {
        answer.value = 'Thinking...';
        setTimeout(() => {
          answer.value = 'Yes';
        }, 1000);
      }
    });

    return {
      question,
      answer,
    };
  },
};
</script>

watchEffect

watchEffect 用于自动追踪回调函数中使用的所有响应式数据,并在这些数据变化时重新执行回调函数。

参数

  • effect:要执行的副作用函数。
  • options:可选参数对象,包括 flush

示例代码

<template>
  <div>
    <input v-model="question" placeholder="Ask a question" />
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const question = ref('');
    const answer = ref('Questions usually contain a question mark. ;-)');

    // 自动追踪 question 的变化
    watchEffect(() => {
      if (question.value.includes('?')) {
        answer.value = 'Thinking...';
        setTimeout(() => {
          answer.value = 'Yes';
        }, 1000);
      }
    });

    return {
      question,
      answer,
    };
  },
};
</script>

onWatcherCleanup

onWatcherCleanup 用于在 watcher 被清理时执行清理逻辑。

示例代码

<template>
  <div>
    <input v-model="question" placeholder="Ask a question" />
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const question = ref('');
    const answer = ref('Questions usually contain a question mark. ;-)');

    // 监听 question 的变化
    watch(question, (newQuestion, oldQuestion, onCleanup) => {
      if (newQuestion.includes('?')) {
        answer.value = 'Thinking...';
        const timeout = setTimeout(() => {
          answer.value = 'Yes';
        }, 1000);

        // 注册清理函数
        onCleanup(() => {
          clearTimeout(timeout);
        });
      }
    });

    return {
      question,
      answer,
    };
  },
};
</script>

最佳实践示例

深度监听

使用 deep 选项来监听对象的嵌套属性变化。

<template>
  <div>
    <input v-model="user.name" placeholder="Enter your name" />
    <p>{{ user.name }}</p>
  </div>
</template>

<script>
import { reactive, watch } from 'vue';

export default {
  setup() {
    const user = reactive({
      name: '',
    });

    // 深度监听 user 对象的变化
    watch(user, (newUser, oldUser) => {
      console.log('User changed:', newUser);
    }, { deep: true });

    return {
      user,
    };
  },
};
</script>

立即执行

使用 immediate 选项在 watcher 创建时立即执行回调。

<template>
  <div>
    <input v-model="question" placeholder="Ask a question" />
    <p>{{ answer }}</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const question = ref('');
    const answer = ref('Questions usually contain a question mark. ;-)');

    // 立即执行回调
    watch(question, (newQuestion) => {
      if (newQuestion.includes('?')) {
        answer.value = 'Thinking...';
        setTimeout(() => {
          answer.value = 'Yes';
        }, 1000);
      }
    }, { immediate: true });

    return {
      question,
      answer,
    };
  },
};
</script>

总结

在 Vue 3 中,watchwatchEffectonWatcherCleanup 是处理响应式数据变化和副作用的重要工具。通过理解它们的实现原理和使用方式,可以更好地管理应用中的数据变化和副作用处理,提升开发效率和代码质量。

推荐文章

利用图片实现网站的加载速度
2024-11-18 12:29:31 +0800 CST
Vue3中如何实现国际化(i18n)?
2024-11-19 06:35:21 +0800 CST
Nginx 性能优化有这篇就够了!
2024-11-19 01:57:41 +0800 CST
Vue3中的v-for指令有什么新特性?
2024-11-18 12:34:09 +0800 CST
7种Go语言生成唯一ID的实用方法
2024-11-19 05:22:50 +0800 CST
微信小程序热更新
2024-11-18 15:08:49 +0800 CST
paint-board:趣味性艺术画板
2024-11-19 07:43:41 +0800 CST
如何在Vue3中处理全局状态管理?
2024-11-18 19:25:59 +0800 CST
25个实用的JavaScript单行代码片段
2024-11-18 04:59:49 +0800 CST
JavaScript 实现访问本地文件夹
2024-11-18 23:12:47 +0800 CST
Python Invoke:强大的自动化任务库
2024-11-18 14:05:40 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
thinkphp swoole websocket 结合的demo
2024-11-18 10:18:17 +0800 CST
GROMACS:一个美轮美奂的C++库
2024-11-18 19:43:29 +0800 CST
Go中使用依赖注入的实用技巧
2024-11-19 00:24:20 +0800 CST
使用Python实现邮件自动化
2024-11-18 20:18:14 +0800 CST
一个简单的打字机效果的实现
2024-11-19 04:47:27 +0800 CST
JavaScript 的模板字符串
2024-11-18 22:44:09 +0800 CST
如何实现虚拟滚动
2024-11-18 20:50:47 +0800 CST
PHP设计模式:单例模式
2024-11-18 18:31:43 +0800 CST
程序员茄子在线接单