简体   繁体   中英

Vue - Pass components to router-view on dynamically loaded component

I need to render a different layout for the same route for a specific URI with different components depending on the user being on mobile or in desktop.

I would like to avoid having route path checks in the PageCommon(layout component) to keep it clean.

The app has a main component taking care of the layout, it has different router-views where we load the different components for each page URI. This would be a normal route for that.

{
    path: '',
    component: PageCommon,
    children: [
      {
        path: '',
        name: 'Home',
        components: {
          default: Home,
          header: Header,
          'main-menu': MainMenu,
          'page-content': PageContent,
          footer: Footer,
          'content-footer': ContentFooter
        }
      },

I can't change the route components property once the component is loaded so I tried to make a wrapper and pass the components dynamically.

 {
    path: 'my-view',
    name: 'My_View',
    component: () => import('@/components/MyView/ViewWrapper')
  },

In /components/MyView/ViewWrapper'

    <page-common v-if="isMobile">
        <my-mobile-view is="default"></my-mobile-view>
        <main-menu is="main-menu"></main-menu>     
    </page-common>    
    <page-common v-else>
        <my-desktop-view is="default"></my-desktop-view>
        <header is="header"></header>    
        <main-menu is="main-menu"></main-menu>    
        <footer is="footer"></footer> 
    </page-common>    

</template>

I would expect that the components passed inside page-common block would be substituted on the appropriate, but is not how it works, and Vue just loads page-common component with empty router-views.

Is there any approach for this?

Note that I already tried using:is property for loading different components, but the problem then is on how to tell the parent to use this or that component for this page. This is the code for that:

<template>
    <component :is="myView"></component>    
</template>
<script>
import DesktopView from "@/components/MyView/DesktopView";
import MobileView from "@/components/MyView/MobileView";
export default {
    name: 'MyView', 
    components: {
        DesktopView,
        MobileView,
    },
    data(){
        return {
            myView: null,
            isMobile: this.detectMobile()
        }
    },
    methods : {
        getViewComponent() {
            return this.isMobile ? 'mobile-view' : 'desktop-view';
        }
    },
    created() {
        this.myView = this.getViewComponent();
    }
}
</script>

I could use this approach for each of the PageCommon router views, creating a component for each that does the above, but it looks like a very bad solution.

A computed method is all you need.

You should have this top level Logic in App.vue and the <router-view> should be placed in both DesktopView and MobileView .

// App.vue

<template>

    <component :is="myView"></component>   
 
</template>

<script>

import DesktopView from "@/components/MyView/DesktopView";
import MobileView from "@/components/MyView/MobileView";

export default {

    name: 'MyView', 

    components: {
        DesktopView,
        MobileView,
    },

    computed: {
     
      myView() {

        return this.detectMobile() ? 'mobile-view' : 'desktop-view';

      }

   }

}
</script>

You may also want to consider code splitting by setting up Dynamic Components for those layouts since Mobile will load Desktop View because it is compiled into final build, register them globally as dynamic imports instead if importing them in MyView and then delete components also after doing the following instead, this way only the one that is needed will be downloaded saving mobile users their bandwidth:

// main.js

import LoadingDesktopComponent from '@/components/LoadingDesktopComponent '

Vue.componenet('desktop-view', () => ({
  component: import('@/components/MyView/DesktopView'),
  loading: LoadingDesktopComponent // Displayed while loading the Desktop View
})

// LoadingDesktopComponent .vue

<template>

  <div>
    Optimizing for Desktop, Please wait.
  </div>

</template>

<script>

export default {

  name: 'loading-component'

}

</script>

Routing logic will only be processed when <router-view> is available,this means you can delay the presentation of Vue Router, for example you can have :is show a splash screen like a loading screen on any URI before displaying a component in :is that contains <router-view> , only than at that point will the URI be processed to display the relevant content.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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