Why 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 PROJECTcd 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: