从 vue-router 调用后 Vue 3 组件未更新
Vue 3 component not updating after call from vue-router
我想用 ionic-vue 构建一个待办事项应用程序。它目前使用 vue 3。
我有这个概述(称为 Lists.vue),可以在其中单击多个列表(每个列表应加载任务)。但是,每次我点击一个列表时,都会出现相同的数据!就好像组件被重用但没有重新渲染/更新。
我尝试了各种解决方案。其中之一是在正在更改的裁判上应用手表,但是,没关系,最终结果保持不变。也试过给 router-link :key,还是不行。
我的Lists.vue
<ion-page>
<ion-content v-if="chunk" class="flex flex-col overflow-auto ion-align-self-center content-wrapper">
<ion-toolbar class="mt-2">
<h1 class="text-4xl pl-5 font-semibold">Lijsten</h1>
</ion-toolbar>
<div v-for="(categoryChunk, index) in chunk.value" :key="index" class="flex flex-wrap w-full flex-row justify-around mt-2">
<div v-for="category in categoryChunk" :key="category.id">
<ion-card class='w-40 sm:w-80'>
<router-link :to="{ name: 'Index', params: {categoryId: category.id} }">
<ion-card-header class="flex">
<ion-icon class="mt-5 text-4xl" color="orange-secondary" :icon="allIcons[category.icon]"></ion-icon>
<div class="m-4">
<p><b>{{ category.title }}</b></p>
<p>Taken: {{ category.tasks }}</p>
</div>
</ion-card-header>
<ion-card-content><div class="line-vert"></div></ion-card-content>
</router-link>
</ion-card>
</div>
</div>
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button color="orange-secondary" @click="setOpen(true)">
<ion-icon class="text-4xl" color="light" :icon="allIcons.add"></ion-icon>
</ion-fab-button>
</ion-fab>
<ion-modal
:is-open="isOpenRef"
css-class="my-custom-class"
>
<create-list v-on:on-close="setOpen(false)"></create-list>
</ion-modal>
</ion-page>
</template>
<script>
import { defineComponent, computed, ref, watch, onBeforeMount } from "vue";
import {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
IonSpinner,
} from '@ionic/vue'
import * as allIcons from 'ionicons/icons'
import getCollection from "../../composables/getCollection"
import CreateList from './CreateList'
export default defineComponent ({
components: {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
CreateList,
IonSpinner,
},
setup() {
const { loadCollection } = getCollection();
const chunk = ref()
// Zet modal open/dicht
const isOpenRef = ref(false);
const setOpen = (state) => isOpenRef.value = state;
// Laad alle categorieën uit de database
const reload = () => {
loadCollection('categories').then(data => {
chunk.value = computed(() => {
// Zet de items uit de database om in delen van twee.
const array = [];
const size = 2;
for(let i = 0; i < data.length; i += size) {
array.push(data.slice(i, i+size));
}
return array;
})
})
}
onBeforeMount(() => {
reload();
})
watch(isOpenRef, () =>{
reload()
})
return {
allIcons,
chunk,
isOpenRef,
setOpen,
}
}
})
</script>
我的列表名为 Index.vue(也许我应该将其命名为 list.vue 或其他...)
<template>
<ion-page>
<ion-content v-if="category">
<ion-toolbar>
<div class="flex justify-between">
<h1 class="font-light pl-5">{{ category.title }}</h1>
<ion-icon class="text-2xl pr-5" :icon="closeOutline" @click="redirectBack()"></ion-icon>
</div>
</ion-toolbar>
{{ category }}
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
</ion-page>
</template>
<script>
import { defineComponent, ref } from "vue";
import { closeOutline } from 'ionicons/icons'
import {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner,
} from '@ionic/vue'
import { useRoute, useRouter } from "vue-router";
import getValue from "@/composables/getValue";
export default defineComponent ({
components: {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner
},
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
return {
closeOutline,
redirectBack,
category,
}
}
})
</script>
我的可组合函数:
import {ref } from "@vue/reactivity";
import { todoFirestore } from "../firebase/config";
const getValue = () => {
const error = ref(null);
const loadValue = async (collectionName: string, id : string) => {
try {
let res = await todoFirestore.collection(collectionName).doc(id)
.get();
if (!res.exists) {
throw Error('Lijst bestaat niet.');
}
return { ...res.data(), id: res.id }
}
catch (err) {
error.value = err.message
}
}
return { error , loadValue }
}
export default getValue;
如果有人知道任何可能的解决方案,或者我可能做错了什么,请帮忙!非常感谢所有解决方案。
PS: 由于情况,我目前不能很快回复,但我向你保证,我会回复你的答案:)
找到了我的问题的答案!我必须在 loadValue 方法上使用 watchEffect 才能从数据库中调用数据。看起来Vue(在互联网上做了一些研究)希望重用组件而不是重新渲染它们,这样效率更高。
路由参数正在更新,但是组件的密钥没有。
Index.vue(任务列表)上的设置函数
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
const getCategory = () => {
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
}
watchEffect(() => {
getCategory()
})
return {
closeOutline,
redirectBack,
category,
}
我想用 ionic-vue 构建一个待办事项应用程序。它目前使用 vue 3。 我有这个概述(称为 Lists.vue),可以在其中单击多个列表(每个列表应加载任务)。但是,每次我点击一个列表时,都会出现相同的数据!就好像组件被重用但没有重新渲染/更新。
我尝试了各种解决方案。其中之一是在正在更改的裁判上应用手表,但是,没关系,最终结果保持不变。也试过给 router-link :key,还是不行。
我的Lists.vue
<ion-page>
<ion-content v-if="chunk" class="flex flex-col overflow-auto ion-align-self-center content-wrapper">
<ion-toolbar class="mt-2">
<h1 class="text-4xl pl-5 font-semibold">Lijsten</h1>
</ion-toolbar>
<div v-for="(categoryChunk, index) in chunk.value" :key="index" class="flex flex-wrap w-full flex-row justify-around mt-2">
<div v-for="category in categoryChunk" :key="category.id">
<ion-card class='w-40 sm:w-80'>
<router-link :to="{ name: 'Index', params: {categoryId: category.id} }">
<ion-card-header class="flex">
<ion-icon class="mt-5 text-4xl" color="orange-secondary" :icon="allIcons[category.icon]"></ion-icon>
<div class="m-4">
<p><b>{{ category.title }}</b></p>
<p>Taken: {{ category.tasks }}</p>
</div>
</ion-card-header>
<ion-card-content><div class="line-vert"></div></ion-card-content>
</router-link>
</ion-card>
</div>
</div>
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button color="orange-secondary" @click="setOpen(true)">
<ion-icon class="text-4xl" color="light" :icon="allIcons.add"></ion-icon>
</ion-fab-button>
</ion-fab>
<ion-modal
:is-open="isOpenRef"
css-class="my-custom-class"
>
<create-list v-on:on-close="setOpen(false)"></create-list>
</ion-modal>
</ion-page>
</template>
<script>
import { defineComponent, computed, ref, watch, onBeforeMount } from "vue";
import {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
IonSpinner,
} from '@ionic/vue'
import * as allIcons from 'ionicons/icons'
import getCollection from "../../composables/getCollection"
import CreateList from './CreateList'
export default defineComponent ({
components: {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
CreateList,
IonSpinner,
},
setup() {
const { loadCollection } = getCollection();
const chunk = ref()
// Zet modal open/dicht
const isOpenRef = ref(false);
const setOpen = (state) => isOpenRef.value = state;
// Laad alle categorieën uit de database
const reload = () => {
loadCollection('categories').then(data => {
chunk.value = computed(() => {
// Zet de items uit de database om in delen van twee.
const array = [];
const size = 2;
for(let i = 0; i < data.length; i += size) {
array.push(data.slice(i, i+size));
}
return array;
})
})
}
onBeforeMount(() => {
reload();
})
watch(isOpenRef, () =>{
reload()
})
return {
allIcons,
chunk,
isOpenRef,
setOpen,
}
}
})
</script>
我的列表名为 Index.vue(也许我应该将其命名为 list.vue 或其他...)
<template>
<ion-page>
<ion-content v-if="category">
<ion-toolbar>
<div class="flex justify-between">
<h1 class="font-light pl-5">{{ category.title }}</h1>
<ion-icon class="text-2xl pr-5" :icon="closeOutline" @click="redirectBack()"></ion-icon>
</div>
</ion-toolbar>
{{ category }}
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
</ion-page>
</template>
<script>
import { defineComponent, ref } from "vue";
import { closeOutline } from 'ionicons/icons'
import {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner,
} from '@ionic/vue'
import { useRoute, useRouter } from "vue-router";
import getValue from "@/composables/getValue";
export default defineComponent ({
components: {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner
},
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
return {
closeOutline,
redirectBack,
category,
}
}
})
</script>
我的可组合函数:
import {ref } from "@vue/reactivity";
import { todoFirestore } from "../firebase/config";
const getValue = () => {
const error = ref(null);
const loadValue = async (collectionName: string, id : string) => {
try {
let res = await todoFirestore.collection(collectionName).doc(id)
.get();
if (!res.exists) {
throw Error('Lijst bestaat niet.');
}
return { ...res.data(), id: res.id }
}
catch (err) {
error.value = err.message
}
}
return { error , loadValue }
}
export default getValue;
如果有人知道任何可能的解决方案,或者我可能做错了什么,请帮忙!非常感谢所有解决方案。
PS: 由于情况,我目前不能很快回复,但我向你保证,我会回复你的答案:)
找到了我的问题的答案!我必须在 loadValue 方法上使用 watchEffect 才能从数据库中调用数据。看起来Vue(在互联网上做了一些研究)希望重用组件而不是重新渲染它们,这样效率更高。
路由参数正在更新,但是组件的密钥没有。
Index.vue(任务列表)上的设置函数
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
const getCategory = () => {
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
}
watchEffect(() => {
getCategory()
})
return {
closeOutline,
redirectBack,
category,
}