服务端预渲染之Nuxt(使用篇)

现在大多数开发都是基于 Vue 或者 React 开发的,能够达到快速开发的效果,也有一些不足的地方, Nuxt 能够在服务端做出渲染,然后让搜索引擎在爬取数据的时候能够读到当前页面。

首先要说明一点,我们可以认为我们所编写的 Vue 项目是一个服务端的项目,虽然编写的还是 Vue 项目,但是 Nuxt 是基于服务器环境的。

就简单的说一下 Nuxt 使用。基础只是还是以官方文档为主,如果博客中哪里有问题,欢迎留言指正。

说了这么多,进入正题。

路由

与传统的 Vue 项目不同的是,我们在使用 Vue 的时候需要配置 Vue-Router 信息,在 Nuxt 有很关键的一点就是 约定优于配置 。 page 目录下的所有 *.vue 文件会自动生成路由配置。

在项目初始化之后,在 pages 下面默认有一个 index.vue 文件,所以当我们使用 npm run dev 启动项目,并且使用 http://localhost:3000/ 访问的时候能够正常访问路由。

为了证实上面这一点,在 pages 下面创建一个信息 about.vue 文件,并且 http://localhost:3000/about 去访问刚刚写的页面。我们可以按照正常的 Vue 页面去开发就好了。

page目录

├─page
│ ├─index.vue
└───└─about.vue

about.vue

<template>
 <div>
  <h2>This About</h2>
 </div>
</template>

创建完成之后使用 http://localhost:3000/about 访问该页面,页面能够正常的渲染出来了。就会看到 This About 显示在页面中。

做到这一步之后就应该实现路由之间的跳转了。 Vue 开发过程中,都是使用 router-link 标签完成路由之间的跳转,在 Nuxt 也同样可以使用 router-link ,但是 Nuxt 仍然推荐使用 nuxt-link , nuxt-link 与 router-link 的功能是等效的。

可能会有一些疑问,既然是等效的,为什么要使用 nuxt-link 呢?官方文档中是这样说的:将来我们会为 nuxt-link 组件增加更多的功能特性,例如资源预加载,用于提升 nuxt.js 应用的响应速度。显然嘛,官方不会无缘无故的就做出这么一个东西来,肯定实在其中做了很多的优化工作的。

稍微的改动一下刚才的 about.vue 在里面添加两个标签,一个使用 nuxt-link ,一个使用 router-link 看下能否正常完成跳转。

about.vue - 更改后

<template>
 <div>
  <h2>This About</h2>
  <nuxt-link to="/">首页</nuxt-link>
  <router-link to="/">首页</router-link>
 </div>
</template>

既然从路由开始那么就不得不说到子路由,全局路由守卫这些都些在路由中经常用到的应该怎么处理?该怎么解决这些问题。

前面既然说到了 Nuxt 会把 pages 文件夹下面的所有 *.vue 文件编译成路由,那么子路由需要使用文件夹嵌套才行。

接下来就尝试一下。首先要更改一下 pgeas 目录结构。

page目录

├─page
│ ├─about
│ │ ├─detail.vue
│ │ └─index.vue
└───└─index.vue

注意上面的 about 目录,是 index.vue 而并非 about.vue ,这里的 index.vue 指的是 about 路由下的首页,也就是最开始放在与 index.vue 同级的那个 about.vue 是一样的效果。

about/index.vue

<template>
 <div>
  <h2>This About</h2>
  <nuxt-link to="/">首页</nuxt-link>
  <router-link to="/">首页</router-link>
 </div>
</template>
about/detail.vue
<template>
 <div>
 <h2>This Detail</h2>
 </div>
</template>

现在如果我们想要访问刚才的那两个路由地址分别就是 http://localhost:3000/about 和 http://localhost:3000/about/detail 就能看到刚才编写的 page 页面了。

如果想要看路由生成到底是什么样子的?可以在根目录下有一个 .nuxt 文件夹,在里面可以看到一个 router.js ,这个文件夹下面就是 Nuex 生成好的路由信息。

打开文件后翻到最后会有一段这样的代码,是不是很眼熟?这是不就是在编写 Vue 项目的时候配置的哪些路由文件么?

router.js

export function createRouter() {
 return new Router({
 mode: 'history',
 base: decodeURI('/'),
 linkActiveClass: 'nuxt-link-active',
 linkExactActiveClass: 'nuxt-link-exact-active',
 scrollBehavior,
 routes: [{
  path: "/about",
  component: _9ceb4424,
  name: "about"
 }, {
  path: "/about/detail",
  component: _18146f65,
  name: "about-detail"
 }, {
  path: "/",
  component: _d3bf5a4e,
  name: "index"
 }],
 fallback: false
 })
}

