Jorayen
Jorayen

Reputation: 1971

vue.js structure application layout

I wish to build my web application with the Holy Grail layout, with just one sidebar, and no footer. The sidebar will be used as a navigation bar, as well as for holding interactive option for content that will be displayed in the center of the layout, depending on the currently chose link. Meaning that, when choosing a link to navigate to in the navigation bar, it will affect the displayed content in the sidebar for custom interaction options, and the main content in the center.

For achieving this I have came up with couple of approaches:

  1. Approach 1:
    • Have one Vue instance
    • Have a Layout component creating our layout, with Header and Navbar for sub-components.
    • Our Vue instance will use a router with a path for each link present in the navigation sidebar
    • Our Vue instance will render one router-view component to display the current path component
    • Each route will display a component that its template is using our previously created Layout component and inject the appropriate custom navigation options, and main content using slots.

Layout component:

<template>
<div class="app-layout">
    <header-component></header-component>
    <div class="main">
      <navbar-component>
        <slot name="navigation-menu"></slot>
      </navbar-component>
      <main>
        <slot name="main-content"></slot>
      </main>
    </div>
  </div>
</template>

<script>
  import Header from './Header.vue';
  import Navbar from './Navbar.vue';
  export default {
    name: 'Layout',
    components: {
      'header-component': Header,
      'navbar-component': Navbar
    }
  };
</script>

<style lang="sass" rel="stylesheet/scss" scoped>
  some styling to achieve our layout for the present tags in the template
</style>

Header component:

<template>
  <header v-once class="header">
    <router-link to="/">
      Brand
    </router-link>
  </header>
</template>

<script>
  export default {
    name: 'Header'
  };
</script>

<style lang="sass" rel="stylesheet/scss" scoped>
  some styling to achieve our layout for the present tags in the template
</style>

Navbar component:

<template>
  <nav class="navigation">
    <div class="links">
      // iterate on some property of this component, and create a list of links
    </div>
    <div class="menu">
      <slot name="navigation-menu"></slot>
    </div>
  </nav>
</template>

<script>
  export default {
    name: 'Navbar'
  };
</script>

<style lang="sass" rel="stylesheet/scss" scoped>
  some styling to achieve our layout for the present tags in the template
</style>

Vue instance with routing:

import link1Component from './components/Link1ComponentUsingLayout.vue';
import link2Component from './components/Link2ComponentUsingLayout.vue';
import link3Component from './components/Link3ComponentUsingLayout.vue';

const router = new VueRouter({
  mode: 'history',
  routes: [
    {
      path: '/',
      redirect: '/link1'
    },
    {
      path: '/link1',
      name: 'link1',
      component: Link1ComponentUsingLayout
    },
    {
      path: '/link2',
      name: 'link2',
      component: Link2ComponentUsingLayout
    },
    {
      path: '/link3',
      name: 'link3',
      component: Link3ComponentUsingLayout
    }
  ]
});

export default new Vue({
  el: '#app',
  router,
  render: h => h('router-view')
});
  1. Approach 2:
    • Have two Vue instances
    • Have out layout as a static html inside index.html instead of a component
    • Each of the Vue instances will use a router (one for each), with a path for each link present in the navigation bar
    • One of the instances will be mounted on the html inside the navigation sidebar in our static html, and one inside the center content area
    • Each of the Vue instances will render one router-view component to display the current path component
  2. Approach 3:
    • Have one Vue instance
    • Our Vue instance will use a router with a path for each link present in the navigation sidebar
    • Or Vue instance will template will represent our layout and inside the navigation sidebar, and the center content area we'll have 2 router-view components
    • Our Vue instances will render both the navigation sidebar, and the center content are components for the current path
  3. Approach 4:
    • Same as Approach 3, except don't use router and use dynamic components to switch between the navigation sidebar, and the center content area components, whenever a link inside the navigation is clicked.

Now, I was wondering which approach would be the best and why? Also I would like to hear new approaches if you have any, and explanation for why they're better.

Upvotes: 13

Views: 5858

Answers (4)

Arnesfield
Arnesfield

Reputation: 508

I don't have an explanation if this would be better, but I would like to add another approach I've previously used.

Assume that your project needs multiple layouts such as a layout for the admin, a separate one for the end user, and so on. Although this doesn't seem to really fit your use case, you could consider it an addition.

Layout.vue

Your Layout will contain the components that will persist on any page under it.

<template>
  <div>
    <header-component />

    <div class="main">
      <navbar-component />

      <main>
        <!-- your child routes here -->
        <router-view />
      </main>
    </div>
  </div>
</template>

router.js

In your routes, you nest your components or views under Layout.vue like so:

routes: [
  {
    path: '',
    component: Layout,
    // child routes now have the style of Layout
    children: [
      {
        path: '/page1',
        name: 'Page1Name',
        component: Page1
      },
      // ...
    ]
  }
]

Child components of Layout.vue are shown in its <router-view />.

NavComponent.vue

<template>
  <nav class="navigation">
    <!-- links and other stuff -->

    <!-- your custom interactive options below -->

    <div v-if="isInRoutes('Page1Name')">
      <!-- div or some component for Page1 -->
    </div>
  </nav>
</template>

wherein isInRoutes method checks for this.$route.name or it can be more complex than that.

Your App.vue (the one you'll render with h(App)) will also contain a <router-view /> of its own so it will show the layouts.

App.vue

<template>
  <div>
    <router-view />
  </div>
</template>

With this approach, you can create multiple layouts and nest your components under a particular layout using your routes.

Upvotes: 1

gao.xiangyang
gao.xiangyang

Reputation: 91

I think you can refer to the more mature code.

I think the approach in this project is very similar to your idea.

VUE-ADMIN-TEPLATE

Upvotes: 0

Mike Harrison
Mike Harrison

Reputation: 1393

The first approach is probably the best way except you don't have a router-view anywhere. You also don't need to use slots unless your navigation will be changing. And if your navigation is changing you probably would do this logic inside the Navbar component anyways.

You can simplify your Layout component to this:

<template>
  <div class="app-layout">
    <header-component />
    <div class="main">
      <navbar-component />
      <main>
        <router-view />
      </main>
    </div>
  </div>
</template>

<script>
  import Header from './Header.vue';
  import Navbar from './Navbar.vue';
  export default {
    name: 'Layout',
    components: {
      'header-component': Header,
      'navbar-component': Navbar
    }
  };
</script>

The way you are calling h => h('router-view') is going to render the entire app as a router-view which is not what you want. You will only want the main div to be changing content based on the route you are in. Change this to:

export default new Vue({
  el: '#app',
  router,
  render: h => h(require('./Layout')) //change this path to where Layout.vue is
});

Which will make your Layout component the root component of your app.

Upvotes: 0

roberto tom&#225;s
roberto tom&#225;s

Reputation: 4697

As much as possible, dont structure your code around your layout. That's the takeaway I would hope for you.

I tend to use basically 2 but one level deep (like 3). I have an app component to load vue stuff from components folder. There I have a router-view in main to show the route, a header component for header and navbar, and a modals component at the bottom of main.

Upvotes: 0

Related Questions