This is the second part of a <span>{{ series.length }}</span>-part series where we use the WordPress REST API to build a blog layout using Vue and the static renderer Nuxt.js.

Getting Site Data

When we finished off last time, we had a very (very, very) basic page that displayed only the titles and excerpts for the five most recent posts. I’m not out to revolutionize blogging or anything, but I think it might also be a good idea to display the name of the blog on the home page.

In order to do that, we need to get that data and then display that data.

Let’s add a method to get the data to our WordPress REST API library:

lib\wp.js

class WpApi {
  ...

  siteData () {
    return axios.get(this.apiBase)
      .then(json => {
        const { name, description, url, home, gmt_offset, timezone_string } = json.data
        return { site_data: { name, description, url, home, gmt_offset, timezone_string } }
      })
      .catch(e => ({ error: e }))
  }

  ...
}

The base endpoint for the API returns basic site data along with a list of all endpoints for the API. We only need the site data, so we’ll discard the rest.

Now we need to display the data.

In our index page, we’ll update our asyncData method to use our new siteData() method. asyncData must return a promise that resolves to an object that will be merged with the component state, so we’ll use Promise.all() to make each call to the API. Promise.all() resolves to an array of the results for all promises, so we’ll call reduce() to combine the results into one object.

pages\index.vue

<template>
  <section class="container">
    <header>
      <h1><nuxt-link to="/">{{ site_data.name }}</nuxt-link></h1>
      <p>{{ site_data.description }}</p>
    </header>
    <div>
      <PostList :posts="posts" />
    </div>
  </section>
</template>

<script>
import PostList from '~/components/posts/PostList.vue'
import wp from '~/lib/wp'

export default {
  async asyncData ({ params }) {
    return Promise.all([wp.posts(), wp.siteData()])
      .then(vals => vals.reduce((inp, val) => ({ ...inp, ...val }), {}))
  },
  head () {
    return {
      title: this.site_data.name || 'Home'
    }
  },
  components: {
    PostList
  }
}
</script>

We’ve added a <header> to our template that displays the site name and tagline. Note that we use the built-in <nuxt-link> component to create a link.

We’ll also update the title in our HTML head. This requires the head() method which returns an object that updates the data in the head. In the head() method, our site_data object that we retrieved in the asyncData() method is available as a property of this.

Looking good!

Header

Setting up Vuex

This works great if we only have a single page, but even my neglected blog has more than one post. We don’t want to have to call out for site data every time we load a new page and it would be great if we didn’t have to clutter up our asyncData() method with an extra call, too.

Enter Vuex.

Vuex is a flux-like state management library for Vue. Vuex is built in to Nuxt and Nuxt has a couple of special hooks to make our life easier.

We can set up a Vuex store and load the site data into the store the first time we render a page. The data will be there for use for the remainder of the session. Later on, we can use our store to hold other things like post and navigation data.

Let’s set up our store.

All you need to create your Vuex store in Nuxt is an index.js file in the store folder with a default export of a function that returns a Vuex.Store. Our purposes are pretty simple, so we can get away with using a single store file, but you can also use a module pattern so long as your final store is exported from index.js.

store\index.js

import Vuex from 'vuex'
import wp from '~/lib/wp'

// Mutation Types
export const types = {
  SITE_DATA_UPDATE: 'SITE_DATA_UPDATE'
}

const createStore = () => {
  return new Vuex.Store({
    state: {
      site_data: {}
    },
    mutations: {
      [types.SITE_DATA_UPDATE] (state, payload) {
        state.site_data = { ...payload }
      }
    },
    actions: {
      nuxtServerInit ({ commit }) {
        return wp.siteData()
          .then(res => {
            commit(types.SITE_DATA_UPDATE, res.site_data)
          })
      }
    }
  })
}

export default createStore

