简体   繁体   English

Vue-router 重新加载组件

[英]Vue-router reload components

I have a few routes that each load 3 components.我有几条路线,每条路线加载 3 个组件。 Two of the components are the same on all routes.其中两个组件在所有路线上都是相同的。 When I move between those routes I want to pass new data and on some init event of the component I want to fill the data of that component so it reflects on the UI.当我在这些路由之间移动时,我想传递新数据,并在组件的某些初始化事件上填充该组件的数据,以便它反映在 UI 上。 Also I want to retrigger bootstrap animations of components being loaded.我还想重新触发正在加载的组件的引导动画。 How would I go about doing that.我 go 如何做到这一点。 Because right now, I don't know where in the component lifecycle would I fetch the data and rerender the component with this new data.因为现在,我不知道在组件生命周期的哪个位置获取数据并使用这些新数据重新呈现组件。 Concretly in myapps/1 and /newapp/ I have a main view component and a sidebar component.具体来说,在 myapps/1 和 /newapp/ 中,我有一个主视图组件和一个侧边栏组件。 In the /newapp/ URL I want all icons on the sidebar to be red (nothing on the main view has been filled) and the main view should be empty.在 /newapp/ URL 中,我希望侧边栏上的所有图标都是红色的(主视图上没有任何内容被填充)并且主视图应该是空的。 On the myapps/1 I want to load the data from the server into the main view and if it's all filled I want icons on the sidebar to be green.在 myapps/1 上,我想将数据从服务器加载到主视图中,如果它全部填满,我希望侧边栏上的图标为绿色。 What now happens I load myapps/1, click on the second item in the sidebar and the main view changes to the second view.现在发生了什么 我加载 myapps/1,单击侧边栏中的第二个项目,主视图更改为第二个视图。 Then I router.push("/newapp/); and sidebar stays on the second item and second main view. So router.push("/myapps/"); doesn't reload my sidebar and my main view.然后我 router.push("/newapp/"); 侧边栏停留在第二个项目和第二个主视图上。所以 router.push("/myapps/"); 不会重新加载我的侧边栏和主视图。

EDIT:编辑:

Here you see my routes, sidebars and default are the same.在这里你看到我的路线,侧边栏和默认是一样的。

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,
    }
  }
];

This is my ProjectMain.vue这是我的 ProjectMain.vue

<template>
  <div class="wrapper wrapper-content">
    <component :is="currentProjectMain"></component>
  </div>
</template>

This is my router-view in index.html:这是我在 index.html 中的路由器视图:

<router-view name="sidebar"></router-view>

And I have another one in index.html, the default one:我在 index.html 中还有一个,默认的:

<router-view></router-view>

When I click on some item on Sidebar i emit event to the ProjectMain to switch out the component.当我单击 Sidebar 上的某个项目时,我会向 ProjectMain 发出事件以切换组件。 So like this: In Sidebar:所以像这样:在边栏中:

eventBus.$emit("currentProjectMainChanged", "appOverview");

or或者

eventBus.$emit("currentProjectMainChanged", "appSettings");

And than in ProjectMain:并且比在 ProjectMain 中:

eventBus.$on("currentProjectMainChanged", function(data) {
  if(data === "appOverview") {
    self.currentProjectMain = CurrentProjectAppOverview;
  }else if(data === "appSettings") {
    self.currentProjectMain = CurrentProjectSettings;
  }
});

If I got to "/myapps/:id".如果我到达“/myapps/:id”。 It loads the sidebar and ProjectMain and I get a little animation of the sidebar and the ProjectMain with this bootstrap classes: <div class="animated fadeInUp"> and both components got through the entire lifecycle.它加载了侧边栏和 ProjectMain,我得到了一些 animation 的侧边栏和带有这个引导类的 ProjectMain: <div class="animated fadeInUp">并且这两个组件都经历了整个生命周期。

By default appOverview is selected in sidebar and CurentProjectAppOverview.vue is loaded as a component in ProjectMain.默认情况下,侧边栏中的 appOverview 处于选中状态,CurentProjectAppOverview.vue 作为 ProjectMain 中的组件加载。 Than I click on appSettings in the sidebar and class is added to that item in the sidebar to mark it as selected and in the ProjectMain CurrentProjectSettings.vue is loaded as a component.然后我点击边栏中的 appSettings,class 被添加到边栏中的该项目以将其标记为已选中,并在 ProjectMain 中将 CurrentProjectSettings.vue 作为组件加载。

