Nuxt.js实战和配置详解

前段时间刚好公司有项目使用了Nuxt.js来搭建,而刚好在公司内部做了个分享,稍微再整理一下发出来。本文比较适合初用Nuxt.js的同学,主要讲下搭建过程中做的一些配置。建议初次使用Nuxt.js的同学先过一遍官方文档,再回头看下我这篇文章。

一、为什么要用Nuxt.js

原因其实不用多说,就是利用Nuxt.js的服务端渲染能力来解决Vue项目的SEO问题。

二、Nuxt.js和纯Vue项目的简单对比

 1. build后目标产物不同

vue: dist

nuxt: .nuxt

2. 网页渲染流程

vue: 客户端渲染,先下载js后,通过ajax来渲染页面;

nuxt: 服务端渲染,可以做到服务端拼接好html后直接返回,首屏可以做到无需发起ajax请求;

3. 部署流程

vue: 只需部署dist目录到服务器,没有服务端,需要用nginx等做Web服务器;

nuxt: 需要部署几乎所有文件到服务器(除node_modules,.git),自带服务端,需要pm2管理(部署时需要reload pm2),若要求用域名,则需要nginx做代理。

4. 项目入口

vue: /src/main.js ,在main.js可以做一些全局注册的初始化工作; nuxt: 没有main.js入口文件,项目初始化的操作需要通过 nuxt.config.js 进行配置指定。

三、从零搭建一个Nuxt.js项目并配置 新建一个项目

直接使用脚手架进行安装:

npx create-nuxt-app <项目名>

大概选上面这些选项。

值得一说的是,关于 Choose custom server framework (选择服务端框架),可以根据你的业务情况选择一个服务端框架,常见的就是Express、Koa,默认是None,即Nuxt默认服务器,我这里选了 Express

  • 选择默认的Nuxt服务器,不会生成 server 文件夹,所有服务端渲染的操作都是Nuxt帮你完成,无需关心服务端的细节,开发体验更接近Vue项目,缺点是无法做一些服务端定制化的操作。
  • 选择其他的服务端框架,比如 Express ,会生成 server 文件夹,帮你搭建一个基本的Node服务端环境,可以在里面做一些node端的操作。比如我公司业务需要(解析protobuf)使用了 Express ,对真正的服务端api做一层转发,在node端解析protobuf后,返回json数据给客户端。

还有 Choose Nuxt.js modules (选择nuxt.js的模块),可以选 axiosPWA ,如果选了axios,则会帮你在nuxt实例下注册 $axios ,让你可以在.vue文件中直接 this.$axios 发起请求。

开启eslint检查

nuxt.config.js 的build属性下添加:

 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统一管理环境变量

在node端,我们喜欢使用 dotenv 来管理项目中的环境变量,把所有环境变量都放在根目录下的 .env 中。

安装:

npm i dotenv

使用: 在根目录下新建一个 .env 文件,并写上需要管理的环境变量,比如服务端地址 APIHOST :

APIHOST=http://your_server.com/api

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

但如果node端和客户端需要使用同一个环境变量时(后面讲到API鉴权时会使用同一个SECRET变量),就需要同时在 nuxt.config.js.env 维护这个字段,比较麻烦,我们更希望环境变量只需要在一个地方维护,所以为了解决这个问题,我找到了 @nuxtjs/dotenv 这个依赖,它使得nuxt的客户端也可以直接使用 .env ,达到了我们的预期。

安装:

npm i @nuxtjs/dotenv

客户端也是通过 process.env.XXX 来使用,不再举例啦。

这样,我们通过 dotenv@nuxtjs/dotenv 这两个包,就可以统一管理开发环境中的变量啦。

另外, @nuxtjs/dotenv 允许打包时指定其他的env文件。比如,开发时我们使用的是 .env ,但我们打包的线上版本想用其他的环境变量,此时可以指定build时用另一份文件如 /.env.prod ,只需在 nuxt.config.js 指定:

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

@nuxtjs/toast模块

