服务端预渲染之Nuxt (使用篇)
服务端预渲染之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合并。这个函数不光在服务端会执行,在客户端同样也会执行。
注意事项:
- asyncData 方法会在组件(限于页面组件)每次加载之前被调用
- asyncData 可以在服务端或路由更新之前被调用
- 第一个参数被设定为当前页面的上下文对象
- Nuxt会将 asyncData 返回的数据融合到组件的data方法返回的数据一并返回给组件使用
- 对于 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: [
'@/plugins/axios'
],
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
],
}
plugins/axios.js
export default ({ $axios, redirect }) => {
$axios.onRequest(config => {
console.log('Making request to ' + config.url)
})
$axios.onError(error => {
const code = parseInt(error.response && error.response.status)
if (code === 400) {
redirect('/400')
}
})
}
总结
说了这么多也许会有些纰漏,或者遗漏的知识点,若有什么错误的地方可以在下方留言,尽快做出改正。谢谢大家花费这么长时间阅读这篇文章。
服务端预渲染之Nuxt (使用篇)的更多相关文章
- 服务端预渲染之Nuxt(介绍篇)
现在前端开发一般都是前后端分离,mvvm和mvc的开发框架,如Angular.React和Vue等,虽然写框架能够使我们快速的完成开发,但是由于前后台分离,给项目SEO带来很大的不便,搜索引擎在检索的 ...
- 服务端预渲染之Nuxt(爬坑篇)
Nuxt是解决SEO的比较常用的解决方案,随着Nuxt也有很多坑,每当突破一个小技术点的时候,都有很大的成就感,在这段时间里着实让我痛并快乐着.在这里根据个人学习情况,所踩过的坑做了一个汇总和总结. ...
- vue-cil 服务端预渲染 prerender-spa-plugin
众所周知单页面应用不利于SEO,为了解决这个问题网上所给出的2个解决方案1.SSH服务器端渲染2.预渲染由于页面较少,且预渲染相对于SSH比较简单,于是选择预渲染页面,预渲染可以极大的提高网页访问速度 ...
- 服务端技术进阶(八)GitHub入门篇
服务端技术进阶(八)GitHub入门篇 前言 在投递简历的过程中,发现有的公司会要求填写自己的GitHub地址,而自己却还没有GitHub帐号,准确点说是自己还不太会使用GitHub.(貌似开源社区中 ...
- Java 服务端监控方案(四. Java 篇)
http://jerrypeng.me/2014/08/08/server-side-java-monitoring-java/ 这个漫长的系列文章今天要迎来最后一篇了,也是真正与 Java 有关的部 ...
- 对爱奇艺PC Web主站来说,良好的SEO能够帮助其获得更多的搜索流量,因而页面上一些非常重要的内容仍然需要依靠服务端进行渲染,由于另外开发一套基于Node的SSR后台成本较高,而乐趣(基于java和velocity模板引擎)平台作为渲染系统已经十分成熟且运行稳定,在充分试验后,我们决定在Uniqy中使用服务端同步与客户端浏览器异步二次渲染相结合的方式,结合Vue2.0提供的 slot插槽机制,很
https://mp.weixin.qq.com/s/eB20BoqzENO_oNk8eDg4Eg 干货|爱奇艺PC Web新框架实践 原创: 前端研发团队 爱奇艺技术产品团队 昨天
- Angular开发实践(六):服务端渲染
Angular Universal Angular在服务端渲染方面提供一套前后端同构解决方案,它就是 Angular Universal(统一平台),一项在服务端运行 Angular 应用的技术. 标 ...
- 使用 PHP 来做 Vue.js 的 SSR 服务端渲染
对于客户端应用来说,服务端渲染是一个热门话题.然而不幸的是,这并不是一件容易的事,尤其是对于不用 Node.js 环境开发的人来说. 我发布了两个库让 PHP 从服务端渲染成为可能.spatie/se ...
- Headless Chrome:服务端渲染JS站点的一个方案【中篇】【翻译】
接上篇 防止重新渲染 其实说不对客户端代码做任何修改是忽悠人的.在我们的Express 应用中,通过Puppteer加载页面,提供给客户端响应,但是这个过程是有一些问题的. js脚本在服务端的Head ...
随机推荐
- Pattern recognition and machine learning 疑难处汇总
不断更新ing......... p141 para 1. 当一个x对应的t值不止一个时,Gaussian nosie assumption就不合适了.因为Gaussian 是unimodal的,这意 ...
- 进阶-MongoDB 知识梳理
MongoDB 一.MongoDB简介 MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.它在许多场景下可用于替代传统的关系型数据库或键/值存储方式.Mo ...
- python+selenium实现登录账户
selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Gr ...
- maven的pom文件中配置测试用例
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> ...
- iOS开发-程序的生命周期
为了更好的管理程序,了解程序的生命周期是很有必要的. 运行结果: 1.首次启动: 2015-05-26 17:33:28.362 Live[4858:214241] 程序开始 2015-05-26 1 ...
- 二十二、Hadoop学记笔记————Kafka 基础实战 :消费者和生产者实例
kafka的客户端也支持其他语言,这里主要介绍python和java的实现,这两门语言比较主流和热门 图中有四个分区,每个图形对应一个consumer,任意一对一即可 获取topic的分区数,每个分区 ...
- Flask开发微电影网站(一)
1.用到的Flask知识 1.使用整形,浮点型,路径型,字符串型下正则表达式路由转化器 2.使用GET与POST请求,上传文件,cookie获取与响应,404处理 3.使用模板自动转义,定义过滤器,定 ...
- PHP使用文件排它锁,应对小型并发
总所知周,并发容易造成数据的重复处理,我的一个项目给游戏客户端提供了接口,游戏用户相对较多,如果使用数据库排它锁消耗的资源较高,因而使用了文件的排它锁来应对并发. 思路是打开一个文件并取得文件的独占锁 ...
- redis缓存本地安装教程
http://www.runoob.com/redis/redis-install.html
- 打开office时提示错误窗口“向程序发送命令时出现问题”的解决方案
今天同事问了我一件很怪异的事情,说她的office打不开了,如打开word或excel时,突然出现错误提示错误窗口"向程序发送命令时出现问题",分析原因才知道她安装了 AVG pc ...