Vue服务端渲染
一、服务端渲染基础
1、概述
我们现在可以使用Vue
,React
等开发SPA
单页面应用,单页面应用的优点,用户体验好,开发效率高,可维护性好等。
缺点:首屏渲染时间长(在客户端通过JS
来生成html
来呈现内容,用户需要等待客户端解析完js
才能够看到页面,这样导致首屏渲染时间变长)
不利于SEO
.(单页面的html
是没有内容的,需要客户端js
解析完才能够生成对应的内容,这样的情况不利于搜索引擎抓取对应的页面内容)
为了解决以上问题,需要借助于服务端的渲染来完成。
在服务端返回生成好的HTML
页面内容,以及客户端SPA
j脚本,这样既有利SEO
,解决首屏加载慢的问题,同时有利于用户的体验。
也就是说通过服务端渲染首屏直出,解决SPA
应用首屏渲染慢以及不利于SEO
的问题,而通过客户端渲染接管页面内容交互得到更好的用户体验。那么这种方式通常称之为现代化的服务端渲染,也叫同构渲染。而这种方式构建的应用称之为服务端渲染应用或者是同构应用。
为了能够更好的掌握服务端渲染的内容,需要了解一些的相关概念:
- 什么是渲染
- 传统的服务端渲染
- 客户端渲染
- 现代化的服务端渲染(同构渲染)
2、什么是渲染
渲染简单的理解就是将数据
与模板
拼接到一起。
对我们前端开发人员来说,我们经常访问服务端的接口,获取数据。然后将获取到的数据通过模板绑定的语法绑定到页面中,最终呈现给用户,这个过程就是我们所说的渲染。
{
"message":'hello vue'
}
<h1>
{
{message}}
</h1>
<h1>
hello vue
</h1>
3、传统的服务端渲染
在早期的WEB
页面渲染都是在服务端进行的,也就是在服务端已经将数据和模板构建好,生成html
,返回给客户端。所以客户端呈现就是包含具体数据内容的页面。
如下图所示:
在上图中最终最重要的就是第四步。
关于这中应用在前面讲解Node
的时候,我们已经学习过。
但是这种方式,有如下的不足:
- 前后端代码完全耦合在一起,不利于开发和维护
- 前端没有足够发挥空间(不利于前后端分离开发)
- 服务端压力大
- 用户体验一般(查看不同的页面,需要刷新浏览器)
4、客户端渲染
在讲解服务端渲染的时候,我们知道了服务端渲染的一些问题,但是随着客户端AJAX
技术的普及,得到了有效的解决。
Ajax
使得客户端动态获取数据成为可能,也就是在服务端的渲染的工作,现在可以在客户端完成。这样带来的好处就是前后端代码完全分离,服务端提供接口,客户端访问接口获取数据,而且有利于前后端开发人员协同开发,提高开发效率。同时由于可以在客户端渲染,从而减轻了服务端的压力。而且也提高了用户体验,进行页面的切换的时候,不会刷新浏览器。
客户端渲染的流程如下图所示:
注意:在第二步返回的html
中没有具体的数据内容。
通过上图,我们可以看到在服务端仅仅是获取了数据,然后将获取到的数据返回给客户端,并不关心页面的渲染。渲染是有客户端完成。
这样就完成了前后端的分离,不仅项目的架构发生了改变,而且开发人员的配置也发生了变化。
也就是前端更为独立,不再受后端的限制。
当然前端渲染也带了了一定的问题,就是前面我们所说的首屏渲染慢
以及不利于SEO
的问题。
下面我们先来看一下为什么客户端渲染会出现首屏渲染慢的问题。
5、为什么客户端渲染首屏渲染慢?
所谓的首屏,是我们在浏览器中输入一个地址后,打开的第一个页面,就是首屏。
我们在浏览器的地址栏中输入了地址,向服务器发送请求,服务器返回的是一个空白HTML
,没有具体的数据内容,只有js
脚本,这时浏览器还会向服务器发送请求获取数据。
而服务端渲染,是在服务端获取数据,然后构建好对应的模板,生成HTML
返回到客户端,这样客户端无需再向服务器发送请求。这样通过对比可以看到,客户端渲染需要多次向服务器发送请求,所以导致渲染慢。
6、为什么客户端渲染不利于SEO
什么是SEO
,就是搜索引擎优化。
7、同构渲染
通过前面的介绍,我们知道了在客户端渲染中,带来的两个比较显著的问题就是,首屏渲染慢和不利于SEO
,那么应该怎样解决这些问题呢?这就需要用到服务端渲染的内容。当然,我们现在提到的服务端渲染与传统的服务端渲染还是有区别的,这里我们称之为现代化的服务端渲染,或者叫做同构渲染。
所谓同构渲染就是后端渲染+前端渲染的模式。这样就集合了前端渲染的优点也集合了服务端渲染的优点。
下面我们具体看一下关于同构渲染的一些具体的介绍:
同构渲染:还是基于React
,Vue
等框架,实现了客户端渲染和服务端渲染的结合,具体的流程就是,在服务端执行一次框架的代码,用于实现服务端渲染,实现首屏的输出,然后在客户端再执行一次,用于接管页面交互(后期的交互都是客户端渲染)。这样就解决了SEO
和首屏渲染慢的问题,拥有传统服务端渲染的优点,也有客户端渲染的优点。
如何实现同构渲染?
第一种方式:使用Vue
,React
等框架的官方解决方案:
这种方式的有点:有助于立即原理。
缺点:需要搭建环境,比较麻烦。
第二种方式:就是使用第三方的解决方案
React
生态的Next.js
Vue
生态的Nuxt.js
8、同构渲染的问题
同构渲染主要如下问题
- 开发条件有限
- 涉及构建设置和部署的更多要求
- 更多的服务器负载
首先来看一下开发条件有限:
浏览器特定的代码只能在某些生命周期钩子函数中使用(因为代码既要在服务端渲染运行,也要在客户端渲染运行,这时需要对服务端渲染的生命周期与客户端渲染的生命周期做一定的区分)。
第二就是一些外部扩展库可能需要特殊的处理以后才能在服务端渲染应用中运行。
第三:不能在服务端渲染期间操作DOM
.
涉及构建设置和部署的更多要求
客户端渲染的构建仅仅构建客户端的应用就可以了,而同构渲染需要构建两个端。
客户端渲染的应用可以部署在任意的Web
服务器中,而同构渲染的应用只能部署在Node.js Server
中。
更多的服务器负载
第一:在Node
中渲染完整的应用程序,相比仅仅提供静态文件的服务器需要大量占用CPU
资源。
第二:如果应用在高流量环境下使用,需要准备相应的服务器负载
第三:x需要更多的服务端渲染优化处理工作。
这时,就会有一个问题:在什么时候使用服务端渲染?
需要你认真的考虑如下两个问题:
第一:是否真的需要提升首屏渲染的速度。
第二:是否真的需要SEO
二、NuxtJS基础
1、什么是NuxtJS
一个基于Vue.js
生态的第三方开源服务端渲染应用框架。它可以帮我们轻松的使用Vue.js
技术栈构建同构应用。
https://zh.nuxtjs.org/
2、初始化NuxtJS项目
关于Nuxt.js
使用的方式:
- 使用
nuxt.js
初始化项目 - 已有的
Node.js
服务端项目,直接把Nuxt
当作一个中间件集成到Node Web Server
中。 - 现有的
Vue.js
项目,需要非常熟悉Nuxt.js
,至少百分之10的代码改动。
初始化Nuxt.js
应用
方式一:使用create-nuxt-app
方式二:手动创建
https://zh.nuxtjs.org/guide/installation
在code
目录下面创建nuxt-demo
目录,然后初始化package.json
文件
npm init -y
安装nuxt
项目
npm i nuxt
安装好以后,打开package.json
,修改启动的配置,如下所示:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev":"nuxt"
},
在上面的代码中,配置了dev
,启动nuxt
项目。
下面在项目中创建pages
目录,该目录下面存放页面组件的,该目录的名字是固定的。
下面在pages
目录下面创建一个index.vue
,这就是网站的首页组件。
内容如下:
<template>
<div>
<h1>Hello Nuxt</h1>
</div>
</template>
<script>
export default {
name:'HomePage'
}
</script>
<style >
</style>
下面启动项目
npm run dev
当项目启动了以后,在浏览器中就会呈现出上面index.vue
文件中的内容。
这里内部集成路由机制(Nuxt.js
依据pages
目录结构自动生成vue-router
模块的路由配置),默认的就会查找pages
目录,将index.vue
首页的内容展示出来。
下面我们可以在pages
目录下面再次创建一个页面about.vue
内容如下:
<template>
<div>
<h1>Hello About</h1>
</div>
</template>
<script>
export default {
name:'AboutPage'
}
</script>
<style >
</style>
在浏览器的地址栏中输入:
http://localhost:3000/about
就可以查看pages
目录下面的about
页面中的内容,从这里我们可以看出,其内部定义了默认的路由机制。
3、基本路由
从这一小节开始,我们来学习一下Nuxt
中的路由,关于路由在其官方网站中也有明确的说明:
https://zh.nuxtjs.org/guide/routing
这一小节,我们先来学习基本路由。
这里可以先查看文档,文档内容如下:
要在页面之间使用路由,我们建议使用<nuxt-link> 标签。
例如:
<template>
<nuxt-link to="/">首页</nuxt-link>
</template>
基础路由
假设 pages
的目录结构如下:
pages/
--| user/
-----| index.vue
-----| one.vue
--| index.vue
那么,Nuxt.js
自动生成的路由配置如下:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'user',
path: '/user',
component: 'pages/user/index.vue'
},
{
name: 'user-one',
path: '/user/one',
component: 'pages/user/one.vue'
}
]
}
下面,我们可以在我们的项目中做一个简单的测试。
在pages
目录下面创建user
目录,在该目录下面创建index.vue
文件,对应的内容为:
<template>
<div>
<h1>Hello User</h1>
</div>
</template>
<script>
export default {
name:'UserPage'
}
</script>
<style >
</style>
在浏览器的地址栏中输入:
http://localhost:3000/user
就可以看到上面的页面中的内容了。
当然,这些路由规则,在nuxt
目录下的router.js
文件中已经定义好了。
4、路由导航
路由导航有如下三种实现方式:
a
标签:它会刷新整个页面,不要使用
nuxt-link
组件,类似于router-link
编程式导航,也就是通过js
跳转路由,也类似于vue-router
中的编程式导航。
在pages/about.vue
页面中添加如下的内容来进行测试。
<template>
<div>
<h1>Hello About</h1>
<h2>a链接</h2>
<a href="/">首页</a>
<h2>nuxt-link</h2>
<nuxt-link to="/">首页</nuxt-link>
<h2>编程式导航</h2>
<button @click="onClick">首页</button>
</div>
</template>
<script>
export default {
name:'AboutPage',
methods:{
onClick(){
this.$router.push('/')
}
}
}
</script>
<style >
</style>
关于编程式导航中的push
的用法,可以参考vue-router
中的编程式导航的内容。
5、动态路由
nuxt
中的动态路由与Vue Router
中的动态路由是一样的。
在 Nuxt.js
里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的Vue
文件 或 目录。
以下目录结构:
pages/
--| _slug/
-----| comments.vue
-----| index.vue
--| users/
-----| _id.vue
--| index.vue
Nuxt.js
生成对应的路由配置表为:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'users-id',
path: '/users/:id?',
component: 'pages/users/_id.vue'
},
{
name: 'slug',
path: '/:slug',
component: 'pages/_slug/index.vue'
},
{
name: 'slug-comments',
path: '/:slug/comments',
component: 'pages/_slug/comments.vue'
}
]
}
你会发现名称为 users-id
的路由路径带有 :id?
参数,表示该路由是可选的。如果你想将它设置为必选的路由,需要在 users/_id
目录内创建一个 index.vue
文件。
下面,我们演示一下:
在user
目录下面创建了_id.vue
,该文件中的代码如下:
<template>
<div>
<p>{
{$route.params.id}}</p>
</div>
</template>
<script>
export default {
name:'UserPage'
}
</script>
这时在浏览器的地址栏中:
http://localhost:3000/user/3
可以看到输出的编号
6、嵌套路由
nuxt
中的嵌套路由与vue-router
中的是一样的。
你可以通过 vue-router
的子路由创建 Nuxt.js
应用的嵌套路由。
创建内嵌子路由,你需要添加一个Vue
文件,同时添加一个与该文件同名的目录用来存放子视图组件
别忘了在父组件(.vue
文件) 内增加 <nuxt-child/>
用于显示子视图内容。
假设文件结构如:
pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
Nuxt.js
自动生成的路由配置如下:
router: {
routes: [
{
path: '/users',
component: 'pages/users.vue',
children: [
{
path: '',
component: 'pages/users/index.vue',
name: 'users'
},
{
path: ':id',
component: 'pages/users/_id.vue',
name: 'users-id'
}
]
}
]
}
下面,演示一下具体的实现,在pages
目录下面创建users.vue
,具体内容如下:
<template>
<div>
<h1>Users父路由</h1>
<!-- 子路由出口 -->
<nuxt-child></nuxt-child>
</div>
</template>
<script>
export default {
}
</script>
在上面的代码中,是将pages
目录下面的users.vue
这个文件作为了父路由,所以加上了nuxt-child
作为子路由的出口。
那么子路由应该怎样定义呢?
注意:在pages
目录下面再次创建一个目录叫做users