But then in the breadcrumbs I have a button to go to "/myapps/newversion/:id" Here is the problem.但是然后在面包屑中我有一个按钮到 go 到“/myapps/newversion/:id” 这就是问题所在。 When I click to go to "/myapps/newversion/:id" ( router.push("/myapps/newversion/" + id); ) the second item on the sidebar remains selected (appSettings) and in ProjectMain CurrentProjectSettings.vue remains loaded, and I don't get the bootstrap animation of the sidebar and ProjectMain, and the components don't go through their lifecycle.当我点击 go 到“/myapps/newversion/:id”( router.push("/myapps/newversion/" + id); )侧边栏上的第二个项目保持选中状态(appSettings)并且在 ProjectMain CurrentProjectSettings.vue 保持已加载,但我没有获得侧边栏和 ProjectMain 的引导程序 animation,并且组件在其生命周期中没有 go。

What I want here when I click the button to go to "/myapps/newversion/:id" is my bootstrap animation ( <div class="animated fadeInUp"> ), I want the Sidebar and ProjectMain to go through their entire lifecycle.当我点击按钮 go 到“/myapps/newversion/:id”时,我想要的是我的引导程序 animation ( <div class="animated fadeInUp"> ),我希望 Sidebar 和 ProjectMain 在整个生命周期内都为 go。 And I want the default item to be selected on the sidebar (so appOverview) and default component to be loaded in ProjectMain (so CurentProjectAppOverview.vue).我希望在侧边栏上选择默认项(因此是 appOverview),并在 ProjectMain 中加载默认组件(因此是 CurentProjectAppOverview.vue)。

There are colored buttons for the each item in the sidebar.边栏中的每个项目都有彩色按钮。 If I go to "/myapps/newversion/:id" from "/myapps/:id", I want to load the data from the server into CurrentProjectAppOverview.vue and if all data is filled I want the button on the sidebar to be green and if not it should be red.如果我 go 从“/myapps/:id”到“/myapps/newversion/:id”,我想将数据从服务器加载到 CurrentProjectAppOverview.vue,如果所有数据都已填充,我希望侧边栏上的按钮是绿色,如果不是,则应为红色。

So In short when moving between this two routes I want to be able to load data and fill the fields I want, and I want bootstrap animation and default views to be selected and loaded and they should go through their entire lifecycle.所以简而言之,当在这两条路线之间移动时,我希望能够加载数据并填写我想要的字段,我希望引导 animation 和默认视图被选择和加载,它们应该 go 通过他们的整个生命周期。 Now router just reuses them as they are.现在路由器只是按原样重用它们。

So something like "ReloadComponent: true", or destroy and recreate component.所以像“ReloadComponent:true”之类的东西,或者销毁并重新创建组件。

I could duplicate SidebarProject.vue and ProjectMain.vue and rename them and in each route load basically a copy but that would mean I have to write the same code in different.vue files.我可以复制 SidebarProject.vue 和 ProjectMain.vue 并重命名它们,并且在每个路由中基本上加载一个副本,但这意味着我必须在不同的 .vue 文件中编写相同的代码。

Before there was an option to set YouComponent.route.canReuse to false, which would do what I want, I think, but it is removed in the newest version.之前有一个选项可以将 YouComponent.route.canReuse 设置为 false,我认为这会做我想做的,但它在最新版本中被删除了。

I was facing the same problem. 我遇到了同样的问题。 I will quote a found answer, which is really simple and great. 我将引用一个找到的答案,这非常简单而且很棒。 The simplest solution is to add a :key attribute to : 最简单的解决方案是将:key属性添加到:

<router-view :key="$route.fullPath"></router-view>

This is because Vue Router does not notice any change if the same component is being addressed. 这是因为如果正在寻址相同的组件,Vue Router不会注意到任何变化。 With the key, any change to the path will trigger a reload of the component with the new data. 使用密钥,对路径的任何更改都将触发使用新数据重新加载组件。

Partially thanx to this: https://forum.vuejs.org/t/vue-router-reload-component/4429 部分地对此: https//forum.vuejs.org/t/vue-router-reload-component/4429

I've come up with a solution that works for me: 我想出了一个适合我的解决方案:

First I added a new function to jQuery which is used to retrigger bootstrap animation when the component loads. 首先,我向jQuery添加了一个新函数,用于在组件加载时重新触发bootstrap动画。

$.fn.redraw = function(){
    return $(this).each(function(){
        var redraw = this.offsetHeight;
    });
};

In component: 在组件中:

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");
            }
        }
}

