【问题标题】:Vue-router reload componentsVue-router 重载组件
【发布时间】:2017-05-09 09:40:30
【问题描述】:

我有几条路线,每条路线都加载 3 个组件。其中两个组件在所有路线上都是相同的。 当我在这些路由之间移动时,我想传递新数据,并且在组件的某些初始化事件上,我想填充该组件的数据,以便它反映在 UI 上。 我还想重新触发正在加载的组件的引导动画。 我将如何去做。 因为现在,我不知道在组件生命周期的哪个位置获取数据并使用这些新数据重新渲染组件。 具体来说,在 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>

当我单击侧边栏上的某个项目时,我会向 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,我通过这个引导类获得了侧边栏和 ProjectMain 的一点动画: &lt;div class="animated fadeInUp"&gt;,这两个组件都经历了整个生命周期。

默认情况下,侧边栏中的 appOverview 被选中,并且 CurentProjectAppOverview.vue 作为组件加载到 ProjectMain 中。 然后我点击侧边栏中的 appSettings 并将类添加到侧边栏中的该项目以将其标记为已选中,并且在 ProjectMain CurrentProjectSettings.vue 中作为组件加载。

然后在面包屑中我有一个按钮可以转到“/myapps/newversion/:id” 这是问题所在。当我单击转到“/myapps/newversion/:id”(router.push("/myapps/newversion/" + id);)时,侧边栏上的第二项保持选中状态(appSettings)并且在 ProjectMain CurrentProjectSettings.vue 中保持加载状态,我没有得到引导动画侧边栏和 ProjectMain,并且组件不会经历它们的生命周期。

当我单击按钮转到“/myapps/newversion/:id”时,我想要的是我的引导动画 (&lt;div class="animated fadeInUp"&gt;),我希望侧边栏和 ProjectMain 经历它们的整个生命周期。我希望在侧边栏上选择默认项目(所以 appOverview),并在 ProjectMain 中加载默认组件(所以 CurentProjectAppOverview.vue)。

边栏中的每个项目都有彩色按钮。如果我从“/myapps/:id”转到“/myapps/newversion/:id”,我想将服务器中的数据加载到 CurrentProjectAppOverview.vue 中,如果所有数据都已填满,我希望侧边栏上的按钮是绿色,如果不是,它应该是红色的。

所以简而言之,当在这两条路线之间移动时,我希望能够加载数据并填充我想要的字段,并且我希望选择和加载引导动画和默认视图,并且它们应该经历它们的整个生命周期。现在路由器只是按原样重用它们。

类似“ReloadComponent: true”,或者销毁并重新创建组件。

我可以复制 SidebarProject.vue 和 ProjectMain.vue 并重命名它们,并且在每个路由中基本上加载一个副本,但这意味着我必须在不同的 .vue 文件中编写相同的代码。

之前有一个选项可以将 YouComponent.route.canReuse 设置为 false,我认为这会做我想做的事,但它在最新版本中被删除了。

【问题讨论】:

    标签: javascript vue.js vue-component vue-router reload


    【解决方案1】:

    这是我想出的:

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

    //现在浅拷贝对象,因此新对象被视为单独的 .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 组件,但它们实际上是相同的。所以我得到了我想要的一切:组件的重新加载、动画、生命周期,而且我不必编写多个相似或相同的组件。 你怎么看,我只是不确定这是否是最佳做法。

    【讨论】:

      【解决方案2】:

      2021 年 9 月更新:

      您可以通过添加 :key="$route.fullPath" 来强制重新加载组件。

      对于组件:

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

      对于router-view标签:

      <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.fullPathvalue 有时会导致一些错误,所以只有当像 Click 这样的事件发生时,我们同时使用 $route.fullPathvalue。除了像 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 的大问题。

      【讨论】:

        【解决方案3】:

        我也遇到了同样的问题。我将引用一个找到的答案,这真的很简单而且很棒。 最简单的解决方案是添加一个 :key 属性到 :

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

        这是因为如果同一组件被寻址,Vue Router 不会注意到任何变化。使用密钥,对路径的任何更改都将触发使用新数据重新加载组件。

        【讨论】:

        • 有趣的是,我仍在寻找自己的答案来解决新项目中的这个问题,即使它是如此简单。这东西应该在 Vue 的文档中,它是必不可少的必须知道的东西。
        • 这不是一个好的解决方法,因为 key 必须由 v-for 中的变量定义。
        • vue 路由器没有明确的方式来重新加载组件有点荒谬。有许多用例,例如当组件依赖于由路由更改设置的存储状态时。 beforeChange 中的逻辑混乱就是这样,一团糟,根本无法扩展......
        • 有时这还不够。有时您希望保持在同一路径上并重新加载组件,例如你想传入一个新的道具,然后重新加载一个组件。在这些情况下,我向 :key 添加了一个额外的变量,我可以随意更新它并在任何时候触发重新渲染: :key="$route.fullPath + $store.state.reloadMainComponentKey"
        【解决方案4】:

        部分感谢:https://forum.vuejs.org/t/vue-router-reload-component/4429

        我想出了一个适合我的解决方案:

        首先我在 jQuery 中添加了一个新函数,用于在组件加载时重新触发引导动画。

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

        当组件加载和路由重新加载或相同路由的 ID 更改但组件保持不变时,我调用 onCreated(); 方法,我只是再次调用该方法。这会处理新数据。

        至于重新触发引导动画,我使用redraw(); 函数,我在应用程序开始时添加到 jQuery。这会重新触发 URL 更改的动画:

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

        【讨论】:

        • 你可以做的另一件事是在你的组件上使用内置的 beforeRouteEnter 函数来拦截路由变化
        【解决方案5】:

        看起来你正在使用 vue-router。

        我没有你的代码,但我可以告诉你我是如何处理的。 See named-views.

        这是我的 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},
                ],
            }
        ]
        })
        

        我有“default”、“altside”和“toolbar”的命名视图,我正在为每个路径的命名视图分配一个组件。

        后半部分是在您分配名称的父组件中

        <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 组件。

        【讨论】:

          猜你喜欢
          • 2019-04-27
          • 2019-04-21
          • 2016-06-28
          • 2018-04-22
          • 2018-09-28
          • 2017-05-21
          • 2019-03-13
          • 2019-06-16
          • 1970-01-01
          相关资源
          最近更新 更多