toast可以说是很常用的功能,一般的UI框架都会有这个功能。但如果你的站点没有使用UI框架,而alert又太丑,不妨引入该模块:

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进行鉴权,防止被人轻易盗用我们node端的API,因此我们需要做一个API的鉴权机制。常见的方案有jwt,可以参考一下阮老师的介绍: 《JSON Web Token 入门教程》 。如果场景比较简单,可以自行设计一下,这里提供一个思路:

  1. 客户端和node端在环境变量中声明一个秘钥:SECRET=xxxx,注意这个是保密的;
  2. 客户端发起请求时,将当前时间戳(timestamp)和 SECRET 通过某种算法,生成一个 signature ,请求时带上 timestampsignature
  3. node接收到请求,获得 timestampsignature ,将 timestamp 和秘钥用同样的算法再生成一次签名 _signature
  4. 对比客户端请求的 signature 和node用同样的算法生成的 _signature ,如果一致就表示通过,否则鉴权失败。

具体的步骤:

客户端对axios进行一层封装:

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端写一个鉴权的中间件, /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会传进一个context参数,通过该参数可以获得一些信息,如:

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

渲染出错和ajax请求出错的处理

asyncData渲染出错

使用 asyncData 钩子时可能会由于服务器错误或api错误导致无法渲染,此时页面还未渲染出来,需要针对这种情况做一些处理,当遇到asyncData错误时,跳转到错误页面,nuxt提供了 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注册一个全局的错误处理方法: context.$errorHandler(err) 。注入方法可以参考: 注入 $root 和 contextctx-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) toast出来即可。

loading方法

nuxt内置了页面顶部loading进度条的样式推荐使用,提供页面跳转体验。 打开: 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

五、最后

Nuxt.js引入了Node,同时nuxt.config.js替代了main.js的一些作用,目录结构和vue项目都稍有不同,增加了很多的约定,对于初次接触的同学可能会觉得非常陌生,更多的内容还是得看一遍官方的文档。

