所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

深入解析 Vue 3.5 Watch 新特性:从基础到高级应用

概述

Vue 3.5 在响应式系统方面进行了重大改进,特别是在 watch API 方面。这些更新提供了更好的性能、更灵活的配置选项和更强大的功能。

主要更新内容

新的 watch 选项

flush 选项增强

  • 新增 prepost 选项
  • 更好的控制执行时机

deep 选项优化

  • 更智能的深度监听
  • 减少不必要的递归

immediate 选项改进

  • 更可靠的立即执行
  • 更好的错误处理

性能优化

更高效的依赖追踪

  • 减少不必要的响应式更新
  • 更精确的依赖收集

内存使用优化

  • 更好的垃圾回收
  • 减少内存泄漏风险

新的 API 功能

watchEffect 增强

  • 更灵活的清理函数
  • 更好的错误边界处理

watchPostEffectwatchSyncEffect

  • 新增的专用 watch 函数
  • 更精确的执行时机控制

详细示例

基础监听

<template>
  <div
    class="min-h-screen flex items-center justify-center bg-gradient-to-br from-purple-400 to-pink-600 p-5"
  >
    <div class="bg-white rounded-2xl p-8 shadow-2xl max-w-md w-full">
      <div class="mb-6">
        <label class="block text-gray-600 font-medium mb-2">输入消息:</label>
        <input
          v-model="message"
          class="w-full px-4 py-3 border-2 border-gray-200 rounded-lg text-base transition-all duration-300 focus:outline-none focus:border-purple-400 focus:ring-4 focus:ring-purple-100"
          placeholder="请输入消息..."
        />
      </div>

      <div class="mb-6 p-4 bg-gray-50 rounded-lg">
        <p class="text-gray-600 mb-2">
          消息:
          <span class="text-purple-500 font-semibold">{{
            message || "暂无"
          }}</span>
        </p>
        <p class="text-gray-600">
          计数: <span class="text-purple-500 font-semibold">{{ count }}</span>
        </p>
      </div>

      <button
        @click="increment"
        class="w-full py-3 px-6 bg-gradient-to-r from-purple-400 to-pink-500 text-white font-medium rounded-lg text-base cursor-pointer transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg active:translate-y-0"
      >
        增加计数
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, watchEffect } from "vue";

const message = ref("");
const count = ref(0);

// 基础 watch
watch(message, (newValue, oldValue) => {
  console.log("消息变化:", oldValue, "->", newValue);
});

// 深度监听对象
const user = ref({
  name: "张三",
  age: 25,
});

watch(
  user,
  (newUser, oldUser) => {
    console.log("用户信息更新:", newUser);
  },
  { deep: true }
);

// 立即执行
watch(
  count,
  (newCount) => {
    console.log("计数更新为:", newCount);
  },
  { immediate: true }
);

// watchEffect 示例
watchEffect(() => {
  console.log("当前消息和计数:", message.value, count.value);
});

const increment = () => {
  count.value++;
};
</script>

高级用法

<template>
  <div class="max-w-2xl mx-auto p-5 font-sans">
    <div class="mb-6">
      <input
        v-model="searchQuery"
        placeholder="搜索水果..."
        class="w-full px-4 py-3 border-2 border-gray-200 rounded-lg text-base transition-all duration-300 bg-white shadow-sm focus:outline-none focus:border-blue-500 focus:shadow-md focus:shadow-blue-100 placeholder-gray-400"
      />
    </div>

    <div v-if="loading" class="flex flex-col items-center py-10">
      <div
        class="w-10 h-10 border-3 border-gray-100 border-t-blue-500 rounded-full animate-spin mb-4"
      ></div>
      <p class="text-gray-500 text-sm m-0">加载中...</p>
    </div>

    <ul v-else class="list-none p-0 m-0 grid gap-3">
      <li
        v-for="item in filteredItems"
        :key="item.id"
        class="bg-white border border-gray-200 rounded-lg p-4 transition-all duration-200 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-lg hover:border-blue-500"
      >
        <span class="text-base font-medium text-gray-800">{{ item.name }}</span>
      </li>
    </ul>

    <div
      v-if="!loading && filteredItems.length === 0"
      class="text-center py-10 text-gray-500 text-base"
    >
      <p>没有找到匹配的水果</p>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, watchEffect, computed } from "vue";

const searchQuery = ref("");
const items = ref([
  { id: 1, name: "苹果" },
  { id: 2, name: "香蕉" },
  { id: 3, name: "橙子" },
]);
const loading = ref(false);

// 使用 flush: 'post' 确保 DOM 更新后执行
watch(
  searchQuery,
  async (newQuery) => {
    if (newQuery.trim()) {
      loading.value = true;
      try {
        // 模拟异步搜索
        await new Promise((resolve) => setTimeout(resolve, 500));
        console.log("搜索:", newQuery);
      } finally {
        loading.value = false;
      }
    }
  },
  { flush: "post" }
);

