Битва и конфигурация Nuxt.js

Nuxt.js

Некоторое время назад компания просто использовала Nuxt.js для создания проекта, и им поделились внутри компании, его разобрали и разослали. Эта статья больше подходит для студентов, которые плохо знакомы с Nuxt.js, и в основном рассказывает о некоторых конфигурациях, сделанных в процессе сборки. Студентам, впервые использующим Nuxt.js, рекомендуется сначала пройти его.официальная документация, а затем вернитесь к моей статье.

1. Зачем использовать Nuxt.js

Излишне говорить, что причина в том, чтобы использовать возможности рендеринга на стороне сервера Nuxt.js для решения проблемы SEO проекта Vue.

Во-вторых, простое сравнение проектов Nuxt.js и чистого Vue.

1. Различные целевые продукты после сборки

vue: dist

nuxt: .nuxt

2. Процесс рендеринга веб-страницы

vue: рендеринг на стороне клиента, сначала скачать js, а потом рендерить страницу через ajax;

nuxt: рендеринг на стороне сервера, который может быть выполнен сразу после того, как сервер соединит html, а первый экран может быть выполнен без инициирования ajax-запроса;

3. Процесс развертывания

vue: Просто разверните каталог dist на сервере, сервера нет, вам нужно использовать nginx в качестве веб-сервера;

NUXT: Необходимо развернуть почти все файлы на сервер (кроме Node_Modules, .git), со своим собственным сервером требуется управление PM2 (перезагрузка PM2 требуется для развертывания), если требуется доменное имя, NGINX требуется в качестве прокси.

4. Вход в проект

vue: /src/main.js, вы можете выполнить некоторую работу по инициализации для глобальной регистрации в main.js; nuxt: нет входного файла main.js, необходимо пройти операцию инициализации проектаnuxt.config.jsВыполните задания по настройке.

3. Создайте проект Nuxt.js с нуля и настройте его.

создать новый проект

Установить непосредственно с помощью строительных лесов:

npx create-nuxt-app <项目名>

Выберите один из вариантов выше.

Стоит упомянуть, что оChoose custom server framework(Выберите фреймворк на стороне сервера), вы можете выбрать фреймворк на стороне сервера в соответствии с вашей бизнес-ситуацией, распространенными являются Express, Koa, по умолчанию None, то есть сервер Nuxt по умолчанию, я выбрал его здесьExpress.

  • Выберите сервер Nuxt по умолчанию, не будет генерироватьсяserverПапки, все операции рендеринга на стороне сервера выполняются Nuxt за вас, и вам не нужно заботиться о деталях на стороне сервера. Опыт разработки ближе к проекту Vue. Недостатком является то, что вы не можете сделать некоторые настраиваемые операции на стороне сервера.
  • Выберите другую серверную структуру, напримерExpress, будет генерироватьserverПапка, которая поможет вам создать базовую среду сервера Node, в которой вы можете выполнять некоторые операции с узлом. Например, бизнес-потребности моей компании (разбор protobuf) используютExpress, делаем слой форварда на реальный серверный апи, после парсинга протобуфа на стороне ноды возвращаем json данные клиенту.

а такжеChoose Nuxt.js modules(выберите модуль nuxt.js), вы можете выбратьaxiosа такжеPWA, если выбран axios, это поможет вам зарегистрироваться под экземпляром nuxt$axios, чтобы можно было прямо в файле .vuethis.$axiosОбратиться с просьбой.

Включить проверку eslint

существуетnuxt.config.jsДобавьте в свойство сборки:

  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)/
        })
      }
    }
  }

Таким образом, вы можете проверить синтаксис, сохранив файл во время разработки. Правила, используемые nuxt по умолчанию:@nuxtjs(Нижний слой происходит изeslint-config-standard), правила настраиваются в/.eslintrc.js:

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  parserOptions: {
    parser: 'babel-eslint'
  },
  extends: [
    '@nuxtjs', // 该规则对应这个依赖: @nuxtjs/eslint-config
    'plugin:nuxt/recommended'
  ],
  // add your custom rules here
  rules: {
    'nuxt/no-cjs-in-config': 'off'
  }
}

Если вы не привыклиstandardкоманды правил могут@nuxtjsСменить на что-то другое.

Унифицированное управление переменными среды с помощью dotenv и @nuxtjs/dotenv

На стороне узла нам нравится использоватьdotenvЧтобы управлять переменными окружения в проекте, поместите все переменные окружения в корневой каталог.envсередина.

  • Установить:
npm i dotenv
  • использовать:
  1. Создайте новый в корневом каталоге.envфайл и запишите переменные среды, которыми необходимо управлять, например, адрес сервераAPIHOST:
APIHOST=http://your_server.com/api
  1. существует/server/index.jsИспользуется в (этот файл автоматически генерируется инфраструктурой сервера Express):
require('dotenv').config()

// 通过process.env即可使用
console.log(process.env.APIHOST) // http://your_server.com/api

