loading...

Vue.js – Chapter 6: Nuxt.js

How to install Ubuntu Server 19.10

While Vue.js is frequently referred to as a framework, out of the box it doesn’t come with all the functionality you might need to build more sophisticated web applications, such as routing and state management.

Nuxt.js (hereafter referred to as Nuxt) is a non-official framework that brings together Vue, Vue Router, and Vuex, and was inspired by the similar, React-based framework Next.js. It takes a convention-over-configuration approach to remove a lot of the boilerplate from the process of developing Vue applications.

With some frameworks, this approach can become restrictive when you want to venture beyond the use cases the developers had in mind. Nuxt’s conventions reduce the time it takes to build out sophisticated Vue applications, but almost every aspect of the framework can be overridden, tweaked, and customized.

Although that in itself is an attractive proposition, Nuxt’s killer feature is its support for server-side rendering (SSR). As you’re probably aware, creating JavaScript apps that can be rendered on the server as well as on the client (often referred to as universal web applications) is a very useful technique for improving first-load performance and SEO.

SSR is commonly quite a complicated thing to put in place, so Nuxt really scores here by making the process relatively straightforward and painless. As if that wasn’t enough, it now supports static site generation as well, allowing you to get the performance and SEO benefits of SSR on cheap, static-page hosting.

Starting a Nuxt Project

While it’s possible to manually install and configure Nuxt, it has its own installation tool which does the initial configuration for you and provides you with a skeleton application layout to get you started.

You can start the installation process by running the following command (assuming you have npm 5.2.0+ installed):

npx create-nuxt-app <project-name>

The npx command is a handy npm utility that will load and run the installer without the need to install the module into your global node_modules folder first.

Installing with Yarn

It’s also possible to run the installer via Yarn, with the following command:

yarn create nuxt-app <my-project>

Once the installer downloads, it will prompt you with a series of options for your new project, in a very similar manner to the Vue CLI.

You’ll be given the following options:

  • Project Name. The default is the name you provided for the project folder, but you can override this here if you wish.
  • Description. A description for your project. Both this and the project name are used to fill in the package.json file.
  • Use a custom server framework. By default, Nuxt hides the server functionality out of sight, but it’s possible to choose from a handful of Node frameworks and have it use that instead. You can pick from Express, Koa, Hapi, Feathers, Micro, and Adonis. You probably won’t want this option unless you have a specific need to customize the server-side functionality.
  • Use a custom UI framework. You can also choose between a handful of CSS UI frameworks in order to have one pre-configured for you. You can pick from Bootstrap, Vuetify, Bulma, Tailwind, Element UI, Ant Design Vue, and Buefy. These are convenient if you want to use one of the given frameworks, but it’s easy enough to add your own later.
  • Rendering mode. Nuxt offers you the choice between a standard SPA (single page application), or a universal web app, which pre-renders each request on the server for better performance and SEO. While Nuxt’s conventions and structure can still be useful for building SPAs, its main selling point is the SSR/static site generation. Hence, “universal” is the default here.
  • axios module. This module wraps the axios HTTP library and gives it better integration with Nuxt. You don’t have to use it, but it’s recommended.
  • ESLint. Pre-installs ESLint for you.
  • Prettier. Pre-installs Prettier.
  • Author name. Your name, for the package.json file.
  • Package manager. You can choose from npm or Yarn to have Nuxt configure itself for your chosen package manager.

If you’re intending to follow along with the examples in this chapter, then accept all the default options except for the axios module, which you should say “yes” to.

Once you’ve answered all the questions, Nuxt will go ahead and download the necessary dependencies, and create a basic folder layout with some boilerplate code. At the time of writing, some of these files will cause linting errors (if you have ESLint and Prettier installed) which you can fix by running npm run lint -- --fix or yarn run lint --fix.

Source Control

As part of the install process, Nuxt will initialize your new project folder as a Git repo. However, unlike the Vue CLI, it won’t make the initial commit for you. It’s a good idea to do this as your first step!

To get started building your app with Nuxt, spin up the built-in development server:

npm run dev

While this process is running, it will automatically rebuild your app if it detects any changes. Let’s take a look at the folder structure that Nuxt uses.

Project Layout

Below is the default folder structure that the Nuxt installer leaves you with. There’s a README file inside each directory, but I’ve left those off for clarity.

├── assets
├── components
│   └── Logo.vue
├── layouts
│   └── default.vue
├── middleware
├── pages
│   └── index.vue
├── plugins
├── static
│   └── favicon.ico
├── store
├── nuxt.config.js
├── package.json
└── package-lock.json

Let’s look at each of these folders and see what role they play within a Nuxt application.

pages

As I mentioned at the beginning of the chapter, Nuxt favors convention over configuration. Rather than having to create a router configuration and specify all your app’s routes and the components they map to, Nuxt can do the hard work for you.

Any components you place inside the pages folder will automatically be wired up according to a simple set of conventions.

Placing a component named about.vue into the folder will automatically create the route /about in your app. Likewise, putting components within subfolders will add the respective segments to the route, so user/profile.vue would give you a URL of /user/profile.

