Simple Guide to Nuxt.js (Vue.js SSR)
February 14, 2020Why use Nuxt
I wanted to do Vue.js Server-Side Rendering (SSR). After reading into https://ssr.vuejs.org/ and exploring https://github.com/vuejs/vue-hackernews-2.0/, I found that there are a lot of codes required to piece things together, and decide to explore Nuxt.js
. I try to keep to vanilla Vue.js
without touching Vuex or Nuxt.js
for quite a while (as more frameworks means more learning and more problems to solve), but I guess there is more pros for using Vuex in the case of SSR.
- Nuxt.js support something called Universal mode, which generate both SSR and client side code.
- It also support static site generation, like
VuePress
orHugo
. - SSR just work out of the box, though you need to be aware of some SSR caveats while coding.
- Using Nuxt.js means I won’t be using
Vue Cli
, but luckily their tooling and hot-reload is pretty good as well.
SSR Caveats
- For AJAX, use library like axios as it support server and client side fetch. Fetch API work on client-side (browser only).
- On server-side, reactivity is disabled. It renders the component default state only.
- Lifecyle Hook: beforeCreate and created is used (beforeMount and mount is executed on client only); Avoid global side effects code (such as setInterval in beforeCreate - created, do them at mounted and destroyed instead)
- Avoid access to browser API like window and document
- Some 3rd party library might not be written with universal (server and client-side) usage in mind
- Probably want to use Webpack for both client and server code, due to vue-loader, file-loader, css-loader, javascript features (Babel), etc.
Setup Nuxt.js Project
npx create-nuxt-app PROJECT
cd PROJECT
NOTE: Previous vue init
nuxt-community/starter-template is deprecated.
create-nuxt-app v2.14.0
✨ Generating Nuxt.js project in journey-nuxt
? Project name PROJECT
? Project description My superior Nuxt.js project
? Author name desmond
? Choose the package manager Npm
? Choose UI framework Bootstrap Vue
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support
? Choose linting tools None
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
NOTE: I setup with Axios, BootstrapVue and Universal (SSR)
rendering mode.
Setup Page / Route
Edit pages/index.vue
, which is the main home page.
<template>
<div class="mb-3">
<h1>{{ title }}</h1>
<div>{{ description }}</div>
<button type="button" class="btn btn-primary" @click="showToast">Show Toast</button>
</div>
<pagination :total-page="totalPage" :current-page="page" />
</template>
<script>
import Pagination from '~/components/Pagination.vue'
export default {
head() {
return {
title: 'Home',
meta: [
{ hid: 'description', name: 'description', content: 'This is home page' }
]
}
},
async asyncData ({ $axios }) {
const { data } = await $axios.get('/data/home.json')
/*
return {
title: data.title,
description: data.desscription
}
*/
return data
},
data() {
return {
page: 1,
totalPage: 3
}
},
methods: {
showToast() {
this.$toast.show('Hello from Toast')
}
},
components: {
Pagination
}
}
</script>
asyncData
is called before the component is rendered, which return data is merged into components’s data
.
head
is for page title and other meta tags.
About Page
Edit pages/about.vue
<template>
<div>
<h1>About</h1>
</div>
</template>
<script>
export default {
head() {
return {
title: 'About'
}
}
}
</script>
Pages
Page component which receive param id
.
Edit pages/page/_id.vue
<template>
<div>
<div class="mb-3">
<h1>Page {{ page }}</h1>
<div v-for="value in items" :key="value">
{{ value }}
</div>
</div>
<pagination :total-page="totalPage" :current-page="page" />
</div>
</template>
<script>
import Pagination from '~/components/Pagination.vue'
export default {
head() {
return {
title: `Page ${this.page}`
}
},
validate ({ params }) {
// Must be a number
return /^\d+$/.test(params.id)
},
async asyncData ({ $axios, params }) {
const { data } = await $axios.get(`/data/page-${params.id}.json`)
return data
},
data() {
return {
totalPage: 3,
}
},
computed: {
page() {
return parseInt(this.$route.params.id)
}
},
components: {
Pagination
}
}
</script>
Header / Meta tag
Edit nuxr.config.js
.
export default {
// ..
head: {
// title formatting
titleTemplate: '%s - My App Name',
meta: [
{ charset: 'utf-8' },
// for bootstrap
{ name: 'viewport', content: 'width=device-width, initial-scale=1, shrink-to-fit=no' },
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// ..
}
Static Json File
Edit static/data/home.json
{
"title": "Home",
"description": "This is Home"
}
Edit static/data/page-2.json
{
"title": "List of Fruit",
"items": ["Apple", "Orage", "Pear"]
}
Edit static/data/page-3.json
{
"title": "List of Friends",
"items": ["Mei Ru", "Jack", "John"]
}
Components
Edit components/Pagination.vue
Import this Pagination component.
Replace all <a>
with <router-link>
and :href
with :to
.
Layout
This is the default layout used by all pages component.
Edit layouts/default.vue
<template>
<div>
<notifications />
<!-- <notifications group="default" /> -->
<b-navbar toggleable="lg" type="dark" variant="info">
<div class="container">
<b-navbar-brand to="/">APP_NAME</b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
<b-collapse id="nav-collapse" is-nav>
<b-navbar-nav>
<b-nav-item to="/about">About</b-nav-item>
</b-navbar-nav>
</b-collapse>
</div>
</b-navbar>
<div class="container" style="padding-top: 16px;">
<nuxt/>
</div>
</div>
</template>
<style>
html {
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
word-spacing: 1px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: border-box;
margin: 0;
}
</style>
WARN: The generated pages/index.vue
have a container
style which mess up the container
CSS of Bootstrap.
Install Vue Plugins
We install a plugin for notification.
npm install --save @nuxtjs/toast
NOTE: Not all vue components are compatible with Nuxt.js/SSR due to the confitions of SSR Caveats
mentioned above. Refer to a list of Nuxt.js Modules.
NOTE: Refer to Nuxt.js Install Vue.js Plugins for non-Nuxt modules.
Edit nuxt.config.js
.
export default {
// ..
modules: [
// ..
'@nuxtjs/toast'
],
toast: {
position: 'top-center',
duration: 3000
},
// ...
}
NOTE: The usage to toast plugin is demonstrated in pages/index.vue
as shown above.
Local Development
npm run dev
Access: http://localhost:3000/
Deployment
Refer Deploy Nuxt.js (SSR) to Firebase Cloud Functions
References:
- algo-trading
- algolia
- analytics
- android
- android-ktx
- android-permission
- android-studio
- apps-script
- bash
- binance
- bootstrap
- bootstrapvue
- chartjs
- chrome
- cloud-functions
- coding-interview
- contentresolver
- coroutines
- crashlytics
- crypto
- css
- dagger2
- datastore
- datetime
- docker
- eslint
- firebase
- firebase-auth
- firebase-hosting
- firestore
- firestore-security-rules
- flask
- fontawesome
- fresco
- git
- github
- glide
- godot
- google-app-engine
- google-cloud-storage
- google-colab
- google-drive
- google-maps
- google-places
- google-play
- google-sheets
- gradle
- html
- hugo
- inkscape
- java
- java-time
- javascript
- jetpack-compose
- jetson-nano
- kotlin
- kotlin-serialization
- layout
- lets-encrypt
- lifecycle
- linux
- logging
- lubuntu
- markdown
- mate
- material-design
- matplotlib
- md5
- mongodb
- moshi
- mplfinance
- mysql
- navigation
- nginx
- nodejs
- npm
- nuxtjs
- nvm
- pandas
- payment
- pip
- pwa
- pyenv
- python
- recylerview
- regex
- room
- rxjava
- scoped-storage
- selenium
- social-media
- ssh
- ssl
- static-site-generator
- static-website-hosting
- sublime-text
- ubuntu
- unit-test
- uwsgi
- viewmodel
- viewpager2
- virtualbox
- vue-chartjs
- vue-cli
- vue-router
- vuejs
- vuelidate
- vuepress
- web-development
- web-hosting
- webpack
- windows
- workmanager
- wsl
- yarn