Vue父组件如何不迁移子组件ref,访问其状态并执行条件操作?
解决方案:父组件访问子组件内部ref状态(无需迁移ref到父组件)
针对你的需求——在父组件的enterLeaveNav函数中,仅当子组件Nav的isHamburgerClosed为true时才切换isNavClosed,这里提供几种可行的实现方式:
方案1:子组件通过defineExpose暴露状态
这是最直接的方式,子组件将内部ref暴露给父组件,父组件通过模板ref获取子组件实例来访问状态。
修改Nav.vue
在<script setup>中用defineExpose暴露需要的状态和方法:
<script setup> import { ref } from 'vue' const isHamburgerClosed = ref(true) const toggleNav = () => { isHamburgerClosed.value = !isHamburgerClosed.value // 保留你原有的菜单切换逻辑 } // 暴露给父组件访问 defineExpose({ isHamburgerClosed, toggleNav }) </script> <template> <div class="hamburgerMenu" :class="isHamburgerClosed ? 'hamburgerMenuMarginLeft2' : 'hamburgerMenuMarginLeft80'" @click="toggleNav" > <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> </div> </template>
修改父组件
添加模板ref绑定Nav组件,在enterLeaveNav中通过ref访问子组件状态:
<script setup> import Header from "@/Components/Header.vue"; import Nav from "@/Components/Nav.vue"; import { ref } from "vue"; const isScroll = ref(false); const isNavClosed = ref(false); // 定义模板ref获取Nav组件实例 const navRef = ref(null) function setIsNavClosed() { isNavClosed.value = !isNavClosed.value; } function enterLeaveNav() { isScroll.value = !isScroll.value; // 安全访问子组件暴露的状态 if (navRef.value?.isHamburgerClosed) { isNavClosed.value = !isNavClosed.value; } } </script> <template> <div> <header class="header container"> <Header /> </header> <div class="container-fluid"> <div class="row g-0"> <nav class="p-0 nav" :class="[ isScroll ? 'overflowScroll' : 'overflowHidden', isNavClosed ? 'width833' : 'width1633', ]" @mouseenter="enterLeaveNav" @mouseleave="enterLeaveNav" > <!-- 绑定模板ref --> <Nav ref="navRef" @open-close-nav="setIsNavClosed" /> </nav> <main class="ps-5" :class="[ isNavClosed ? 'offset-md-1 col-md-10' : 'offset-md-2 col-md-9', ]" style="margin-top: 100px" > <slot name="content" /> </main> </div> </div> </div> </template>
方案2:修复组合式函数的状态共享问题
你之前的组合式函数每次调用都会创建新的ref实例,导致父子组件状态不同步。可以改成单例模式,让所有组件共享同一状态:
修改useNavigation.js
让组合式函数只初始化一次状态:
import { ref } from "vue"; // 内部保存单例状态,避免重复创建 let navigationState = null; export function useNavigation() { if (!navigationState) { const isNavClosed = ref(false); const isHamburgerClosed = ref(false); const isScroll = ref(false); function toggleNav() { isNavClosed.value = !isNavClosed.value; isHamburgerClosed.value = !isHamburgerClosed.value; } function enterLeaveNav() { if (isHamburgerClosed.value) { isNavClosed.value = !isNavClosed.value; } isScroll.value = !isScroll.value; } navigationState = { isScroll, isNavClosed, isHamburgerClosed, toggleNav, enterLeaveNav, }; } return navigationState; }
父组件使用
直接调用useNavigation()即可获取共享状态:
<script setup> import Header from "@/Components/Header.vue"; import Nav from "@/Components/Nav.vue"; import { useNavigation } from "@/Composables/useNavigation"; const { isScroll, isHamburgerClosed, isNavClosed, enterLeaveNav } = useNavigation(); </script> <template> <div> <header class="header container"> <Header /> </header> <div class="container-fluid"> <div class="row g-0"> <nav class="p-0 nav" :class="[ isScroll ? 'overflowScroll' : 'overflowHidden', isNavClosed ? 'width833' : 'width1633', ]" @mouseenter="enterLeaveNav" @mouseleave="enterLeaveNav" > <Nav /> </nav> <main class="ps-5" :class="[ isNavClosed ? 'offset-md-1 col-md-10' : 'offset-md-2 col-md-9', ]" style="margin-top: 100px" > <slot name="content" /> </main> </div> </div> </div> </template>
Nav.vue使用
同样调用useNavigation()获取同一状态:
<script setup> import { useNavigation } from "@/Composables/useNavigation"; const { isHamburgerClosed, toggleNav } = useNavigation(); </script> <template> <div class="hamburgerMenu" :class="isHamburgerClosed ? 'hamburgerMenuMarginLeft2' : 'hamburgerMenuMarginLeft80'" @click="toggleNav" > <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> </div> </template>
方案3:子组件emit状态变化给父组件
子组件在汉堡菜单状态改变时,通过emit事件通知父组件更新状态,父组件统一维护状态:
修改Nav.vue
定义emit事件,在状态变化时通知父组件:
<script setup> import { ref, defineEmits } from 'vue' const emit = defineEmits(['update:hamburgerState']) const isHamburgerClosed = ref(true) const toggleNav = () => { isHamburgerClosed.value = !isHamburgerClosed.value // 向父组件发送状态更新事件 emit('update:hamburgerState', isHamburgerClosed.value) // 保留原有的菜单切换逻辑 } </script> <template> <div class="hamburgerMenu" :class="isHamburgerClosed ? 'hamburgerMenuMarginLeft2' : 'hamburgerMenuMarginLeft80'" @click="toggleNav" > <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> <div class="hamburgerPiece"></div> </div> </template>
修改父组件
接收子组件的状态更新,维护isHamburgerClosed的值:
<script setup> import Header from "@/Components/Header.vue"; import Nav from "@/Components/Nav.vue"; import { ref } from "vue"; const isScroll = ref(false); const isNavClosed = ref(false); const isHamburgerClosed = ref(true) function setIsNavClosed() { isNavClosed.value = !isNavClosed.value; } function enterLeaveNav() { isScroll.value = !isScroll.value; if (isHamburgerClosed.value) { isNavClosed.value = !isNavClosed.value; } } // 接收子组件的状态更新 const updateHamburgerState = (newState) => { isHamburgerClosed.value = newState } </script> <template> <div> <header class="header container"> <Header /> </header> <div class="container-fluid"> <div class="row g-0"> <nav class="p-0 nav" :class="[ isScroll ? 'overflowScroll' : 'overflowHidden', isNavClosed ? 'width833' : 'width1633', ]" @mouseenter="enterLeaveNav" @mouseleave="enterLeaveNav" > <Nav @update:hamburgerState="updateHamburgerState" @open-close-nav="setIsNavClosed" /> </nav> <main class="ps-5" :class="[ isNavClosed ? 'offset-md-1 col-md-10' : 'offset-md-2 col-md-9', ]" style="margin-top: 100px" > <slot name="content" /> </main> </div> </div> </div> </template>
内容的提问来源于stack exchange,提问作者ahmad kharabsheh
相关产品推荐
相关产品推荐