I call the onCreated(); 我叫onCreated(); method when the component loads and when the route reloads or the ID of the same route changes but the component stays the same I just call the method again. 当组件加载时以及路由重新加载或同一路由的ID发生变化但组件保持不变时,我再次调用该方法。 This takes care of the new data. 这会处理新数据。

As for re triggering bootstrap animations I use redraw(); 至于重新触发bootstrap动画,我使用redraw(); function I add to jQuery right on the start of the app. 函数我在应用程序的开头添加到jQuery。 and this re triggers the animations on URL change: 这会重新触发URL更改的动画:

$("#breadcrumbsCurrentProject").find(".animated").removeClass("fadeIn").redraw().addClass("fadeIn");

It looks like you are using vue-router. 看起来你正在使用vue-router。

I don't have your code, but I can show you how I handled this. 我没有你的代码,但我可以告诉你我是如何处理的。 See named-views . 请参阅命名视图

Here is my router-map from my bundle.js file: 这是我的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},
        ],
    }
]
})

I have named-views of "default", "altside" and "toolbar" I am assigning a component to the named view for each path. 我有“默认”,“altside”和“工具栏”的命名视图我正在为每个路径的命名视图分配一个组件。

The second half of this is in your parent component where you assign the name 其后半部分位于您分配名称的父组件中

<router-view  name="toolbar"></router-View>   

Here is my parent template: 这是我的父模板:

<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>

So, what I've done is I've told the parent template to look at the named-view of the router-view. 所以,我所做的就是告诉父模板查看路由器视图的命名视图。 And based upon the path, pull the component I've designated in the router-map. 并根据路径,拉出我在路由器映射中指定的组件。

For my '/' path toolbar == the toolbar component but in the '/create' path toolbar = eventToolbar component. 对于我的'/'路径工具栏==工具栏组件,但在'/ create'路径工具栏= eventToolbar组件中。

The default component is dynamic, which allows me to use child components without swapping out the toolbar or altside components. 默认组件是动态的,这允许我使用子组件而无需交换工具栏或altside组件。

this is what I came up with: 这就是我提出的:

import ProjectMain from './templates/ProjectMain.vue';
import SidebarProject from './templates/SidebarProject.vue';

//now shallow copy the objects so the new object are treated as seperatete .vue files with: $.extend({}, OriginalObject); //现在浅层复制对象,以便将新对象视为seperatete .vue文件: $.extend({}, OriginalObject); //this is so I don't have to create basically the same .vue files but vue-router will reload those files because they are different //这样我就不必创建基本相同的.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
}
}
];

This basically tricks the vue-router into thinking he's loading different .vue components, but they are actually the same. 这基本上欺骗了vue-router,认为他正在加载不同的.vue组件,但它们实际上是相同的。 So I get everything I want: reloading of the component, animation, life cycle, and I don't have to write multiple similar or the same components. 所以我得到了我想要的一切:重新加载组件,动画,生命周期,而且我不必编写多个相似或相同的组件。 What do you think, I'm just not sure if this is the best practice. 您怎么看?我不确定这是否是最佳做法。

Sep, 2021 Update: 2021 年 9 月更新:

You can force-reload components by adding :key="$route.fullPath" .您可以通过添加:key="$route.fullPath"来强制重新加载组件。

For Child Component:对于组件:

<Child :key="$route.fullPath" />

For router-view tag:对于路由器视图标签:

<router-view :key="$route.fullPath" />

However, :key="$route.fullPath" only can force-reload the components of the different route but not the components of the same route.但是, :key="$route.fullPath"只能强制重新加载不同路由的组件,而不能强制重新加载同一路由的组件。 To be able to force-reload the components of the same route as well, we need to add a value to :key= and change the value.为了能够强制重新加载同一路由的组件,我们需要向:key=添加一个值并更改该值。

*We can assign Array to :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>

However, to keep using both $route.fullPath and value causes some error sometimes so only when some event like Click happens, we use both $route.fullPath and value .但是,为了继续同时使用$route.fullPathvalue有时会导致一些错误,因此只有在发生诸如Click 之类的事件时,我们才同时使用$route.fullPathvalue Except when some event like Click happens, we always need to use only $route.fullPath .除了像Click这样的事件发生时,我们总是只需要使用$route.fullPath

This is the final code:这是最终的代码:

<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>

Unfortunately, there are no simple ways to force-reload components properly in Vue.不幸的是,在 Vue 中没有简单的方法可以正确地强制重新加载组件。 That's the big problem of Vue for now.这是 Vue 目前的大问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM