Some time ago, the company just had a project using nuxT.js to build, and just in the company to do a share, a little more sorting out a release. This article is more suitable for the first use of NUxT.JS students, mainly under the construction process to do some configuration. If you are using NuxT.js for the first time, I suggest you go through the official documentation and then go back to this article.

Why use nuxT.js

The reason, needless to say, is to use the server rendering capability of NuxT.js to solve the SEO problem of Vue project.

A simple comparison between NuxT.js and pure Vue projects

1. Different goals after build

vue: dist

nuxt: .nuxt

2. Web rendering process

Vue: client rendering, first download JS, through Ajax to render the page;

Nuxt: Server rendering, the server can return directly after splicing the HTML, and the first screen can be done without initiating Ajax requests;

3. Deployment process

Vue: Only need to deploy the dist directory to the server, no server side, need to use nginx and other Web server;

Nuxt: Deploy almost all files to the server (except node_modules,.git). It has its own server, which needs to be managed by pm2 (deployment needs to be reload pm2). If you want to use a domain name, you need nginx as a proxy.

4. Project entry

Vue: / SRC /main.js, main.js can do some initialization of global registration; Nuxt: There is no main.js entry file. The operation of project initialization needs to be configured with nuxt.config.js.

Build a nuxT.js project from zero and configure it

Create a new project

Directly use scaffolding for installation:

NPX create-nuxt-app < project name >Copy the code

If you Choose a custom Server framework, you can Choose a server framework based on your business situation. The common one is Express, Koa, and the default is None. The default server is Nuxt.

  • Select the default Nuxt server that will not be generatedserverNuxt helps you complete all the rendering operations of the server side, so you don’t need to care about the details of the server side. The development experience is closer to the Vue project. The disadvantage is that it can’t do some customized operations of the server side.
  • Choose another server framework, such asExpress, will be generatedserverFolder to help you set up a basic Node server environment where you can do some Node operations. For example, my company’s business needs (parse Protobuf) to useExpressThe node parses the protobuf and returns JSON data to the client.

Choose Nuxt.js modules. You can Choose Axios and PWA. If axios is selected, it will help you register $axios under the Nuxt instance, allowing you to make requests directly from this.

Enable ESLint checking

In the build property of nuxt.config.js add:

  build: {
    extend (config, ctx) {
      // Run ESLint on save
      if (ctx.isDev && ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre'.test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    }
  }
Copy the code

This way you can check the syntax when you save the file at development time. The default rule nuxt uses is @nuxtjs (the underlying rule is from eslint-config-standard), which is configured in /.eslintrc.js:

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: 'babel-eslint'
  },
  extends: [
    '@nuxtjs'// This rule corresponds to this dependency: @nuxtjs/eslint-config'plugin:nuxt/recommended'
  ],
  // add your custom rules here
  rules: {
    'nuxt/no-cjs-in-config': 'off'}}Copy the code

Teams that are not comfortable with standard rules can change @nuxtjs to something else.

Use dotenv and @nuxtjs/dotenv to manage environment variables in a unified manner

On the Node side, we like to use Dotenv to manage environment variables in a project, keeping all environment variables in the root directory of.env.

  • Installation:
npm i dotenv
Copy the code
  • Use:
  1. Create one in the root directory.envFile and write the environment variables you need to manage, such as the server addressAPIHOST:
APIHOST=http://your_server.com/api
Copy the code
  1. in/server/index.js(this file is automatically generated by selecting the Express server framework)
require('dotenv').config() // Use console.log(process.env.apihost) // http://your_server.com/apiCopy the code

Env is not available to Nuxt clients. Env is not available to Nuxt clients. As described in the nuxt.js document, we can place the client environment variables in nuxt.config.js:

module.exports = {
  env: {
    baseUrl: process.env.BASE_URL || 'http://localhost:3000'}}Copy the code

However, if node and client need to use the same environment variable (we will use the same SECRET variable later in API authentication), we need to maintain this field in both nuxt.config.js and.env, which is troublesome. We prefer to maintain environment variables in only one place, so to solve this problem, I found the @nuxtjs/dotenv dependency, which allows nuxt clients to use.env directly, as expected.

  • Installation:
npm i @nuxtjs/dotenv
Copy the code

The client is also used through process.env.xxx, no more examples.

This way, we can manage the variables in our development environment in a unified way through the two packages dotenv and @nuxtjs/dotenv.

In addition, @nuxtjs/dotenv allows you to specify other env files when packaging. For example, if we use.env for development, but we want to use other environment variables for the online version we package, we can specify a different file for build, such as /.env.prod, by specifying it in nuxt.config.js:

module.exports = {
    modules: [
    ['@nuxtjs/dotenv', { filename: '.env.prod'}} // Specify the dotenv to use when packaging,}Copy the code

@ nuxtjs/toast modules

Toast is a very common feature, and most UI frameworks have it. But if your site doesn’t use a UI framework and Alert is ugly, you can introduce this module:

npm install @nuxtjs/toast
Copy the code

It is then introduced in nuxt.config.js

module.exports = {
    modules: [
    '@nuxtjs/toast'['@nuxtjs/dotenv', { filename: '.env.prod'}] // specify the dotenv to use when packaging, toast: {// configure the position of the toast module:'top-center', 
    duration: 2000
  }
}
Copy the code

This way, Nuxt will register the $toast method globally for you to use, very convenient:

this.$toast.error('The server is wandering')
this.$toast.error('Request successful ~~')
Copy the code

API authentication

For some sensitive services, we may need to authenticate the API to prevent the NODE API from being easily stolen. Therefore, we need to create an API authentication mechanism. Common solution is JWT, you can refer to Mr. Ruan’s introduction: “JSON Web Token Introduction tutorial”. If the scene is relatively simple, you can design your own, here is an idea:

  1. The client and node declare a SECRET key in the environment variable: SECRET= XXXX. Note that this is confidential;
  2. When the client initiates the request, the current timestamp (timestamp) andSECRETBy some algorithm, generate onesignature“On requesttimestampandsignature;
  3. The node receives the request and obtainstimestampandsignatureThat will betimestampRegenerate a signature using the same algorithm as the secret key_signature
  4. Compared to what the client requestedsignatureGenerated using the same algorithm as Node_signatureIf they are consistent, the authentication is approved. Otherwise, the authentication fails.

Specific steps:

The client encapsulates AXIOS in a layer:

import axios from 'axios'
import sha256 from 'crypto-js/sha256'
import Base64 from 'crypto-js/enc-base64'// Encryption algorithm, need to install crypto-jsfunction crypto (str) {
  const _sign = sha256(str)
  return encodeURIComponent(Base64.stringify(_sign))
}

const SECRET = process.env.SECRET

const options = {
  headers: { 'X-Requested-With': 'XMLHttpRequest' },
  timeout: 30000,
  baseURL: '/api'
}

// The server-side needs a full url to works
if (process.server) {
  options.baseURL = `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}/api`
  options.withCredentials = true} const instance = axios.create(options) // Do one for each axios request, Carry on the signature and timestamp instance. Interceptors. Request. Use (config = > {const timestamp = new Date (). GetTime () const param = `timestamp=${timestamp}&secret=${SECRET}`
    const sign = crypto(param)
    config.params = Object.assign({}, config.params, { timestamp, sign })
    return config
  }
)

export default instance
Copy the code

Then write an authentication on the server end of middleware, / server/middleware/verify. Js:

const sha256 = require('crypto-js/sha256')
const Base64 = require('crypto-js/enc-base64')

function crypto (str) {
  const _sign = sha256(str)
  returnEncodeURIComponent (base64.stringify (_sign))} // Use the same key as the client const SECRET = process.env.secretfunctionverifyMiddleware (req, res, next) { const { sign, Timestamp} = req.query const _sign = crypto(timestamp=${timestamp}&secret=${SECRET}`)
  if (_sign === sign) {
    next()
  } else {
    res.status(401).send({
      message: 'invalid token'
    })
  }
}

module.exports = { verifyMiddleware }
Copy the code

Finally, reference the middleware in the route that needs authentication, /server/index.js:

const { Router } = require('express')
const { verifyMiddleware } = require('.. /middleware/verify.js') const router = router () // Add router.get() to the route to be authenticated'/test', verifyMiddleware, function (req, res, next) {
    res.json({name: 'test'})})Copy the code

Handling of static files

/server/index.js = /server/index.js = /server/index.js = /server/index.js = /server/index.js

const express = require('express')
const app = express()

app.use('/static', express.static('static'))
Copy the code

4. Nuxt development

The life cycle

Nuxt extends the Vue lifecycle to something like this:

export default {
  middleware() {}, // servervalidate() {}, // serverasyncData() {}, // serverfetch() {}, // store data loadingbeforeCreate() {// Both the server and client execute},created() {// Both the server and client execute},beforeMount () {}, 
  mounted() {} // Client}Copy the code

asyncData

This approach is one of Nuxt’s biggest selling points, the ability to render on the server is here, so be sure to use it for your first rendering. AsyncData will pass in a context parameter to get some information, such as:

exportDefault {asyncData (CTX) {ctx.app // root instance ctx.route // Route instance ctx.params // Route parameter ctx.query // route question mark parameter ctx.error // Error handling}}Copy the code

Handling rendering errors and Ajax request errors

  • AsyncData rendering error

Nuxt provides context.error for handling asyncData errors. Nuxt provides context.error for handling asyncData errors. Call this method in asyncData to jump to the error page.

exportDefault {async asyncData (CTX) {// try catch, Try {throw new Error()} catch {ctx.error({statusCode: 500, message:'The server is wandering ~'})}}}Copy the code

This way, when an exception occurs, you will be redirected to the default error page, which can be customized via /layout/error.vue.

Error must be an argument like {statusCode: 500, message: {resultCode: 10005, resultInfo: {resultCode: 10005, resultInfo: {resultCode: 10005, resultInfo: ‘Server internal error’}, you need to convert the API error returned.

For convenience, I have introduced /plugins/ctx-inject. Js to register a global errorHandler for context: context.$errorHandler(err). Inject $root and context, ctx-inject. Js:

// Register global error handling events for contextexport default (ctx, inject) => {
  ctx.$errorHandler = err => {
    try {
      const res = err.data
      if(res) {// The nuxt error page can only recognize HTTP status codes, so the statusCode is sent to 500, indicating that the server is abnormal. ctx.error({ statusCode: 500, message: res.resultInfo }) }else {
        ctx.error({ statusCode: 500, message: 'The server is wandering ~' })
      }
    } catch {
      ctx.error({ statusCode: 500, message: 'The server is wandering ~'})}}}Copy the code

Then use the plugin in nuxt.config.js:

export default {
  plugins: [
    '~/plugins/ctx-inject.js']}Copy the code

AsyncData: asyncData: asyncData: asyncData

exportDefault {async asyncData (CTX) {// try {throw new Error()} catch(err) {CTX.$errorHandler(err)
        }
    }
}
Copy the code
  • Ajax request error

For ajax exceptions, the page is already rendered. Instead of going to the error page, you can toast it with this.$toast.error(res.message).

Loading method

Nuxt has a built-in loading progress bar at the top of the page. You are advised to use this style to provide page hopping experience. Open: this.$nuxt.$loading.start() Finish: this.$nuxt.$loading.finish()

Packaged deployment

In general, you can package it locally before deployment, run it locally for confirmation and upload it to the server for deployment. Command:

// Pack NPM run build // run NPM start locallyCopy the code

Node_modules,.git,.env, node_modules,.git,.env

{
  "name": "nuxt-test"."script": "./server/index.js"."instances": 2."cwd": "."
}
Copy the code

Env. Prod: cp./.env.prod./.env. For the first deployment or if you have a new dependency package, you need to install NPM on the server once, then you can start the process with pm2:

// Run pm2 start. /pm2.json in the project root directoryCopy the code

Pm2: pm2 save && pm2 startup. Note that each deployment requires a restart of the process :pm2 reload Nuxt-test.

Five, the last

Nuxt.js introduces Node, and nuxt.config.js replaces some of the functions of main.js. The directory structure and vue project are slightly different, adding a lot of conventions, for the students who first contact may feel very strange, more content or have to read the official document.

Demo source code: fengxianqi/front_end-demos/ SRC /nuxt-test.