What about creating routes with dynamic segments? There’s a simple rule for that too. Let’s say we want to recreate the blog route we looked at in Chapter 4, /blog/:slug. We would create a blog subfolder within pages, and inside that create the file _slug.vue. Beginning the name with an underscore lets Nuxt know this segment of the route will be dynamic.

This is equivalent to creating the following route config:

{
  name: 'blog-slug',
  path: '/blog/:slug',
  component: 'pages/blog/_slug.vue'
}

The installer also creates a components folder for any other Vue components that you don’t want to affect the routing configuration.

layouts

In previous chapters, we put any markup that was common to every “page” of our app into the <App> component. But this isn’t a very flexible approach. What if we wanted to have different layouts for different sections of our app?

Fortunately, Nuxt provides us with a neat solution to this. The layouts folder contains a component called default.vue, which contains the following markup.

layouts/default.vue

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

<style>
  ...
</style>

The important thing to note here is the <nuxt/> component. This is Nuxt’s equivalent of the <router-view> component we saw in Chapter 4, and it’s necessary in order for your app’s components to actually be displayed. As you can see, this layout simply renders the page component within a plain <div>.

To change the layout, we just need to create a new file in the layouts directory (for example, two-column.vue) and specify that we want to use this layout for a page component by adding a layout property to its options.

pages/faq.vue

<script>
  export default {
    layout: 'two-column'
  }
</script>

Avoid Styling Global Markup

At the time of writing, there’s a bug in Nuxt which results in CSS rules from a layout component remaining in the page even after navigating to pages using a different layout. For this reason, avoid styling global elements (such as the <body> tag) from within your layouts.

assets

The assets folder is for any non-JS assets (such as images, fonts and CSS files) that you want to use from your components. You can access the contents of this folder via the ~ alias, both within your components’ code and template sections.

So, for example, if you have the file logo.png that you want to use in one of your components, you can put the file in the assets folder and reference it within your component template like this:

<img src="~/assets/logo.png">

Inlining Smaller Assets

By default, Nuxt configures webpack (which it uses under the hood to bundle your application) to convert any images and fonts smaller than 1KB to inline base-64 data URLs.

For other kinds of assets that you don’t want to be processed by webpack, Nuxt creates the static folder. Files in this folder will be served relative to the app’s base URL, so static/brochure.pdf will be available as /brochure.pdf.

store

There’s no Vuex store set up by default with Nuxt, but it’s very easy to add one should your app require it. All that’s necessary for a basic store is to create an index.js file within the store folder.

Reloading the Development Server

If you add a Vuex store to your Nuxt application, you’ll need to restart the development server before you can access it from your components!

Defining the store is slightly different than in a non-Nuxt app: the store file needs to return a factory function that will return a new store when called.

store/index.js

import Vuex from 'vuex'

export default () => {
  return new Vuex.Store({
    state: () => ({
      // ...
    }),
    mutations: {
      // ...
    }
  })
}

It’s also important to note that the state property must be a function that returns the base state object. This is to prevent problems with shared state when the site is rendered on the server.

The store can also define a special action, nuxtServerInit, which will be called whenever a page is rendered on the server:

actions: {
  nuxtServerInit ({ commit }, { req, res }) {
    // ...
  }
}

As well as the usual Vuex context object that actions receive, this method receives a Nuxt context object as its second argument.

This object contains quite a lot of keys (see the API documentation for a full list), including the server request and response objects—req and res respectively. This allows you to pre-populate the store with the logged-in user, for example.

middleware

This folder is for code that you want to be run for every page, or a group of pages. To create a middleware, you add a .js file that exports a function. When the function is called, it’ll be passed a context object.

Middleware can be used to do things like check the authentication status of the current user.

middleware/authenticated.js

export default function ({ store, redirect }) {
  if (!store.getters.isAuthenticated) {
    return redirect('/login')
  }
}

The example middleware shown above uses a Vuex getter to check if the user is authenticated, and if not, redirects them to the login page.

To use a middleware, you add a middleware property to a page or layout component. This property is a string (or an array of strings) containing the name of the middleware(s) that you want to run before the component is loaded.

layouts/admin.vue

export default {
  middleware: 'authenticated'
}

plugins

Because Nuxt abstracts away the underlying setup of Vue and libraries like Vue Router, you might be wondering how to install other Vue plugins.

The Nuxt way of handling this is to create a file inside the plugins folder where you do the usual initialization.

plugins/vue-flash-message.js

import Vue from 'vue';
import VueFlashMessage from 'vue-flash-message';

Vue.use(VueFlashMessage);

Then tell Nuxt about the plugin, by editing the plugins key of the global config file.

nuxt.config.js

export default {
  // ...
  plugins: [
    { src: '~/plugins/vue-flash-message', ssr: false }
  ],
  // ...
}

Note that, in the example above, we’re setting the ssr property to false to prevent the code being executed on the server. Many Vue plugins aren’t written with SSR in mind and can crash Nuxt if they try to access browser-only globals.

Nuxt Component Options

In order to hook into the functionality that the framework offers, Nuxt allows you to add some specific properties and functions to your page components. Let’s take a look at them.

