Search code examples
javascriptnode.jsvue.jshandlebars.jsexpress-handlebars

What is the Vue.js equivalent of Express-Handlebars sections?


I started a project using Express and Handlebars and then was encouraged to look at Vue.js. I am still at the stage of reading the docs but so far can't understand how to have layouts, partials and sections in Vue.js. I think a partial would be a component, but I'm lost how to have a layout with partials and sections that I can inject content into.

This is what I do using npm express-handlebars in a file called baselayout.hbs:

<!doctype html>
<html lang="en">
<head>
    {{> global/headcode }} <!-- partial view with code for the head tag. it has stuff like favicon links --->
    {{{_sections.pagemeta}}} <!-- page specific metadata injected here. this would be meta keywords description etc for a page/view --->
</head>
<body>
<div>
    {{> global/siteheader }} <!--- partial view for the site's header --->

    <div id="base-Container">
        <main id="base-Content" role="main">
            {{{ body }}} <!--- a page's main body content goes here --->
        </main>
    </div>
</div>
{{> sitefooter }} 
{{{_sections.pagescripts}}} <!-- section for page-specific scripts injected here --->
</body>
</html>

How could I setup something like the above in Vue.js that would also work with server-side rendering? I just need a base layout with header/footer components included but also sections into which page-specific content can go.


Solution

  • For SSR, you should look at Nuxt.js, Vapper, or one of the other SSR Vue frameworks.

    That said, yes, you would use components for everything. Generally, you would have one component for your main layout, then one for each view, then individual components for each partial/section that you would then import into your views and/or main layout. So, for example, based on the above code:

    Your main app layout:

    // AppLayout.vue
    
    <template>
      <div id="app-layout">
        <site-header />
        <router-view />
        <site-footer />
      </div>
    </template>
    
    <script>
    import SiteHeader from './components/global/SiteHeader.vue'
    import SiteFooter from './components/global/SiteFooter.vue'
    
    export default {
      name: 'AppLayout',
      components: {
        SiteHeader,
        SiteFooter
      },
      meta: {
        // metatags and other head content can be modified using vue-meta or similar 
      }
    }
    </script>
    

    Example 'partial' component:

    // BaseContainer.vue
    
    <template>
      <main id="base-container" role="main">
        <h1 class="title">{{ content.title }}</h1>
        <img :src="image" alt="" />
        <base-content v-html="content.body" />
      </main>
    </template>
    
    <script>
    import BaseContent from './components/content/BaseContent.vue'
    
    export default {
      name: 'BaseContainer',
      components: {
        BaseContent
      },
      props: {
        content: {
          type: Object,
          default() {
            return {}
          }
        },
        image: {
          type: String,
          default: ''
        }
      }
    }
    </script>
    

    Example view component:

    // MyView.vue
    
    <template>
      <div id="my-view">
        <base-container :content="content" :image="image" />
      </div>
    </template>
    
    <script>
    import BaseContainer from './components/BaseContainer.vue'
    import content from './data/myContent.json'
    import image from './assets/myImage.jpg'
    
    export default {
      name: 'MyView',
      components: {
        BaseContainer
      },
      data() {
        return {
          content,
          image
        }
      }
    }
    </script>
    

    You would then use vue-router to specify which view component to load based on the current URL.