На данный момент мы просто делаем сервер доступным.envФайл только, клиент NUXT не может использовать.env,согласно сДокументация Nuxt.jsСказал, что переменные среды клиента могут быть помещены вnuxt.config.jsсередина:

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

Но если узлу и клиенту необходимо использовать одну и ту же переменную среды (одна и та же переменная SECRET будет использоваться, когда аутентификация API упоминается позже), им необходимоnuxt.config.jsа также.envПоддерживать это поле сложнее, мы предпочитаем, чтобы переменные среды хранились только в одном месте, поэтому для решения этой проблемы я нашел@nuxtjs/dotenvЭта зависимость позволяет клиентам nuxt использовать ее напрямую..env, что оправдало наши ожидания.

  • Установить:
npm i @nuxtjs/dotenv

Клиент также проходитprocess.env.XXXиспользовать, больше нет примеров.

Таким образом, мы проходимdotenvа также@nuxtjs/dotenvЭти два пакета могут единообразно управлять переменными в среде разработки.

Кроме того,@nuxtjs/dotenvПозволяет указать дополнительные файлы env при упаковке. Например, при разработке мы используем.env, но онлайн-версия, которую мы упаковали, хочет использовать другие переменные среды.В этом случае вы можете указать другой файл во время сборки, например/.env.prod, как раз вnuxt.config.jsУточнить:

module.exports = {
    modules: [
    ['@nuxtjs/dotenv', { filename: '.env.prod' }] // 指定打包时使用的dotenv
  ],
}

@nuxtjs/модуль тостов

Можно сказать, что Toast — это очень распространенная функция, и общие фреймворки пользовательского интерфейса будут иметь эту функцию. Но если ваш сайт не использует UI-фреймворк, а оповещение слишком уродливое, вы можете ввести этот модуль:

npm install @nuxtjs/toast

затем вnuxt.config.jsвведен в

module.exports = {
    modules: [
    '@nuxtjs/toast',
    ['@nuxtjs/dotenv', { filename: '.env.prod' }] // 指定打包时使用的dotenv
  ],
  toast: {// toast模块的配置
    position: 'top-center', 
    duration: 2000
  }
}

Таким образом, nuxt будет зарегистрирован во всем мире.$toastМетоды в вашем распоряжении, очень удобно:

this.$toast.error('服务器开小差啦~~')
this.$toast.error('请求成功~~')

API-аутентификация

Для некоторых конфиденциальных служб нам может потребоваться аутентификация API, чтобы люди не могли легко украсть API на стороне нашего узла, поэтому нам необходимо создать механизм аутентификации API. Распространенным решением является jwt, вы можете обратиться к введению г-на Руана:Начало работы с веб-токеном JSON. Если сцена относительно простая, вы можете спроектировать ее самостоятельно, вот идея:

  1. Клиент и узел объявляют секретный ключ в переменной среды: SECRET=xxxx, обратите внимание, что это конфиденциально;
  2. Когда клиент инициирует запрос, текущая временная метка (timestamp) иSECRETС помощью некоторого алгоритма сгенерируйтеsignature, возьмите его с собой, когда вы попроситеtimestampа такжеsignature;
  3. Узел получает запрос и получаетtimestampа такжеsignature,БудуtimestampИспользуйте тот же алгоритм, что и ключ, чтобы снова сгенерировать подпись_signature
  4. по сравнению с запросом клиентаsignatureГенерируется по тому же алгоритму, что и node_signatureЕсли это согласуется, это означает, что он проходит, в противном случае аутентификация не удалась.

Конкретные шаги:

Клиент инкапсулирует аксиомы в один слой:

import axios from 'axios'
import sha256 from 'crypto-js/sha256'
import Base64 from 'crypto-js/enc-base64'
// 加密算法,需安装crypto-js
function 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)
// 对axios的每一个请求都做一个处理,携带上签名和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

Затем напишите промежуточное ПО аутентификации на стороне сервера,/server/middleware/verify.js:

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

function crypto (str) {
  const _sign = sha256(str)
  return encodeURIComponent(Base64.stringify(_sign))
}
// 使用和客户端相同的一个秘钥
const SECRET = process.env.SECRET

function verifyMiddleware (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 }

Наконец, укажите это промежуточное ПО в маршруте, который требует аутентификации,/server/index.js:

const { Router } = require('express')
const { verifyMiddleware } = require('../middleware/verify.js')
const router = Router()

// 在需要鉴权的路由加上
router.get('/test', verifyMiddleware, function (req, res, next) {
    res.json({name: 'test'})
})

Обработка статических файлов

Есть корневой каталог/staticПапка, мы надеемся, что к файлам в ней можно получить доступ напрямую через URL-адрес, который должен быть в/server/index.jsДобавьте предложение:

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

app.use('/static', express.static('static'))

В-четвертых, связанные с развитием Nuxt

Жизненный цикл

Nuxt продлевает жизненный цикл Vue примерно следующим образом:

