SSF0SSF0
首页
前端
  • Node
  • Go
  • C#
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
Timeline
Github
标签
分类
首页
前端
  • Node
  • Go
  • C#
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
Timeline
Github
标签
分类
  • HTML

    • html1
    • html2
  • CSS

    • Flex 布局常见问题与解决方案
  • JavaScript

    • 数据类型及引用问题
    • 处理 Blob 类型文件下载问题总结
    • localStorage 与 sessionStorage 区别
    • JavaScript 中的 script 标签问题详解
    • JavaScript 中的`this`指向问题详解
    • SessionStorage踩坑记录:它真的能"只设置一次"吗?
    • 动态加载 JS 脚本方法对比
    • 浏览器页面关闭场景下的数据上报
  • es6

    • Promise
    • es6 模块导出方式全解析
  • Vue2

    • created VS mounted 发起异步请求
    • vue2-2
  • Vue3

    • Vite + Vue3 修改 element-plus 源码
    • Vue v-if 与 v-show
    • Vue3 ref 获取组件
    • Vue3 路由传参
    • 父子组件与组件里 Hooks 加载顺序
    • 第三方组件传入参数TS提示
    • Vue3 Props 在 Hooks 中的响应性处理
    • Vue Router 的两种历史模式及部署配置详解
    • 在Vue 3项目中顺利集成Tailwind CSS的完整指南
    • Vue 3 深度选择器:deep()完全指南
  • Electron

    • 快速构建 electron + vue3 项目
  • TS

    • TS 泛型
    • 记录模板使用断言的问题
    • type 与 interface
  • WebPack

    • Webpack 介绍
  • Vite

    • Vite CLI 常见命令
    • vite 与 webpack 比较
  • 项目工程

    • 前端代码风格
    • Vue3 项目规范
    • npm 镜像问题
    • 包管理工具
    • 使用 engines 限制开发环境依赖
    • 打包与shell交互指定模式
    • 使用 pnpm 构建 Monorepo 实战指南
    • pnpm 修改依赖源码打包报错
  • 记录一下小程序
  • 控制浏览器正确保存网站账号密码的技巧

Vue3 Props 在 Hooks 中的响应性处理

1. 问题背景

在 Vue3 的组合式 API 开发中,经常需要将父组件的 props 传递给自定义 hooks 进行处理。这个过程中很容易遇到响应性丢失的问题。

2. 问题场景

2.1 业务需求

  • 父组件需要动态切换 API 函数

  • 子组件需要使用这个 API 函数获取数据

  • API 函数的变化需要能够被子组件感知到

2.2 初始代码

父组件 (Parent.vue):

<template>
  <div>
    <button @click="changeApi">Change API</button>
    <child-component :api="getDataApi" />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const getDataApi = ref();
// 模拟两个不同的 API 函数
const api1 = (params: any) => console.log("API 1 called", params);
const api2 = (params: any) => console.log("API 2 called", params);
const changeApi = () => {
  getDataApi.value = api1; // 或 api2
};
</script>

子组件 (Child.vue):

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
  </div>
</template>

<script setup lang="ts">
import { toRef } from "vue";
import { useDataFetch } from "./useDataFetch";

const props = defineProps<{ api: any }>();

// ❌ 问题版本 1:直接传递 props.api (在 hooks 中使用时已经失去响应性)
const { fetchData } = useDataFetch({
  api: props.api,
});
</script>

Hook (useDataFetch.ts):

export function useDataFetch(reception: { api: any }) {
  const fetchData = async () => {
    const res = await reception.api({ id: 1 });
    console.log(res);
  };
  return {
    fetchData,
  };
}

3. 问题分析

3.1 响应性丢失的原因

  1. Props 虽然本身是响应式的,但在传递给 hooks 时可能会失去响应性

  2. 直接使用 props.api 会创建一个值的副本,失去响应式连接

  3. 错误的 toRef 使用方式会创建新的响应式引用,但与原始 props 断开连接

3.2 代码中的具体表现

  1. 当父组件中的 getDataApi 改变时,子组件中的 fetchData 方法仍然使用旧的 API

  2. 没有正确建立响应式连接

  3. 控制台输出始终显示第一次传入的 API 结果

4. 尝试解决

4.1 尝试方案一(失败)

// ❌ 错误尝试:使用 toRef 包装 props.api
const { fetchData } = useDataFetch({
  api: toRef(props.api),
});

问题:创建了新的 ref,但失去了与原始 props 的响应式连接

4.2 尝试方案二(成功)

// ✅ 正确方案:使用 toRef(props, "propertyName")
const { fetchData } = useDataFetch({
  api: toRef(props, "api"),
});

5. 完整的解决方案

修改后的子组件 (Child.vue):

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
  </div>
</template>

<script setup lang="ts">
import { toRef } from "vue";
import { useDataFetch } from "./useDataFetch";

const props = defineProps<{ api: any }>();

const { fetchData } = useDataFetch({
  api: toRef(props, "api"),
});
</script>

修改后的 Hook (useDataFetch.ts):

import { type Ref } from "vue";

export function useDataFetch(reception: { api: Ref<any> }) {
  const fetchData = async () => {
    if (!reception.api.value) return;
    const res = await reception.api.value({ id: 1 });
    console.log(res);
  };

  return {
    fetchData,
  };
}

6. 总结

6.1 三种传值方式的区别

  1. api: toRef(props, "api")

    • ✅ 正确方式

    • 创建指向 props.api 的响应式引用

    • 保持与原始 props 的响应式连接

  2. api: toRef(props.api)

    • ❌ 错误方式

    • 创建新的响应式引用,但与原始 props 断开连接

    • 父组件更新时不会触发子组件更新

  3. api: props.api

    • ❌ 错误方式

    • 直接传值(在 hooks 中使用时已经失去响应性),在复杂场景中失去响应性

    • 无法感知父组件的更新

6.2 最佳实践

  1. 在传递 props 到组合式函数(hooks)时,使用 toRef(props, "propertyName")

  2. 确保正确定义类型,包括 Ref 类型的标注

  3. 在使用前检查值是否存在

  4. 使用 .value 访问 ref 的值

6.3 原因解释

  1. Vue3 的响应式系统需要正确的引用链接才能追踪变化

  2. toRef(props, "propertyName") 创建了正确的响应式连接

  3. 直接传值或错误使用 toRef 会破坏这个响应式连接

6.4 注意事项

  1. Props 本身是响应式的,只有在传递给其他函数时才需要特殊处理

  2. 使用 toRef 时要注意参数的正确性

  3. 在 TypeScript 中要正确标注类型,包括 Ref 类型

这个问题的核心在于理解 Vue3 的响应式系统和正确维持响应式连接。使用正确的方式可以确保组件间的数据流动保持响应性。

这个 demo 完整展示了:

  1. 问题的起因

  2. 错误的尝试

  3. 正确的解决方案

  4. 详细的原理解释

  5. 最佳实践建议

希望这个案例能帮助更好地理解 Vue3 中 props 传值和响应性的问题。

最后更新时间:
贡献者: 何风顺
上一页
第三方组件传入参数TS提示
下一页
Vue Router 的两种历史模式及部署配置详解