// 使用 watchEffect 进行响应式计算
const filteredItems = computed(() => {
  if (!searchQuery.value.trim()) return items.value;
  return items.value.filter((item) =>
    item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
  );
});

// 清理函数示例
watchEffect((onCleanup) => {
  const timer = setTimeout(() => {
    console.log("延迟执行");
  }, 1000);

  onCleanup(() => {
    clearTimeout(timer);
  });
});
</script>

监听多个源

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

const firstName = ref("");
const lastName = ref("");

// 监听多个响应式源
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
  console.log("姓名变化:", `${oldFirst} ${oldLast} -> ${newFirst} ${newLast}`);
});

// 使用 getter 函数
watch(
  () => [firstName.value, lastName.value],
  ([newFirst, newLast]) => {
    console.log("完整姓名:", `${newFirst} ${newLast}`);
  }
);
</script>

<template>
  <div>
    <input v-model="firstName" placeholder="请输入名字" />
    <input v-model="lastName" placeholder="请输入姓氏" />
  </div>
</template>

异步监听

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

const data = ref(null);
const error = ref(null);

// 异步 watch
watch(data, async (newData) => {
  if (newData) {
    try {
      error.value = null;
      // 处理数据
      await processData(newData);
    } catch (err) {
      error.value = err.message;
    }
  }
});

async function processData(data) {
  // 模拟异步处理
  await new Promise((resolve) => setTimeout(resolve, 1000));
  console.log("数据处理完成:", data);
}
</script>

最佳实践

选择合适的监听方式

// 对于简单的响应式更新,使用 watchEffect
watchEffect(() => {
  console.log("自动追踪依赖:", count.value);
});

// 对于需要精确控制的场景,使用 watch
watch(count, (newCount, oldCount) => {
  console.log("精确控制:", oldCount, "->", newCount);
});

性能优化

// 使用 deep 选项时要谨慎
watch(object, callback, { deep: true });

// 对于大型对象,考虑使用 getter
watch(() => object.value.specificProperty, callback);

错误处理

watch(data, async (newData) => {
  try {
    await processData(newData);
  } catch (error) {
    console.error("处理数据时出错:", error);
    // 处理错误
  }
});

清理资源

watchEffect((onCleanup) => {
  const subscription = subscribe();

  onCleanup(() => {
    subscription.unsubscribe();
  });
});

迁移指南

从 Vue 2 迁移

// Vue 2 写法
watch: {
  message(newVal, oldVal) {
    console.log("消息变化:", oldVal, "->", newVal);
  }
}

// Vue 3.5 写法
watch(message, (newVal, oldVal) => {
  console.log("消息变化:", oldVal, "->", newVal);
});

从 Vue 3.0 迁移

// Vue 3.0 写法
watch(source, callback, { flush: "pre" });

// Vue 3.5 写法 - 使用新的 flush 选项
watch(source, callback, { flush: "post" });

使用建议

选择合适的监听方式

// 简单响应式更新
watchEffect(() => {
  console.log("自动追踪:", value.value);
});

// 需要精确控制
watch(source, callback, { flush: "post" });

性能优化

// 避免深度监听大型对象
watch(() => object.value.specificProperty, callback);

// 使用清理函数
watchEffect((onCleanup) => {
  const subscription = subscribe();
  onCleanup(() => subscription.unsubscribe());
});

错误处理

watch(data, async (newData) => {
  try {
    await processData(newData);
  } catch (error) {
    console.error("处理失败:", error);
  }
});

注意事项

内存管理: 确保在组件卸载时正确清理 watch

性能考虑: 避免在 watch 中执行昂贵的操作

循环依赖: 注意避免在 watch 回调中修改被监听的响应式数据

异步操作: 正确处理异步 watch 中的错误和清理


总结

Vue 3.5 的 watch 更新带来了:

更好的性能表现: 更高效的依赖追踪和内存优化

更灵活的配置选项: 新的 flush 选项和增强的 immediate 选项

更强大的错误处理: 更好的错误边界处理和清理机制

更清晰的 API 设计: 新增的 watchPostEffect 和 watchSyncEffect

更好的开发体验: 更智能的深度监听和更可靠的立即执行

这些改进使得 Vue 的响应式系统更加健壮和易用,为开发者提供了更好的工具来处理复杂的响应式逻辑。

资源下载
下载价格免费
注意:本网站资源属于虚拟产品,不支持退款。请谨慎购买! 购买后资源无法下载,请联系客服QQ:844475003,微信号:th844475003。
原文链接:https://code.ifrontend.net/archives/956,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?