export default {
  middleware () {}, //服务端
  validate () {}, // 服务端
  asyncData () {}, //服务端
  fetch () {}, // store数据加载
  beforeCreate () {  // 服务端和客户端都会执行},
  created () { // 服务端和客户端都会执行 },
  beforeMount () {}, 
  mounted () {} // 客户端
}

asyncData

Этот метод является одним из самых больших преимуществ Nuxt. Возможность рендеринга на стороне сервера здесь. Обязательно используйте этот метод при рендеринге в первый раз. asyncData будет передавать параметр контекста, через который вы можете получить некоторую информацию, например:

export default {
  asyncData (ctx) {
    ctx.app // 根实例
    ctx.route // 路由实例
    ctx.params  //路由参数
    ctx.query  // 路由问号后面的参数
    ctx.error   // 错误处理方法
  }
}

Ошибка рендеринга и ошибка запроса AJAX

  • ошибка рендеринга asyncData

использоватьasyncDataПри подключении он может не отображаться из-за ошибки сервера или ошибки API. В настоящее время страница не была обработана. В этой ситуации необходимо выполнить некоторую обработку. При обнаружении ошибки asyncData перейдите на страницу ошибки. обеспечиваетcontext.errorЭтот метод используется для обработки ошибок. Вызовите этот метод в asyncData, чтобы перейти на страницу с ошибкой.

export default {
    async asyncData (ctx) {
        // 尽量使用try catch的写法,将所有异常都捕捉到
        try {
            throw new Error()
        } catch {
            ctx.error({statusCode: 500, message: '服务器开小差了~' })
        }
    }
}

Таким образом, когда возникает исключение, оно переходит кстраница ошибки по умолчанию, на страницу ошибки можно попасть через/layout/error.vueнастроить.

Здесь есть проблема,context.errorПараметры должны быть примерно такими{ statusCode: 500, message: '服务器开小差了~' },statusCodeДолжен быть код состояния http, И ошибки, возвращаемые нашим сервером, часто имеют другие пользовательские коды, такие как{resultCode: 10005, resultInfo: '服务器内部错误' }, вам нужно преобразовать возвращенную ошибку API.

Для удобства я ввел/plugins/ctx-inject.jsЗарегистрируйте глобальную обработку ошибок для контекста:context.$errorHandler(err). Инъекционный метод может означать:Внедрить $root и контекст,ctx-inject.js:

// 为context注册全局的错误处理事件
export default (ctx, inject) => {
  ctx.$errorHandler = err => {
    try {
      const res = err.data
      if (res) {
        // 由于nuxt的错误页面只能识别http的状态码,因此statusCode统一传500,表示服务器异常。
        ctx.error({ statusCode: 500, message: res.resultInfo })
      } else {
        ctx.error({ statusCode: 500, message: '服务器开小差了~' })
      }
    } catch {
      ctx.error({ statusCode: 500, message: '服务器开小差了~' })
    }
  }
}

затем вnuxt.config.jsИспользуйте этот плагин:

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

После завершения инъекции мы можемasyncDataОн используется следующим образом:

export default {
    async asyncData (ctx) {
        // 尽量使用try catch的写法,将所有异常都捕捉到
        try {
            throw new Error()
        } catch(err) {
            ctx.$errorHandler(err)
        }
    }
}
  • Ошибка в ajax-запросе

Для исключений ajax в это время страница была обработана, и нет необходимости переходить на страницу ошибки при возникновении ошибки.this.$toast.error(res.message)Тост может выйти.

способ загрузки

nuxt имеет встроенную верхнюю часть страницыСтиль индикатора загрузкиРекомендуется, обеспечивая возможность перехода на страницу. Открыть:this.$nuxt.$loading.start()Заканчивать:this.$nuxt.$loading.finish()

Развертывание пакета

Вообще говоря, вы можете упаковать его локально перед развертыванием, запустить его локально, чтобы подтвердить его правильность, а затем загрузить его на сервер для развертывания. Заказ:

// 打包
npm run build
// 本地跑
npm start

Кроме node_modules, .git, .env, залить все остальные файлы на сервер, а потом передатьpm2Для управления можно создать корневой каталог проектаpm2.jsonПростое обслуживание:

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

Затем настройте переменные среды производственной среды, обычно напрямую используя.env.prodКонфигурация:cp ./.env.prod ./.env. Развернут в первый раз или имеет новый пакет зависимостей, который должен быть на сервереnpm installодин раз, затем вы можете использоватьpm2Запустите процесс:

// 项目根目录下运行
pm2 start ./pm2.json

При необходимости можно настроить загрузку на автоматический запуск pm2:pm2 save && pm2 startup. Следует отметить, что каждое развертывание должно перезапускать процесс:pm2 reload nuxt-test.

5. Наконец

Nuxt.js представляет Node, аnuxt.config.jsзамененыmain.jsНекоторые функции, структура каталогов и проект vue немного отличаются, и было добавлено много соглашений, которые могут быть очень незнакомы начинающим студентам.Для получения дополнительной информации вы должны прочитать официальные документы.

исходный код демо:fengxianqi/front_end-demos/src/nuxt-test.