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

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

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

推荐文章

18个实用的 JavaScript 函数
2024-11-17 18:10:35 +0800 CST
MySQL 1364 错误解决办法
2024-11-19 05:07:59 +0800 CST
Go 语言实现 API 限流的最佳实践
2024-11-19 01:51:21 +0800 CST
前端项目中图片的使用规范
2024-11-19 09:30:04 +0800 CST
前端如何给页面添加水印
2024-11-19 07:12:56 +0800 CST
一键压缩图片代码
2024-11-19 00:41:25 +0800 CST
Vue3 vue-office 插件实现 Word 预览
2024-11-19 02:19:34 +0800 CST
Vue3中如何实现插件?
2024-11-18 04:27:04 +0800 CST
服务器购买推荐
2024-11-18 23:48:02 +0800 CST
实现微信回调多域名的方法
2024-11-18 09:45:18 +0800 CST
随机分数html
2025-01-25 10:56:34 +0800 CST
Hypothesis是一个强大的Python测试库
2024-11-19 04:31:30 +0800 CST
使用Python提取图片中的GPS信息
2024-11-18 13:46:22 +0800 CST
JavaScript设计模式:单例模式
2024-11-18 10:57:41 +0800 CST
避免 Go 语言中的接口污染
2024-11-19 05:20:53 +0800 CST
快手小程序商城系统
2024-11-25 13:39:46 +0800 CST
Nginx 负载均衡
2024-11-19 10:03:14 +0800 CST
Elasticsearch 条件查询
2024-11-19 06:50:24 +0800 CST
程序员茄子在线接单