[英]Vue-router reload components
我有几条路线,每条路线加载 3 个组件。 其中两个组件在所有路线上都是相同的。 当我在这些路由之间移动时,我想传递新数据,并在组件的某些初始化事件上填充该组件的数据,以便它反映在 UI 上。 我还想重新触发正在加载的组件的引导动画。 我 go 如何做到这一点。 因为现在,我不知道在组件生命周期的哪个位置获取数据并使用这些新数据重新呈现组件。 具体来说,在 myapps/1 和 /newapp/ 中,我有一个主视图组件和一个侧边栏组件。 在 /newapp/ URL 中,我希望侧边栏上的所有图标都是红色的(主视图上没有任何内容被填充)并且主视图应该是空的。 在 myapps/1 上,我想将数据从服务器加载到主视图中,如果它全部填满,我希望侧边栏上的图标为绿色。 现在发生了什么 我加载 myapps/1,单击侧边栏中的第二个项目,主视图更改为第二个视图。 然后我 router.push("/newapp/"); 侧边栏停留在第二个项目和第二个主视图上。所以 router.push("/myapps/"); 不会重新加载我的侧边栏和主视图。
编辑:
在这里你看到我的路线,侧边栏和默认是一样的。
const routes = [
{
path: "/myapps/newproject",
components: {
default: ProjectMain,
"breadcrumbs": BreadcrumbsNewProject,
"sidebar": SidebarProject,
}
},
{
path: "/myapps/:id",
components: {
default: ProjectMain,
"breadcrumbs": BreadcrumbsCurrentProject,
"sidebar": SidebarProject,
}
},
{
path: "/myapps/newversion/:id",
components: {
default: ProjectMain,
"breadcrumbs": BreadcrumbsNewVersion,
"sidebar": SidebarProject,
}
}
];
这是我的 ProjectMain.vue
<template>
<div class="wrapper wrapper-content">
<component :is="currentProjectMain"></component>
</div>
</template>
这是我在 index.html 中的路由器视图:
<router-view name="sidebar"></router-view>
我在 index.html 中还有一个,默认的:
<router-view></router-view>
当我单击 Sidebar 上的某个项目时,我会向 ProjectMain 发出事件以切换组件。 所以像这样:在边栏中:
eventBus.$emit("currentProjectMainChanged", "appOverview");
或者
eventBus.$emit("currentProjectMainChanged", "appSettings");
并且比在 ProjectMain 中:
eventBus.$on("currentProjectMainChanged", function(data) {
if(data === "appOverview") {
self.currentProjectMain = CurrentProjectAppOverview;
}else if(data === "appSettings") {
self.currentProjectMain = CurrentProjectSettings;
}
});
如果我到达“/myapps/:id”。 它加载了侧边栏和 ProjectMain,我得到了一些 animation 的侧边栏和带有这个引导类的 ProjectMain: <div class="animated fadeInUp">
并且这两个组件都经历了整个生命周期。
默认情况下,侧边栏中的 appOverview 处于选中状态,CurentProjectAppOverview.vue 作为 ProjectMain 中的组件加载。 然后我点击边栏中的 appSettings,class 被添加到边栏中的该项目以将其标记为已选中,并在 ProjectMain 中将 CurrentProjectSettings.vue 作为组件加载。
但是然后在面包屑中我有一个按钮到 go 到“/myapps/newversion/:id” 这就是问题所在。 当我点击 go 到“/myapps/newversion/:id”( router.push("/myapps/newversion/" + id);
)侧边栏上的第二个项目保持选中状态(appSettings)并且在 ProjectMain CurrentProjectSettings.vue 保持已加载,但我没有获得侧边栏和 ProjectMain 的引导程序 animation,并且组件在其生命周期中没有 go。
当我点击按钮 go 到“/myapps/newversion/:id”时,我想要的是我的引导程序 animation ( <div class="animated fadeInUp">
),我希望 Sidebar 和 ProjectMain 在整个生命周期内都为 go。 我希望在侧边栏上选择默认项(因此是 appOverview),并在 ProjectMain 中加载默认组件(因此是 CurentProjectAppOverview.vue)。
边栏中的每个项目都有彩色按钮。 如果我 go 从“/myapps/:id”到“/myapps/newversion/:id”,我想将数据从服务器加载到 CurrentProjectAppOverview.vue,如果所有数据都已填充,我希望侧边栏上的按钮是绿色,如果不是,则应为红色。
所以简而言之,当在这两条路线之间移动时,我希望能够加载数据并填写我想要的字段,我希望引导 animation 和默认视图被选择和加载,它们应该 go 通过他们的整个生命周期。 现在路由器只是按原样重用它们。
所以像“ReloadComponent:true”之类的东西,或者销毁并重新创建组件。
我可以复制 SidebarProject.vue 和 ProjectMain.vue 并重命名它们,并且在每个路由中基本上加载一个副本,但这意味着我必须在不同的 .vue 文件中编写相同的代码。
之前有一个选项可以将 YouComponent.route.canReuse 设置为 false,我认为这会做我想做的,但它在最新版本中被删除了。
我遇到了同样的问题。 我将引用一个找到的答案,这非常简单而且很棒。 最简单的解决方案是将:key属性添加到:
<router-view :key="$route.fullPath"></router-view>
这是因为如果正在寻址相同的组件,Vue Router不会注意到任何变化。 使用密钥,对路径的任何更改都将触发使用新数据重新加载组件。
部分地对此: https : //forum.vuejs.org/t/vue-router-reload-component/4429
我想出了一个适合我的解决方案:
首先,我向jQuery添加了一个新函数,用于在组件加载时重新触发bootstrap动画。
$.fn.redraw = function(){
return $(this).each(function(){
var redraw = this.offsetHeight;
});
};
在组件中:
export default{
created:function(){
this.onCreated();
},
watch: {
'$route' (to, from) {
//on route change re run: onCreated
this.onCreated();
//and trigger animations again
$("#breadcrumbsCurrentProject").find(".animated").removeClass("fadeIn").redraw().addClass("fadeIn");
}
}
}
我叫onCreated();
当组件加载时以及路由重新加载或同一路由的ID发生变化但组件保持不变时,我再次调用该方法。 这会处理新数据。
至于重新触发bootstrap动画,我使用redraw();
函数我在应用程序的开头添加到jQuery。 这会重新触发URL更改的动画:
$("#breadcrumbsCurrentProject").find(".animated").removeClass("fadeIn").redraw().addClass("fadeIn");
看起来你正在使用vue-router。
我没有你的代码,但我可以告诉你我是如何处理的。 请参阅命名视图 。
这是我的bundle.js文件中的路由器映射:
const router = new VueRouter({
routes: [
{ path: '/',
components: {
default: dashboard,
altside: altSide,
toolbar: toolbar
},
},
{ path: '/create',
components: {
default: create,
altside: altSide,
toolbar: toolbar
},
},
{ path: '/event/:eventId',
name: 'eventDashboard',
components: {
default: eventDashboard,
altside: altSide,
toolbar: eventToolbar,
},
children: [
{ path: '/', name: 'event', component: event },
{ path: 'tickets', name: 'tickets', component: tickets},
{ path: 'edit', name: 'edit', component: edit },
{ path: 'attendees', name: 'attendees', component: attendees},
{ path: 'orders', name: 'orders', component: orders},
{ path: 'uploadImage', name: 'upload', component: upload},
{ path: 'orderDetail', name: 'orderDetail', component: orderDetail},
{ path: 'checkin', name: 'checkin', component: checkin},
],
}
]
})
我有“默认”,“altside”和“工具栏”的命名视图我正在为每个路径的命名视图分配一个组件。
其后半部分位于您分配名称的父组件中
<router-view name="toolbar"></router-View>
这是我的父模板:
<template>
<router-view class="view altside" name="altside"></router-view>
<router-view class="view toolbar" name="toolbar"></router-view>
<router-view class="view default"></router-view>
</template>
所以,我所做的就是告诉父模板查看路由器视图的命名视图。 并根据路径,拉出我在路由器映射中指定的组件。
对于我的'/'路径工具栏==工具栏组件,但在'/ create'路径工具栏= eventToolbar组件中。
默认组件是动态的,这允许我使用子组件而无需交换工具栏或altside组件。
这就是我提出的:
import ProjectMain from './templates/ProjectMain.vue';
import SidebarProject from './templates/SidebarProject.vue';
//现在浅层复制对象,以便将新对象视为seperatete .vue文件: $.extend({}, OriginalObject);
//这样我就不必创建基本相同的.vue文件,但vue-router会重新加载这些文件,因为它们不同
const ProjectMainA = $.extend({}, ProjectMain);
const ProjectMainB = $.extend({}, ProjectMain);
const ProjectMainC = $.extend({}, ProjectMain);
const SidebarProjectA = $.extend({}, SidebarProject);
const SidebarProjectB = $.extend({}, SidebarProject);
const SidebarProjectC = $.extend({}, SidebarProject);
const routes = [
{
path: "/myapps/newproject",
components: {
default: ProjectMainA,
"breadcrumbs": BreadcrumbsNewProject,
"sidebar": SidebarProjectA
}
},
{
path: "/myapps/:id",
components: {
default: ProjectMainB,
"breadcrumbs": BreadcrumbsCurrentProject,
"sidebar": SidebarProjectB
}
},
{
path: "/myapps/newversion/:id",
components: {
default: ProjectMainC,
"breadcrumbs": BreadcrumbsNewVersion,
"sidebar": SidebarProjectC
}
}
];
这基本上欺骗了vue-router,认为他正在加载不同的.vue组件,但它们实际上是相同的。 所以我得到了我想要的一切:重新加载组件,动画,生命周期,而且我不必编写多个相似或相同的组件。 您怎么看?我不确定这是否是最佳做法。
您可以通过添加:key="$route.fullPath"来强制重新加载组件。
对于子组件:
<Child :key="$route.fullPath" />
对于路由器视图标签:
<router-view :key="$route.fullPath" />
但是, :key="$route.fullPath"只能强制重新加载不同路由的组件,而不能强制重新加载同一路由的组件。 为了能够强制重新加载同一路由的组件,我们需要向:key=添加一个值并更改该值。
*我们可以将Array分配给:key= 。
<template>
<Child
:key="[$route.fullPath, value]" // Can assign "Array" to ":key="
@childReload="reload" // Call @click="$emit('childReload')" in
/> // Child Component to increment the value.
</template>
OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR
<template>
<router-view
:key="[$route.fullPath, value]" // Can assign "Array" to ":key="
@routerViewReload="reload" // Call @click="$emit('routerViewReload')"
/> // in Child Component to increment the value.
</template>
<script>
export default {
name: "Parent", components: { Child, },
data() {
return {
value: 0,
};
},
methods: {
reload() {
this.value++;
}
}
}
</script>
但是,为了继续同时使用$route.fullPath和value有时会导致一些错误,因此只有在发生诸如Click 之类的事件时,我们才同时使用$route.fullPath和value 。 除了像Click这样的事件发生时,我们总是只需要使用$route.fullPath 。
<template>
<Child
:key="state ? $route.fullPath : [$route.fullPath, value]"
@childReload="reload" // Call @click="$emit('childReload')" in
/> // Child Component to increment the value.
</template>
OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR OR
<template>
<router-view
:key="state ? $route.fullPath : [$route.fullPath, value]"
@routerViewReload="reload" // Call @click="$emit('routerViewReload')" in
/> // Child Component to increment the value.
</template>
<script>
export default {
name: "Parent", components: { Child, },
data() {
return {
state: true,
value: 0,
};
},
methods: {
reload() {
this.state = false;
this.value++;
this.$nextTick(() => this.state = true);
}
}
}
</script>
不幸的是,在 Vue 中没有简单的方法可以正确地强制重新加载组件。 这是 Vue 目前的大问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.