There are three parts here in our initial store:

  • state — This is where we initialize our store. We can start with an empty site data object.
  • mutations — In Vuex, you make updates to state by calling the store’s commit method identifying a mutation to perform. Our SITE_DATA_UPDATE mutation simply replaces the existing site data with the object passsed in as the payload on the commit. Note that we’re exporting our mutation names to make it easy to reference them throughout the app.
  • actions — In Vuex, mutations are synchronous. To perform asynchronous updates, we use actions. nuxtServerInit() is a special action in Nuxt that is called when the Vuex store is first created. Our nuxtServerInit() action fetches the site data from the WordPress REST API then commits the SITE_DATA_UPDATE mutation passing the site data as the payload.

Now using our Vuex store, the site data that was part of component state in index.vue is now stored in the Vuex store for use throughout our session. We won’t need to fetch it again every time we load a new page.

Adding the Header to Our Layout

Now that we’ve moved the site data from component state to our Vuex store, we need to update how we’ll display it. The purpose of adding the site data to our Vuex store was so that we don’t have to fetch the data for every page, so it would be nice if we also don’t have to add the header individually to every page in our site.

Let’s start by updating the title in the head element for our index page.

pages\index.vue

<template>
  <main class="container">
    <div>
      <PostList :posts="posts" />
    </div>
  </main>
</template>

<script>
import PostList from '~/components/posts/PostList.vue'
import wp from '~/lib/wp'

export default {
  async asyncData ({ params }) {
    return wp.posts()
  },
  head () {
    return {
      title: `${this.$store.state.site_data.name} | Home`
    }
  },
  components: {
    PostList
  }
}
</script>

We’ll undo our changes from above, remove the <header> from our template, and go back to only calling out for post data from our asyncData() method. Nuxt automatically injects the Vuex store into our components, so it is available from this.$store. In our head() function, we can now access the site name from the store instead of component state.

To display the header on every page, we’re going to use layouts. In Nuxt, layouts are the base components. The contents of the page components are displayed in a layout component. (By default, the aptly-named default.vue component, though you can use different layouts throughout your app by specififying a different layout in your page component.)

The default layout component doesn’t do anything except display the contents of the page component using the <nuxt/> component.

layouts\default.vue

<template>
  <div>
    <nuxt/>
  </div>
</template>

We’re going to create a header component that displays the site data that we’ve fetched into our Vuex store and then add that component to the default.vue layout.

components\layouts\BlogHeader.vue

<template>
  <header class="container">
    <h1><nuxt-link to="/">{{ siteData.name }}</nuxt-link></h1>
    <p>{{ siteData.description }}</p>
  </header>
</template>

<script>
export default {
  props: {
    siteData: { type: Object }
  }
}
</script>

Our BlogHeader component takes the site data as props and uses the same header that we created in our initial version at the beginning of this post. We’ll still need to inject our data from the store. We’ll do that when we update the default.vue layout.

layouts\default.vue

<template>
  <div>
    <blog-header :siteData="site_data" />
    <nuxt/>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import BlogHeader from '~/components/layout/BlogHeader'

export default {
  computed: mapState(['site_data']),
  components: {
    BlogHeader
  }
}
</script>

We need to add a script section to import and use our new BlogHeader component. In the template, we could use the injected store to directly pass the site data as props to our BlogHeaer component like this:

<blog-header :siteData="$store.state.site_data" />

To be a little cleaner, we’ll use the Vuex mapState helper. The simple format for mapState just takes an array of property names from the store and makes those available as props. So, this.$store.state.site_data is now available in props as site_data. We can just pass that into our BlogHeader component.

Boom! After all that work, we’re exactly where we were when we started. Awesome.

Header Again

I kid, I kid. We may look exactly the same, but we’ve saved ourself a bunch of data calls and effort going forward.

Wrapping Up

As you can see, adding a Vuex store gives us a lot of power to let us reuse code and data and make our lives easier.

You can also check out the entire repo on GitHub at this point in project.

Next time, we’ll learn more about routing when we create pages for our posts and then we’ll have some fun adding a little style.