什么是 SSR ?
# SSR
# 什么是 SSR?
英语全称为 Server Side Render。翻译成中文就是服务器端渲染。
回顾整个web开发的历程:
- 服务器端渲染
- 单页应用
在最早的时候,采用的就是服务器端渲染,那个时候是把数据装填到模板里面,在服务器端生成具体的页面,返回给客户端。
后面流行单页应用,向客户端返回一个空的 html 页面,再去发送请求,将各个模块请求到客户端,在客户端通过前端路由来调控显示哪一个模块。
但是这个单页应用有一个很大的缺陷:
不利于 SEO,有一个很大的问题,就是首屏渲染的问题。
所以后面又重新提出了服务器端渲染。
但是这一次服务器端渲染和之前是不一样,这一次只在服务器端渲染首屏,客户端请求这个页面,到达客户端后,重新变成一个单页应用。
vue ssr 快速入门示例如下:
首先创建一个项目目录,npm init -y 初始化这个项目,安装如下的依赖:
npm i vue vue-server-renderer express
接下来在项目根目录下面创建一个 server.js,代码如下:
// 这是一个服务器文件
const Vue = require('vue');
// 创建一个渲染器
// 回头我们会将首屏的内容使用这个渲染器在服务器端渲染出来
const renderer = require('vue-server-renderer').createRenderer();
// 创建服务器
const server = require('express')();
// 创建一个 vue 实例
// 使用 vue 来作为服务器端的模版
const app = new Vue({
template : `
<div>Hello {{ name }}</div>
`,
data(){
return {
name : 'William'
}
}
})
// 创建服务器
server.get('*',(req,res)=>{
renderer.renderToString(app,(err, html)=>{
if(err) throw err;
res.send(html);
})
})
server.listen(8080,()=>{
console.log('服务器端已经启动...');
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
之后,启动这个服务器,node server,查看 network,可以看到页面里面不再是一个空壳 div,而是由内容的:
<div data-server-rendered="true">Hello William</div>
上面的示例,就是一个简单的服务器端渲染的示例。
但是上面的示例,还欠缺了很多东西,比如到达客户端后,应用会重新变成一个单页应用,那么涉及到具体的路由的设置,状态的管理。
如果手动的进行配置,非常的麻烦,好在官方提供了一个 vue ssr 的框架 nuxt。
# Nuxt 框架的基本使用
Nuxt 是一个 vue 的通用框架。
之前我们学习过 vue-cli,它是一个脚手架工具,可以快速的搭建一个 vue项目,但是它搭建的项目,只能是单页应用项目。
Nuxt 框架,也是快速搭建一个 vue 项目,但是在搭建的时候,可以选择搭建单页应用还是SSR项目。
Nuxt 也可以将我们的 vue 项目生成静态页面。
首先第一步,我们需要安装 Nuxt 框架。
npx 和 npm 的区别在于,npm会下载对应的包到我们的电脑,但是npx不会,npx在用完之后会将包删除掉。
// 方法一:
npx create-nuxt-app <项目名>
// 如果电脑上有 vue-cli 脚手架
vue init nuxt/starter <项目名>
2
3
4
5
6
搭建好项目之后,我们来认识 nuxt 项目的结构:
- .nuxt:Nuxt 自动生成的,build 的文件
- assets:存放静态资源的,比如 less、sass 以及 css 文件
- components:存放组件的,主要是指功能性组件。
- layouts:布局的目录,负责项目的布局结构
- middleware:存放中间件的目录
- pages:存放页面的,这个是我们的主要工作区域
- plugins:插件目录,用于存放插件的。
- static:静态资源,图片、图标
- store:存放组件状态的仓库
- .eslintrc.js:eslint 的配置文件
- .editorconfig:配置编辑器的
- nuxt.config.js:nuxt 的配置文件
在 nuxt 中,没有配置路由的目录,究其原因,是因为在 nuxt 中,会根据的 pages 目录,自动生成路由。
# 常用配置项
(1)配置 ip 和端口号
在 package.json里面对 config进行配置:
"config" : {
"nuxt" : {
"host" : "127.0.0.1",
"port" : "1818"
}
},
2
3
4
5
6
(2)配置全局 css
比如我们首先在 assets/css/main.css,书写一个全局的样式:
/* 全局css样式 */
html{
color: red;
}
2
3
4
接下来在 nuxt.config.js 里面,加入这个全局样式:
css: [
'~assets/css/main.css'
],
2
3
(3)loader
如果涉及到 loader 的配置,也可以在 nuxt.config.js 中进行配置。
build: {
loaders : []
}
2
3
# 路由配置
在 nuxt 中,不需要配置路由,nuxt可以根据我们的目录结构,自动生成路由。
在 pages 目录下面创建对应的目录 about 和 news
<template>
<div>
<h2>这是 about 页面</h2>
<ul>
<li>
<nuxt-link to="/">返回首页</nuxt-link>
</li>
</ul>
</div>
</template>
2
3
4
5
6
7
8
9
10
<template>
<div>
<h2>这是 news 页面</h2>
<ul>
<li>
<nuxt-link to="/">返回首页</nuxt-link>
</li>
</ul>
</div>
</template>
2
3
4
5
6
7
8
9
10
在 index.vue 这是我们的首页,代码如下:
<template>
<ul>
<li>
<nuxt-link to="/">首页</nuxt-link>
</li>
<li>
<nuxt-link to="/about">About 页面</nuxt-link>
</li>
<li>
<nuxt-link to="/news">News 页面</nuxt-link>
</li>
</ul>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
接下来,我们来看一下 nuxt 中如何传递参数:
首先,在跳转的路由上,使用动态绑定,参数就可以绑定在 params 里面,如下:
// pages/index.vue
<li>
<nuxt-link :to="{name:'news',params:{newsID:128}}">News 页面</nuxt-link>
</li>
2
3
4
接下来,在对应的组件里面,可以接收该参数,如下:
// pages/news/index.vue
<template>
<div>
<h2>这是 news 页面</h2>
<p>newID:{{$route.params.newsID}}</p>
<ul>
<li>
<nuxt-link to="/">返回首页</nuxt-link>
</li>
</ul>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
# 嵌套路由
目前为止,我们要跳转到哪个页面,具体的写法是建立一个该页面的文件夹,里面书写一个index.vue。
嵌套路由,首先创建具体的组件以及文件夹。
详细见代码
# 动态参数和参数校验
所谓动态路由,就是指带参数的路由。
这种情况在做详情页的时候很常见。详情页首先要拿到一个 id,根据这个 id 查询该 id 所对应的具体信息,然后才能决定这个组件渲染什么信息。
书写方式如下:
首先在对应的页面创建 _id.vue 组件,这个组件就是接收动态的参数,然后根据这个参数和后台服务器进行交互。
// /pages/news/_id.vue
<template>
<div>
<h2>News-Content: {{$route.params.id}}</h2>
<nuxt-link to="/">回到首页</nuxt-link>
</div>
</template>
2
3
4
5
6
7
接下来在 news 页面,我们就可以设置两个链接,链接后面跟动态的参数,一般是 id。
<template>
<div>
<h2>这是 news 页面</h2>
<p>newID:{{$route.params.newsID}}</p>
<ul>
<li>
<nuxt-link to="/">返回首页</nuxt-link>
</li>
<li>
<nuxt-link to="/news/123">New1</nuxt-link>
</li>
<li>
<nuxt-link to="/news/456">New2</nuxt-link>
</li>
</ul>
</div>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在进入页面的时候,nuxt 还会我们提供了参数的校验方法。
代码如下:
<script>
export default {
validate({params}){
// 要求参数必须为数字
return /^\d+$/.test(params.id)
}
}
</script>
2
3
4
5
6
7
8
# 路由的动画效果
在 nuxt 中,提供两种方式的路由动画效果,一种是全局的,一种是局部的。
# 全部的动画效果
顾名思义,就是会对所有的页面进行应用。比如我们这里给所有的页面设置一个渐变的效果。
// assets/css/main.css
.page-enter-active, .page-leave-active{
transition: opacity .5s;
}
.page-enter, .page-leave-active{
opacity: 0;
}
2
3
4
5
6
7
8
# 局部样式
所谓局部样式,就是只针对某一个路由有效果。
首先还是书写样式,如下:
.test-enter-active, .test-leave-active{
transition: all 1s;
font-size: 12px;
}
.test-enter, .test-leave-active{
opacity: 0;
font-size: 40px;
}
2
3
4
5
6
7
8
9
接下来在具体的组件中使用 transition来设置这个路由,如下:
<template>
<div>
<h2>这是 about 页面</h2>
<ul>
<li>
<nuxt-link to="/">返回首页</nuxt-link>
</li>
</ul>
</div>
</template>
<script>
export default {
transition : 'test'
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 默认模板和默认布局
默认模板就是提供一个公共的页面,后面的所有页面都在这个公共页面里面显示。
在项目根目录下面创建一个 app.html,这个页面就是我们的默认模板。
pages所有的内容都会显示在这个模板下面的 的位置
<!DOCTYPE html>
<html lang="en">
<head>
{{ HEAD }}
</head>
<body>
<p>这个页面就是所有页面的公共页面</p>
<p>所有页面在显示内容的时候,都会显示这两行文字</p>
<p>{{ APP }}</p>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
# 默认布局
就是针对每一个页面使用同一个布局,基本上和默认模版有点像,但是是针对的每一个页面。
代码如下:
<template>
<div>
<p>页面前置内容</p>
<Nuxt />
<p>页面后置内容</p>
</div>
</template>
2
3
4
5
6
7
# 个性化错误页面
在 layouts 里面创建一个 error.vue,触发错误的时候,会向这个组件自动传入一个 error 对象,类似于事件对象。
<template>
<div>
<h2 v-if='error.statusCode === 404'>404错误页面显示</h2>
<h2 v-if='error.statusCode === 500'>500错误页面显示</h2>
</div>
</template>
<script>
export default {
props : ['error']
}
</script>
2
3
4
5
6
7
8
9
10
11
12
# asyncData 方法获取数据
SSR中做首屏渲染的时候,存在一种情况,我们要向服务器发送请求,请求回来的数据渲染到首屏里面。那么这里就涉及到一个问题,我们需要等待这个异步的操作结果。
nuxt 中,就提供了一个asyncData 的方法。这个方法是在加载组件之前调用,它可以在服务器端端渲染组件之前就调用,获取到数据。
使用示例如下:
<template>
<div>
<div v-for="(item,index) in info" :key="index">
{{item}}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data(){
return {
info : []
}
},
asyncData() {
// 千万要注意,不能用 this,因为该方法会在组件加载前就调用,这个时候组件都还没加载,自然而然this就没有
return axios.get('https://autumnfish.cn/api/joke/list?num=5')
.then(res=>{
return {info : res.data.jokes}
})
},
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
一定要注意,asyncData 方法是在组件加载前调用,这个时候还没有 this。
除了 asyncData 方法以外,还有一种方式,fetch 方法。
fetch 方法用于在渲染页面前填充应用的状态树数据。
<script>
import axios from 'axios'
export default {
fetch({store,params}){
return axios.get('http://www.xxx.com/api')
.then((res)=>{
store.commit('setStore',res)
})
}
}
</script>
2
3
4
5
6
7
8
9
10
11
# 静态资源的使用
直接引入即可。注意在 nuxt 里面一个 ~ 代表定位到项目根目录,而在 cli 里面 @ 代表定位到 src 目录。
<template>
<div>
<ul>
<li>
<nuxt-link to="/">首页</nuxt-link>
</li>
<li>
<nuxt-link to="/about">About 页面</nuxt-link>
</li>
<li>
<nuxt-link :to="{ name: 'news', params: { newsID: 128 } }"
>News 页面</nuxt-link
>
</li>
<li>
<nuxt-link to="/contact">contact</nuxt-link>
</li>
<li>
<nuxt-link to="/asyncData">asyncData</nuxt-link>
</li>
<li>
<img src="~static/ok.png" alt="" />
</li>
</ul>
<div class="test"></div>
</div>
</template>
<style scoped>
.test{
width: 200px;
height: 200px;
border: 1px solid;
background: url('~static/ok.png') no-repeat;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 打包
nuxt 允许我们打包成两种形式:静态资源和普通的打包。
// 打包为静态资源(html)
npm run generate
2
打包完成后,会在项目根目录下面生成一个 dist 目录,里面就全部都是 html 静态资源,直接部署到服务器上面即可。
// 普通
npm run build
2
如果是普通打包,在 .nuxt 目录下面会生成一个 dist 目录。
普通打包,会生成两个 bundle,一个 client,一个server
接下来如果要进行部署,需要选中如下的几个文件:
- .nuxt
- nuxt.config.js
- package.json
- static
部署到服务器上面之后,npm i 安装依赖,之后npm start 启动应用即可。
-EOF-