有了这个文件的话我们就可以清楚的知道,路由的结构了。不仅仅这样,还可以使用 name 去实现路由的跳转了。

需要注意的是,如果你的路由是有文件夹嵌套的话, Nuxt 是用使用 - 来拼接路由的 name 名称的(如: about-detail ),但是文件夹内部的 index.vue 会直接已文件夹的名字作为 name 。一旦知道了路由的 name ,这样我们就可以使用命令的方式跳转路由了。

再次更改一下 about/index.vue 。

about/index.vue

<template>
 <div>
  <h2>This About</h2>
  <nuxt-link :to="{name:'about-detail'}">详情</nuxt-link>
  <router-link :to="{name:'index'}">首页</router-link>
  <button @click="onClick">跳转到详情</button>
 </div>
</template>
<script>
export default {
 methods: {
 onClick() {
  this.$router.push({name:"about-detail"})
 }
 }
}
</script>

使用路由访问 http://localhost:3000/about 地址,分别点击详情、首页与 button ,都是能够正常跳转的,与之前的 Vue 开发是完全没有任何区别的。在 vue-router 中有一个很重要的一个点就是 动态路由 的概念,如果想要实现动态路由应该怎么处理呢?

如果想要在 Nuxt 中使用动态路由的话,需要在对应的路由下面添加一个 _参数名.vue 的文件,在 about 文件下面添加一个 _id.vue

page目录

├─page
│ ├─about
│ │ ├─detail.vue
│ │ ├─_id.vue
│ │ └─index.vue
└───└─index.vue

新建完成之后在去 router.js 中看一下更改后的路由结构

export function createRouter() {
 return new Router({
 mode: 'history',
 base: decodeURI('/'),
 linkActiveClass: 'nuxt-link-active',
 linkExactActiveClass: 'nuxt-link-exact-active',
 scrollBehavior,
 routes: [{
  path: "/about",
  component: _9ceb4424,
  name: "about"
 }, {
  path: "/about/detail",
  component: _18146f65,
  name: "about-detail"
 }, {
  path: "/about/:id",
  component: _6b59f854,
  name: "about-id"
 }, {
  path: "/",
  component: _d3bf5a4e,
  name: "index"
 }],
 fallback: false
 })
}

可以明显的看到在 /about/:id 这个路由,明显的变化不止这些变动的还有 name: "about-id" 不再是之前的 name:about 了。如果想要使用这个 id 的话必须在 _id.vue 中才能获取到。

**_id.vue**

<template>
 <div>
 {{$route.params.name}}
 {{$route.params.id}}
 </div>
</template>

在 _id.vue 中编写以上代码并使用 http://localhost:3000/about/ABC ,可以看到在页面中已经展示了当前的 id 值。

在实际开发过程当中可能 params 可能会有多个参数,又应该怎么处理呢?

调整目录结构

// id为可选参数
├─page
│ ├─about
│ │ ├─_name
| | | └─_id
| | |  └─index.vue
│ │ └─index.vue
└───└─index.vue

**about - _name - _id.vue**

<template>
 <div>
 {{$route.params.name}}
 {{$route.params.id}}
 </div>
</template>

弄完之后看下 router.js 的变化

export function createRouter() {
 return new Router({
 mode: 'history',
 base: decodeURI('/'),
 linkActiveClass: 'nuxt-link-active',
 linkExactActiveClass: 'nuxt-link-exact-active',
 scrollBehavior,
 routes: [{
  path: "/about",
  component: _9ceb4424,
  name: "about"
 }, {
  path: "/about/detail",
  component: _18146f65,
  name: "about-detail"
 }, {
  path: "/about/:name",
  component: _2ec9f53c,
  name: "about-name"
 }, {
  path: "/about/:name/:id",
  component: _318c16a4,
  name: "about-name-id"
 }, {
  path: "/",
  component: _d3bf5a4e,
  name: "index"
 }],
 fallback: false
 })
}

这里展示的是第二种情况, id 为必选参数的情况,路由被编译的结果。

虽然路由已经添加了参数,但是 id 属性不是必填属性,这样的话不能满足项目需求又要如何处理呢?很简单的,在 _id.vue 文件同目录下添加一个 index.vue 文件就可以了。

// id为必选参数
├─page
│ ├─about
│ │ ├─_name
| | | ├─_id.vue
| | | └─index.vue
│ │ └─index.vue
└───└─index.vue

需要注意的是,一定要在 _id.vue 文件中使用传入的参数,直接获取在 index.vue 中是拿不到任何信息的。但是如果访问 http://localhost:3000/about/ABC 这样的路由的话,实在 index.vue 中是可以获取到 name 参数的。

在刚才的 router.js 文件中生成的所有的路由都是平级的,如何实现路由的嵌套,如果想要实现嵌套路由的话,必须有和当前路由同名的文件夹存在,才能完成路由的嵌套。

