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

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

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 14:19:35 +0800 CST
JavaScript设计模式:桥接模式
2024-11-18 19:03:40 +0800 CST
快手小程序商城系统
2024-11-25 13:39:46 +0800 CST
CentOS 镜像源配置
2024-11-18 11:28:06 +0800 CST
JS 箭头函数
2024-11-17 19:09:58 +0800 CST
Java环境中使用Elasticsearch
2024-11-18 22:46:32 +0800 CST
Vue3中如何处理权限控制?
2024-11-18 05:36:30 +0800 CST
【SQL注入】关于GORM的SQL注入问题
2024-11-19 06:54:57 +0800 CST
html一个全屏背景视频
2024-11-18 00:48:20 +0800 CST
Mysql允许外网访问详细流程
2024-11-17 05:03:26 +0800 CST
手机导航效果
2024-11-19 07:53:16 +0800 CST
js一键生成随机颜色:randomColor
2024-11-18 10:13:44 +0800 CST
Go 开发中的热加载指南
2024-11-18 23:01:27 +0800 CST
Hypothesis是一个强大的Python测试库
2024-11-19 04:31:30 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
内网穿透技术详解与工具对比
2025-04-01 22:12:02 +0800 CST
程序员茄子在线接单