demo源码: fengxianqi/front_end-demos/src/nuxt-test

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • nuxt.js中间件实现拦截权限判断的方法

    最近在把vue的项目改成基于nuxt.js是 ssr渲染,需要进行权限拦截判断,网上查了不少资料.最终看官方手册解决了,贴一下过程 项目环境 "element-ui":"^2.3.4", "flyio":"^0.5.2", "js-cookie":"^2.2.0", "nuxt":"^1.4.0", 一:首先登录页面 在登录页面script中引入

  • Nuxt.js实战详解

    一.为什么选择Nuxt.js 多数是基于webpack构建的项目,编译出来的html文件,资源文件都被打包到js中,以下图404页面代码为例.从代码中可以看出,这样的页面是不利于 搜索引擎优化(SEO, Search Engine Optimization) ,并且 内容到达时间(time-to-content) (或称之为首屏渲染时长)也有很大的优化空间.为了解决以上问题,引入了 Nuxt.js 框架. vue官网对于Nuxt.js也是很推荐的,除此之外,Nuxt.js的开发者积极活跃,版本迭

  • 详解Nuxt.js Vue服务端渲染摸索

    本文采用nuxt进行服务端渲染https://zh.nuxtjs.org/ Nuxt.js 十分简单易用.一个简单的项目只需将 nuxt 添加为依赖组件即可. Vue因其简单易懂的API.高效的数据绑定和灵活的组件系统,受到很多前端开发人员的青睐.国内很多公司都在使用vue进行项目开发,我们正在使用的简书,便是基于Vue来构建的. 我们知道,SPA前端渲染存在两大痛点:(1)SEO.搜索引擎爬虫难以抓取客户端渲染的页面meta信息和其他SEO相关信息,使网站无法在搜索引擎中被用户搜索到.(2)用

  • Nuxt.js踩坑总结分享

    构建问题 1. 如何在 head 里面引入js文件? 背景: 在<head>标签中,以inline的形式引入flexible.js文件.本项目主要为移动端项目,引入flexible.js 实现移动端适配问题. Nuxt.js 通过 vue-meta 实现头部标签管理,通过查看文档发现,可以按照如下方式配置: // nuxt.config.js head: { script: [ { innerHTML: 'console.log("hello")', type: 'text

  • Nuxt.js 数据双向绑定的实现

    假定我们有一个需求,一开始通过mounted()将一个字符串渲染在页面上,但是我们经过操作后修改了数据并且需要将得到的结果重新异步渲染到页面中去,而不是跳转刷新页面来重新渲染 首先模板中data()中定义数据,并且要将定义的数据显示出来 <template> <div> <span @click="click">{{ text }}</span> </div> </template> <script>

  • nuxt.js 缓存实践

    nuxt 是基于 vue 的 ssr 解决方案,可以是使用vue语法完成前后端的同构. 然而在与传统纯字符串拼接的 ssr 方案相比,性能就没那么好了, nuxt 需要在服务端生成虚拟 dom ,然后再序列化出HTML字符串,我们常说 nodejs 的高性能指的是异步IO操作频繁的场景而非CPU操作密集的场景,毕竟 nodejs 是运行在单线程下的,在涉及到高并发的场景下,性能就会有所下降,可以考虑采用合理的缓存策略 nuxt 的缓存可以分为 组件级别缓存 , API级别缓存 以及 页面级别缓存

  • Nuxt.js实现校验访问浏览器类型的中间件

    前言 Nuxt.js 十分简单易用.一个简单的项目只需将 nuxt 添加为依赖组件即可. 这个需求非常常见,分享出去的页面的有时候在手机访问,有时候别人是PC打开的; 若是不是共享同一个页面的情况,就需要拦截跳转了; 当然你要共享同一个页面也可以(放大化到PC也需要添加某些CSS,也需判断设备) 思路 本质上还是校验 UA , 只是这次是从 req 拿到,而不是从客户端获取再做处理 客户端的处理的姿势 window.navigator.userAgent 服务端的处理姿势 其实基本和上面的思路一

  • Nuxt.js之自动路由原理的实现方法

    Nuxt.js 是一个基于 Vue.js 的通用应用框架.集成了Vue 2.Vue-Router. Vuex.Vue-Meta,用于开发完整而强大的 Web 应用. 它的特性有服务端渲染.强大的路由功能,支持异步数据.HTML头部标签管理等. 今天主要介绍的就是Nuxt的特性之一,强大的路由功能.nuxt.js会根据pages目录结构自动生成vue-router模块的路由配置. nuxt源码地址: https://github.com/nuxt/nuxt.js 建议打开源码跟着文章的步骤看,第一

  • Nuxt.js SSR与权限验证的实现

    Nuxt.js是一个建立在Vue.js基础上的服务端渲染框架.它非常易于上手并且可以让你在几分钟内构建你的应用. 服务端渲染是一个解决所有SPA的SEO问题的伟大解决方案,但不幸的是它带来了另一个问题:权限验证成了另一个项目管理中的痛点. Nuxt.js官网提供了一个称为"路由鉴权"的示例(https://nuxtjs.org/examples/auth-routes).它展示了如何通过一个中间件来限定一个页面是否可访问,但是这个检查是在客户端的进行的并且服务端渲染出的内容无论是否进行

  • Nuxt.js开启SSR渲染的教程详解

    第一节:nuxt.js相关概述 nuxt.js简单的说是Vue.js的通用框架,最常用的就是用来作SSR(服务器端渲染).Vue.js是开发SPA(单页应用)的,Nuxt.js这个框架,用Vue开发多页应用,并在服务端完成渲染,可以直接用命令把我们制作的vue项目生成为静态html. 1.那服务器端渲染到底有什么好处呢? 主要的原因时SPA(单页应用)不利于搜索引擎的SEO操作,Nuxt.js适合作新闻.博客.电影.咨询这样的需要搜索引擎提供流量的项目.如果你要作移动端的项目,就没必要使用这个框

  • Vue在 Nuxt.js 中重定向 404 页面的方法

    Nuxt.js 依据 pages 目录结构自动生成 vue-router 模块的路由配置. 要在页面之间使用路由,我们建议使用<nuxt-link> 标签. 正文 对我来说,Nuxt 是我所用过最好用的软件代码之一.它可以让我高效率地打造网站应用,无论这些应用是 SPA,还是 SSR,又或者是静态站点,这同时也是 JAM Stack 上面所提到的趋势. 顺便提一点,我们这个网站Vuedose 也是使用 Nuxt 所建立静态站点. 但是,绝大多数自己领悟的很有意思的使用技巧并没有被记载沉淀下来,

随机推荐