page目录

├─page
│ ├─about
| | ├─_id.vue
| | └─detaile.vue
│ ├─about.vue
└───└─index.vue

router.js

export function createRouter() {
 return new Router({
 mode: 'history',
 base: decodeURI('/'),
 linkActiveClass: 'nuxt-link-active',
 linkExactActiveClass: 'nuxt-link-exact-active',
 scrollBehavior,
 routes: [{
  path: "/about",
  component: _76687814,
  children: [{
  path: "",
  component: _9ceb4424,
  name: "about"
  }, {
  path: ":id",
  component: _6b59f854,
  name: "about-id"
  }]
 }, {
  path: "/",
  component: _d3bf5a4e,
  name: "index"
 }],
 fallback: false
 })
}

更改完目录结构,那我们嵌套的路由应该如何展示?在 vue.js 中开发的时候使用 router-view 这个标签完成的。为了性能的优化 Nuxt 也提供了一个对应的标签 nuxt-child 。

如果想实现嵌套路由传参需要稍微的改动一下目录结构,按照上面的方法实现就好了,下面是一个路由结构的例子。

page目录

├─page
│ ├─about
│ │ ├─detail
| | | ├─_id.vue
| | | └─index.vue
│ │ └─index.vue
└───└─index.vue

router.js

export function createRouter() {
 return new Router({
 mode: 'history',
 base: decodeURI('/'),
 linkActiveClass: 'nuxt-link-active',
 linkExactActiveClass: 'nuxt-link-exact-active',
 scrollBehavior,
 routes: [{
  path: "/about",
  component: _76687814,
  name: "about",
  children: [{
  path: "detail",
  component: _0a09b97d,
  name: "about-detail"
  }, {
  path: "detail/:id?",
  component: _fa7c11b6,
  name: "about-detail-id"
  }]
 }, {
  path: "/",
  component: _d3bf5a4e,
  name: "index"
 }],
 fallback: false
 })
}

在 _id.vue 中则可以使用id这个参数了。访问路由 http://localhost:3000/about/detail/123 ,依然可以拿到传入的 id 为 123 的这个参数。

说了这么多了,还有很多问题没得说完,关于路由的全局守卫又应该如何去使用?在 Nuxt 根目录下有个 plugins 文件夹。首先要做的是在里面创建一个名为 router.js 文件。

plugins-router.js

export default ({app}) => {
 app.router.beforeEach((to,form,next) => {
 console.log(to)
 next();
 });
}

导出了一个函数,在这个函数中可以通过结构拿到 vue 的实例对象名叫 app 。需要注意的是,这个 beforeEach 函数的执行,有可能会在服务端也会有可能在客户端输出。客户端首次访问的页面会在服务端做输出,一旦渲染完成之后,则不会再在服务端输出,则会一直在客户端进行输出了。

说到这里做个小插曲,那么又该怎么区分当前是在客户端环境还是服务端环境呢?可以使用 process.server 获取到当前的运行环境,其得到的是 Boolean 值, true 服务端, fasle 客户端。

做了这些之后去访问路由,仿佛没有任何输出,无论实在客户端还是在服务端,都没有任何打印输出,中间缺少了步骤,需要在根目录下找到 nuxt.config.js 对插件进行配置。

nuxt.config.js

const pkg = require('./package')
module.exports = {
 plugins: [
 '@/plugins/element-ui',
 '@/plugins/router'
 ]
}

由于更改了 Nuxt 配置需要重启一下服务,才能正常执行刚刚写入的插件。然后访问刚刚写入的路由,会看在服务端初次渲染的时候,会输出我们想要的那些东西,进行路由跳转的话,会在客户端输出,这也就证明了 Nuxt 只会做首屏的服务器渲染。

路由说了这么接下来需要说一下 Nuxt 是如何为指定的路由配置数据做渲染的。其实 Nuxt 在做渲染的时候包裹了很多层。首先有一个 Document 作为其模板,然后再去寻找其布局的页面,找到对应的页面之后,再根据引用去找到相关的组件进行渲染,数据请求与数据挂载,一系列完成之后,把剩余的路由信息返还给客户端,渲染完成,这个就是 Nuxt 简单的渲染流程。

在上面提到了一个 布局页面 ,这个东西应该去哪里找?又应该怎么做呢?它对于项目而言对于开发又有什么好处?在 Nuxt 根目录下有一个 layouts 文件夹,下面有一个 default.vue 这个文件就是上面提到的渲染页面,也就同等于 vue 开发中的 App.vue ,在这里可以做很多事情。例如添加一个全局的导航。

