Vue JS:如何让页面中的两个Vue实例协同工作?
问题描述
我做了一个食谱网站,首页的标签组件是一个Vue实例,同时还做了一个作为Vue实例的搜索栏。现在的问题是,搜索结果只会显示在搜索栏上方,我想要的是搜索结果只在标签区域展示,并且隐藏标签标题(比如早餐、沙拉、烘焙这些)。
目前两个Vue实例完全独立,没法互相通信。我试过把搜索和标签实例合并成一个,但一直出现Vue警告:
[Vue warn]: Property or method "searchQuery" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
现在卡住了,求建议。
搜索组件代码
<div id="search"> <div class="main-search"> <h1 class="search-question">What are you in the mood for?</h1> <div class="search-box"> <input type="text" v-model="searchQuery" class="input-search" placeholder="Bread"> <button class="btn-search"><i class="fa fa-search"></i></button> </div> </div> <div class="container"> <div class="recipe" v-for="post in filteredList"> <div v-if="searchQuery"> <a v-bind:href="post.url" class="recipe-card__card-link" target="_blank"> <img v-bind:src="post.image" alt="" class="recipe-card__image"/> <div class="recipe-card__text-wrapper"> <h2 class="recipe-card__title">{{post.name}}</h2> <div class="recipe-card__details-wrapper"> <p class="recipe-card__excerpt">{{post.body}}</p> <a v-bind:href="post.url" class="recipe-card__read-more">Read more <i class="fa fa-arrow-right"></i></a> </div> </div> </a> </div> </div> </div> </div> </div>
标签组件代码
<main id="tab"> <header> <nav> <ul> <div id="arrow"> <span></span> <span></span> <span></span> </div> <li v-for="(tab, tabName) in tabs" :key="tabName"> <button class="tab" @click="setTabActive(tabName)" :class="{'active': tabName === activeTab}"> <span class="tab-copy">{{ tabName }}</span> <span class="tab-background"> </span> </button> </li> </ul> </nav> </header> <article> <div class="container"> <transition name="fade" mode="out-in" appear :duration="500"> <tab-content v-for="(tabContent, t) in tabs" :data="tabContent" :key="'content'+t" v-if="t === activeTab" inline-template> <div class="content-wrapper"> <div class="recipes" v-for="(recipe, i) in data.recipes" :key="i"> <a v-bind:href="recipe.url" class="recipe-card__card-link"></a> <img :src="recipe.image" alt="" class="recipe-card__image"> <div class="recipe-card__text-wrapper"> <h2 class="recipe-card__title">{{recipe.name}}</h2> <div class="recipe-card__details-wrapper"> <p class="recipe-card__excerpt">{{recipe.body}}</p> <a v-bind:href="recipe.url" class="recipe-card__read-more">Read more <i class="fa fa-arrow-right"></i></a> </div> </div> </div> </div> </tab-content> </transition> </div> </article> </main>
解决方案
1. 统一Vue实例,解决searchQuery未定义问题
你合并实例时出现的警告,本质是searchQuery没在合并后的Vue实例的data里声明成响应式数据。合并后要做这几点:
- 在根实例的
data中添加searchQuery: '' - 把搜索相关的逻辑(比如
filteredList计算属性)也移到根实例里 - 页面根容器要包含搜索和标签组件,比如用
<div id="app">包裹两者
2. 控制标签区域的显示逻辑
在标签组件里加条件判断,实现「有搜索内容时显示搜索结果、隐藏标签栏;无搜索内容时恢复标签栏和对应内容」:
<main id="tab"> <!-- 标签导航:仅无搜索内容时显示 --> <header v-if="!searchQuery"> <nav> <ul> <div id="arrow"> <span></span> <span></span> <span></span> </div> <li v-for="(tab, tabName) in tabs" :key="tabName"> <button class="tab" @click="setTabActive(tabName)" :class="{'active': tabName === activeTab}"> <span class="tab-copy">{{ tabName }}</span> <span class="tab-background"> </span> </button> </li> </ul> </nav> </header> <article> <div class="container"> <!-- 搜索结果:有搜索内容时显示 --> <div v-if="searchQuery" class="content-wrapper"> <div class="recipes" v-for="(post, i) in filteredList" :key="i"> <a v-bind:href="post.url" class="recipe-card__card-link" target="_blank"> <img v-bind:src="post.image" alt="" class="recipe-card__image"/> <div class="recipe-card__text-wrapper"> <h2 class="recipe-card__title">{{post.name}}</h2> <div class="recipe-card__details-wrapper"> <p class="recipe-card__excerpt">{{post.body}}</p> <a v-bind:href="post.url" class="recipe-card__read-more">Read more <i class="fa fa-arrow-right"></i></a> </div> </div> </a> </div> </div> <!-- 原标签内容:无搜索内容时显示 --> <transition name="fade" mode="out-in" appear :duration="500" v-else> <tab-content v-for="(tabContent, t) in tabs" :data="tabContent" :key="'content'+t" v-if="t === activeTab" inline-template> <div class="content-wrapper"> <div class="recipes" v-for="(recipe, i) in data.recipes" :key="i"> <a v-bind:href="recipe.url" class="recipe-card__card-link"></a> <img :src="recipe.image" alt="" class="recipe-card__image"> <div class="recipe-card__text-wrapper"> <h2 class="recipe-card__title">{{recipe.name}}</h2> <div class="recipe-card__details-wrapper"> <p class="recipe-card__excerpt">{{recipe.body}}</p> <a v-bind:href="recipe.url" class="recipe-card__read-more">Read more <i class="fa fa-arrow-right"></i></a> </div> </div> </div> </div> </tab-content> </transition> </div> </article> </main>
3. 精简搜索组件
把搜索组件里的结果展示部分(<div class="container">及内部内容)删掉,只保留搜索输入框和按钮,避免重复渲染结果。
4. 合并后的根实例示例
new Vue({ el: '#app', data: { searchQuery: '', activeTab: 'Breakfast', // 你的默认激活标签 tabs: { // 你的标签数据 Breakfast: { recipes: [...] }, Salad: { recipes: [...] }, // ...其他标签 }, allRecipes: [] // 所有食谱合集,用于搜索过滤 }, computed: { filteredList() { if (!this.searchQuery) return []; const query = this.searchQuery.toLowerCase(); return this.allRecipes.filter(recipe => recipe.name.toLowerCase().includes(query) || recipe.body.toLowerCase().includes(query) ); } }, created() { // 把所有标签里的食谱合并到allRecipes this.allRecipes = Object.values(this.tabs).flatMap(tab => tab.recipes); }, methods: { setTabActive(tabName) { this.activeTab = tabName; } } });
内容的提问来源于stack exchange,提问作者IrinaPenzina
相关产品推荐
相关产品推荐