asyncData

Before a component is initialized, Nuxt will look to see if it has an asyncData method. If it does, this method is called, giving you the opportunity to fetch any data you might need to render the component.

On the initial request it will be run on the server, but once the app has loaded, subsequent calls will be run on the client as the user navigates around the running SPA.

The method receives the Nuxt context object as its first parameter. This object gives you access to the following.

  • app. The main Vue instance for your application, including any attached plugins.
  • route. The current route object (see Chapter 4).
  • store. The Vuex store, if you’re using one.
  • params. A shortcut to the route.params object.
  • query. A shortcut to the route.query object.
  • req. The request object (if running on the server).
  • res. The response object (if running on the server).
  • redirect. A function for redirecting the current route.
  • error. A function for displaying an error page.

Additional Context Properties

See the documentation for an exhaustive list of additional properties.

The return value from the method should be an object that Nuxt can merge with the component’s data object. It’s important to note that you don’t have access to the component itself from within the asyncData method, as it hasn’t yet been initialized.

asyncData ({ $axios, params, error }) {
  return $axios.get(`https://api.mysite.com/posts/${params.slug}`)
    .then(res => ({ content: res.data }))
    .catch(e => error({ statusCode: 404, message: 'Post not found' }))
}

In the example above, we fetch the slug (which is a route parameter) from the context object and use it to request the content from an API.

If the request is successful, the content is returned and Nuxt will merge it into our page’s data. If it fails, we call the error method, passing it an object with a statusCode and a message key.

fetch

The fetch method is very similar to asyncData, except that it doesn’t merge returned data into your component. Rather, the purpose of this method is to allow you to populate your Vuex store.

You can fetch the data directly within this method and commit it to the store, or you can opt to dispatch an action in your store that takes care of fetching the data itself.

Like asyncData, this method receives the context object, allowing you access to the store state, getters, and dispatch and commit functions.

head

The head property allows you to set options for the page header and metadata:

{
  title: 'About Us',
  meta: [
    { name: 'description', 
      content: 'Spacely Sprockets is the galaxy\'s largest producer ...' }
  ]
}

These properties will be output in the header when the page is rendered.

layout

As mentioned in the earlier section on layouts, setting this property allows you to change which layout the page component will be rendered within.

Building for Production

Once we have our new site or application built with Nuxt, the next step is to run the build process and prepare our code to be deployed.

Single Page Application

If you chose the SPA mode when creating the project (or if you changed the mode setting in nuxt.config.js) you can build your code as a normal front-end app.

Running the command npm run build will generate your app inside the dist folder, allowing you to deploy it to your web hosting.

Universal Application

For SSR apps, we need to copy our project folder to a web server and first run the npm run build command. Once the application code has been built, we can run npm run start to launch the Nuxt server.

By default, the server runs on port 3000 (although this can be set in nuxt.config.js) so you’ll need to configure your web server to proxy incoming requests to the app.

The Nuxt FAQ site provides examples of configuring a NGINX proxy, as well as deploying a Nuxt app to popular platforms.

Static Site

Static site generation attempts to be the best of both worlds.

In a universal app, the initially requested page is rendered on the server and returned to the browser, where the client-side code then takes over rendering the rest of the app as the user navigates around.

Static site generation, on the other hand, renders all possible routes in advance, saving the output as HTML files. This technique gives you the benefit of fast response times, and pages that are indexable by the search engines, without the overhead of running a Node.js server.

It’s important to bear in mind your pages will only be as up to date as the last time you ran the generate command, making this approach more suited to situations where the data changes less often. For sites that rely on frequently changing data (for example, up-to-the-minute news or stock data), users (and search engine spiders) could potentially be seeing out-of-date information.

You can generate a static version of your site by running npm run generate and the resulting files will be saved to the dist folder.

There’s one important thing to be aware of when generating sites with dynamic route segments. By default, these will be ignored by Nuxt, as it has no way of knowing all the possible parameter values the route could be called with.

To get around this problem, Nuxt allows you to give it a list of routes to render, adding them to nuxt.config.js as an array:

export default {
  // ...
  generate: {
    routes: [
      '/blog/my-first-post',
      '/blog/pictures-of-my-cat',
      '/blog/the-best-pie-ever'
    ]
  }
  // ...
}

It’s possible to generate the list of routes programmatically so you can do things like query an API endpoint for a current list of posts (for example). I recommend checking out the official documentation if you want to know more.

Summary

Hopefully this chapter has given you a good understanding of what Nuxt.js is and why you might want to use it. We started off by talking about how it brings together Vue, Vue Router, and Vuex, providing some straightforward conventions that allow you to quickly build apps with a minimum of boilerplate code.

We walked through the install process, and looked at the skeleton application structure that it creates, using this as a starting point to see how Nuxt applications are put together.

We saw how to implement SSR with Nuxt and looked at some of the properties and methods it makes available to facilitate this.

Lastly, we saw how to build a Nuxt app for production as either a plain SPA, or as a universal SSR app, and examined the pros and cons of the recent generate command and when we might want to use it.

Comments are closed.

loading...