J'ai une page où un ClientPortfolio (composant parent) contenant une liste de titres (composant enfant) est chargé dans une liste de v-data-table.
Le problème que j'ai, c'est que ClientPortfolio est entièrement rechargé chaque fois que je clique sur une sécurité de la liste, ce qui provoque l'actualisation de la liste entière, ce qui entraîne la réinitialisation du défilement et de la classe sélectionnée, ainsi que des frais généraux de performance inutiles. J'ai regardé la documentation de Vue et rien ne semble indiquer comment actualiser un composant enfant uniquement quand il a des paramètres, il semble que le composant parent soit actualisé car l'itinéraire change tous les fois qu'une sécurité est sélectionnée, malgré le fait de s'attendre à ce que Vue serait sachez que seul le sous (itinéraire imbriqué) est en train de changer, donc besoin de recharger uniquement le composant enfant
La réponse la plus proche que j'ai obtenue a été expliquée sur https://github.com/vuejs/vue-router/issues/2 qui n'explique pas dans le code comment y parvenir.
routes.js:
routes: [
{
path: '/client/:clientno/portfolios/:portfolioNo',
component: ClientPortfolios,
children: [
{ path: 'security/:securityNo', component: Security }
]
},
]
Lien du routeur dans ClientPortfolios.vue:
<router-link tag="tr" style="cursor:pointer"
:to="`/client/${$route.params.clientno}/portfolios/${selectedPortfolioSequenceNo}/security/${props.item.SecurityNo}-${props.item.SequenceNo}`"
:key="props.item.SecurityNo+props.item.SequenceNo">
</router-link>
Vue du routeur (pour le composant Sécurité) dans ClientPortfolios.vue:
<v-flex xs10 ml-2>
<v-layout>
<router-view :key="$route.fullPath"></router-view>
</v-layout>
</v-flex>
Tout indice sur la façon d'empêcher les parents de se recharger est apprécié.
EDIT: En essayant de se rapprocher du problème, je remarque que l'attr "Key" dans les portefeuilles clients change (comme indiqué dans la fenêtre de débogage Vue ci-dessus) chaque fois que je change la sécurité, est-ce que cela pourrait être le Existe-t-il un moyen d'affecter une clé au composant ClientPortfolios bien que ce ne soit pas un enfant? Ou un moyen de ne pas mettre à jour sa clé lors de la navigation vers différents titres?
MISE À JOUR: Code complet
ClientPortfolios.vue
<template>
<v-layout row fill-height>
<v-flex xs2>
<v-layout column class="ma-0 pa-0 elevation-1">
<v-flex>
<v-select v-model="selectedPortfolioSequenceNo" :items="clientPortfolios" box label="Portfolio"
item-text="SequenceNo" item-value="SequenceNo" v-on:change="changePortfolio">
</v-select>
</v-flex>
<v-data-table disable-initial-sort :items="securities" item-key="Id" hide-headers hide-actions
style="overflow-y: auto;display:block;height: calc(100vh - 135px);">
<template slot="items" slot-scope="props">
<router-link tag="tr" style="cursor:pointer"
:to="{ name: 'Security', params: { securityNo: props.item.SecurityNo+'-'+props.item.SequenceNo } }"
>
</router-link>
</template>
<template v-slot:no-data>
<v-flex class="text-xs-center">
No securities found
</v-flex>
</template>
</v-data-table>
</v-layout>
</v-flex>
<v-flex xs10 ml-2>
<v-layout>
<keep-alive>
<router-view></router-view>
</keep-alive>
</v-layout>
</v-flex>
</v-layout>
</template>
<script>
import Security from '@/components/Security'
export default {
components: {
security: Security
},
data () {
return {
portfoliosLoading: false,
selectedPortfolioSequenceNo: this.$route.params.portfolioNo,
selectedPortfolio: null,
securityNo: this.$route.params.securityNo
}
},
computed: {
clientPortfolios () {
return this.$store.state.ClientPortfolios
},
securities () {
if (this.clientPortfolios == null || this.clientPortfolios.length < 1) {
return []
}
let self = this
this.selectedPortfolio = global.jQuery.grep(this.clientPortfolios, function (portfolio, i) {
return portfolio.SequenceNo === self.selectedPortfolioSequenceNo
})[0]
return this.selectedPortfolio.Securities
}
},
mounted () {
this.getClientPortfolios()
},
activated () {
},
methods: {
changePortfolio () {
this.$router.Push({
path: '/client/' + this.$route.params.clientno + '/portfolios/' + this.selectedPortfolioSequenceNo
})
},
getClientPortfolios: function () {
this.portfoliosLoading = true
let self = this
this.$store.dispatch('getClientPortfolios', {
clientNo: this.$route.params.clientno
}).then(function (serverResponse) {
self.portfoliosLoading = false
})
}
}
}
</script>
Security.vue
<template>
<v-flex>
<v-layout class="screen-header">
<v-flex class="screen-title">Security Details </v-flex>
</v-layout>
<v-divider></v-divider>
<v-layout align-center justify-space-between row class="contents-placeholder" mb-3 pa-2>
<v-layout column>
<v-flex class="form-group" id="security-portfolio-selector">
<label class="screen-label">Sequence</label>
<span class="screen-value">{{security.SequenceNo}}</span>
</v-flex>
<v-flex class="form-group">
<label class="screen-label">Security</label>
<span class="screen-value">{{security.SecurityNo}}-{{security.SequenceNo}}</span>
</v-flex>
<v-flex class="form-group">
<label class="screen-label">Status</label>
<span class="screen-value-code" v-if="security.Status !== ''">{{security.Status}}</span>
</v-flex>
</v-layout>
</v-layout>
</v-flex>
</template>
<script>
export default {
props: ['securityNo'],
data () {
return {
clientNo: this.$route.params.clientno,
securityDetailsLoading: false
}
},
computed: {
security () {
return this.$store.state.SecurityDetails
}
},
created () {
if (this.securityNo.length > 1) {
this.getSecurityDetails()
}
},
methods: {
getSecurityDetails: function () {
let self = this
this.securityDetailsLoading = true
this.$store.dispatch('getSecurityDetails', {
securityNo: this.securityNo,
clientNo: this.clientNo
}).then(function (serverResponse) {
self.securityDetailsLoading = false
})
}
}
}
</script>
router.js
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
component: Dashboard
},
{
path: '/client/:clientno/details',
component: Client,
props: true
},
{
path: '/client/:clientno/portfolios/:portfolioNo',
component: ClientPortfolios,
name: 'ClientPortfolios',
children: [
{ path: 'security/:securityNo',
component: Security,
name: 'Security'
}
]
}
]
})
[~ # ~] mise à jour [~ # ~] :
Juste pour mettre à jour cela depuis un certain temps, j'ai finalement pu découvrir quel est le problème, ce qui est indiqué par @matpie ailleurs, j'ai découvert que mon App.vue est le coupable où il y a une touche: ajouter au racine de l'application: <router-view :key="$route.fullPath" />
il s'agit d'un modèle que j'ai utilisé quelque part, mais je n'ai jamais dû le regarder car il "fonctionnait", après avoir retiré la clé, tout fonctionne comme il se doit, marquant la réponse matpie acceptée.
La prévention du rechargement des composants est le comportement par défaut dans Vue.js. Le système de réactivité de Vue mappe automatiquement les dépendances des propriétés et n'effectue que le minimum de travail pour s'assurer que le DOM est à jour.
En utilisant un :key
attribut n'importe où, vous dites à Vue.js que cet élément ou composant doit niquement correspondre lorsque les clés correspondent. Si les clés ne correspondent pas, l'ancienne sera détruite et une nouvelle sera créée.
Il semble que vous récupériez également des paramètres de route sur l'objet de données (Security.vue
). Ceux-ci ne seront pas mis à jour lorsque les paramètres de l'itinéraire changent, vous devez les insérer dans une propriété calculée afin qu'ils restent toujours à jour.
export default {
computed: {
clientNo: (vm) => vm.$route.params.clientno,
}
}
Cela garantira que clientNo
correspond toujours à ce qui se trouve dans le routeur, indépendamment du fait que Vue décide de réutiliser cette instance de composant. Si vous devez effectuer d'autres effets secondaires lorsque clientNo
change, vous pouvez ajouter un observateur:
vm.$watch("clientNo", (clientNo) => { /* ... */ })
Pourriez-vous s'il vous plaît vérifier à nouveau après avoir supprimé l'enregistrement local du composant de sécurité? Comme ce n'est pas nécessaire car il est géré par le routeur vue lui-même.
components: { // delete this code
security: Security
},