在 layouts 文件夹添加一个 about.vue 文件写入如下内容,接下来需要在 pages 下面的 about.vue 中通知,对应 pages 使用哪个布局页面,不写则使用默认,然后访问 http://localhost:3000/about 相关的页面,只要是和 about 相关的页面,都会展示这个内容。

layouts - about.vue

<template>
 <div>
 <h2>Aaron 个人博客主页</h2>
 <nuxt></nuxt>
 </div>
</template>

pages - about.vue

<template>
 <div>
  <h2>About</h2>
  <nuxt-child></nuxt-child>
 </div>
</template>
<script>
export default {
 layout:"about"
}
</script>

访问一下所有与 about 页面有关的页面,都会看到 Aaron个人博客主页 这个字样,若访问根路由则无法看到的。

如果做过 mvc 开发的话,如果页面发生错误会跳转到一个错误页面的。 Nuxt 也是有默认的错误页面的,但是全是英文而且样式也不太好看,不能自定义样式。如何自定义错误页面呢?

在 layouts 文件夹中新建一个 error.vue 文件。

layouts - error.vue

<template>
 <div>
  <h1>这里是错误页面</h1>
  <h2 v-if="error.statusCode == 404">404 - 页面不存在</h2>
  <h2 v-else>500 - 服务器错误</h2>
  <ul>
    <li><nuxt-link to="/">HOME</nuxt-link></li>
  </ul>
 </div>
</template>

<script>
export default {
 props:["error"]
}
</script>

在 error.vue 中可以通过 props 拿到一个 error 对象,获取到 error 错误信息之后能做任何想要做的事情。需要注意的一点是,自定意的错误页面,只能在客户端访问失效的时候才会响应到该页面,若在服务端的话,是无法直接渲染这个页面的。

更改页面配置 Nuxt 中有些全局的配置,配置信息在 nuxt.config.js 更改其全局配置, pages 文件夹中的 *.vue 文件也是可以配置的,页面私有的配置会覆盖掉全局的配置。

举例:

export default {
 layout:"about",
 head: {
  title:"About"
 }
}

在这些全局配置中最重要的一个就是 asyncData 这个属性。 asyncData 到底是用来做什么的呢?这个数据可以在设置组件的数据之前能一步获取或者处理数据。也就是说在组件渲染之前先获取到数据,然后等待挂载渲染。

举个例子:

<template>
 <div>
   <h2>姓名:{{userInfo.name}}</h2>
   <h2>年龄:{{userInfo.age}}</h2>
   <nuxt-child></nuxt-child>
 </div>
</template>
<script>
let getUserInfo = () => {
 return new Promise(resolve => {
  setTimeout(() => {
   let data = {"name":"Aaron","age":18};
   resolve(data);
  })
 })
}
export default {
 layout:"about",
 head: {
  title:"About"
 },
 async asyncData(){
  const userInfo = await getUserInfo();
  return {userInfo}
 }
}
</script>

一定要 return 出去获取到的对象,这样就可以在组件中使用,这里返回的数据会和组件中的 data 合并。这个函数不光在服务端会执行,在客户端同样也会执行。

注意事项:

1.asyncData 方法会在组件(限于页面组件)每次加载之前被调用
2.asyncData 可以在服务端或路由更新之前被调用
3.第一个参数被设定为当前页面的上下文对象
4.Nuxt会将 asyncData 返回的数据融合到组件的data方法返回的数据一并返回给组件使用
5.对于 asyncData 方式实在组件初始化前被调用的,所以在方法内饰没办法通过this来引用组件的实例对象

刚刚提到了一点就是上下问对象,在上线文对象中可以获取到很多东西,如路由参数,错误信息等等等,这里就不作太多赘述了,有了这些可以做一些页面的重定向或者其他工作,比如参数校验,安全验证等工作。

路由扯了一大堆,接下来说一下如何在 Nuxt 中融入 axios 的使用。

安装 axios

npm install @nuxtjs/axios --save-dev

安装完成后更改配置信息:

nuxt.config.js

module.exports = {
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
  ],
  axios: {
    proxy:true // 代理
  },
  proxy: {
    "/api/":"http://localhost:3001/"  // key(路由前缀):value(代理地址)
  }
}

主要说名一下 proxy 这里, /api/ 在请求的时候遇到此前缀则会只指向的代理地址去请求数据。

既然说到了 axios ,就不得不提到的一个东西就是拦截器,很是有用在项目开发过程中必不可少的。

举个例子:

module.expores{
  plugins: [
    '@/pluginx/axios'
  ]
}
plugins/axios.js
export default ({$axios,request}) => {
  $axios. onRequest((config) => {
    config.headers.token = "Aaron"
  })
}

总结

说了这么多也许会有些纰漏,或者遗漏的知识点,若有什么错误的地方可以留言,尽快做出改正。谢谢大家花费这么长时间阅读这篇文章。

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

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。