目录
7.3 Nuxt.js中如何实现异步数据获取(asyncData方法)?
一、 什么是服务器端渲染?
server side render 前端页面的产生是由服务器端生成的,我们就称之为服务端渲染
1.1 初始化项目
首先,在 ssr 根目录下新建 server 文件夹
其次,在 server 文件夹里通过 npm init -y 命令初始化一个 node 项目
然后,通过 npm install express --save 命令安装 express 框架(express官方文档)
最后,在项目根目录下新建 app.js 文件,作为服务端的入口文件
1.2 服务端渲染小案例
app.js
const express = require('express')
const app = express()
const port = 3000
// 当路径为跟路径,返回完整的html片段
app.get('/', (req, res) => res.send(`
<html>
<body>
<h1>hi,hello</h1>
</body>
</html>
`))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
在项目根目录下打开终端,通过 node app.js 运行服务器
在浏览器里输入,http://localhost:3000 查看效果,如下:
1.3 右键查看源代码
总结:所谓的服务端渲染指的是页面的内容完全是由服务端侧决定到底要展示出什么内容
二、什么是客户端渲染?
client side render 服务端只提供json格式的数据,渲染成什么样子由客户端通过js控制
通过vite快速创建一个基于vue框架的客户端渲染样例
2.1 初始化项目
首先,在 ssr 目录下新建 client 文件夹
我们使用vite工具快速生成一个vue项目,https://vitejs.dev/
其次,通过 npm init @vitejs/app my-vue-app --template vue 命令(npm 6.x)快速创建一个基于 vite 的 名为 my-vue-app 的 vue 项目
若出现如下即表示项目创建成功:
最后,利用cmd命令通过 npm run dev 启动项目
注意:此时可能会出现---> 'vite' 不是内部或外部命令,也不是可运行的程序
或批处理文件。若出现以上提示,那么只需输入 npm install 后,再通过 npm run dev 即可启动项目
http://localhost:3000
2.2 查看源代码
结论:通过查看源代码我们发现,源代码并没有显示我们页面中实际渲染的内容,我们只看到一个main.js文件,和一个id为app的根元素,所以我们知道网页内容是通过js来动态的进行渲染的,js运行在浏览器,浏览器也就是客户端,这种由浏览器端的js做主导渲染网页内容的方式,我们就称之为客户端渲染
思考题:如何得知一个网站是哪种方式的渲染?
三、客户端渲染vs服务端渲染
客户端渲染我们叫做CSR渲染方式,服务端渲染我们叫做SSR渲染
3.1 运行架构对比
说明
- CSR执行流程:浏览器加载html文件 -> 浏览器下载js文件 -> 浏览器运行vue代码 -> 渲染页面
- SSR执行流程:浏览器加载html文件 -> 服务端装填好内容 -> 返回浏览器渲染
3.2 开发模式对比
CSR:前后端通过接口JSON数据进行通信,各自开发互不影响
SSR:前后端分工搭配复杂,前端需要写好html模板交给后端,后端装填模板内容返给浏览器
客户端渲染(CSR) | 服务端渲染(SSR) | |
---|---|---|
首次渲染时间 | 长 | 很短 |
seo支持 | 差 | 良好 |
前后端分工开发效率 | 快 | 慢 |
思考:如果我们的项目既想要使用vue高效率的开发项目,同时还想要首屏渲染时间很短,那该怎么办?
四、vue框架中的服务端渲染
为了解决第3章节提出的问题,目前我们的vue组件都是在浏览器侧通过js渲染出来的,所以首次加载时间很慢,那么我们把vue组件交给服务端负责渲染,渲染为完整内容之后直接返给客户端,是不是就可以可以解决既想渲染快,还想继续使用vue进行开发的问题了?
4.1 初始化项目
在 ssr 目录下新建 vue-ssr 文件夹
把之前 server文件夹中的所有文件拷贝到刚创建的 vue-ssr 文件夹里
通过 npm install vue vue-server-renderer --save 命令安装必要的依赖
4.2 vue服务端渲染最小demo
app.js
const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()
server.get('*', (req, res) => {
const app = new Vue({
data: {
url: req.url
},
template: `<div>访问的 URL 是:{{ url }}</div>`,
})
renderer.renderToString(app, (err, html) => {
if (err) throw err
res.send(html)
})
})
server.listen(8888,() => console.log(`Example app listening on port 8888!`))
在项目根目录下通过 node app.js 启动服务器
在浏览器里输入:http://localhost:8888 查看
查看源代码:
结论:我们通过在服务器端渲染vue组件的方式,让网页中又有了完整的内容,这样我们就可以既使用了vue开发又节省了首次渲染时间
4.3 遗留问题
修改app.js,添加一个button元素并使用vue的方式绑定click事件
// 目标:是把vue组件的渲染从客户端放到了服务端
// 目的: 既想使用vue继续开发项目 享受vue提供的高效的开发方式 而且我们还想让首屏渲染时间短
// 解决方案:把vue组件的渲染的地方放到服务端进行即可
// 引入vue框架
const Vue = require('vue')
// 引入express框架 进行服务端功能开发
const server = require('express')()
// 引入vue服务端渲染插件 帮助我们把vue组件渲染成字符串
const renderer = require('vue-server-renderer').createRenderer()
// 编写一个通用路由 -> new一个Vue实例 -> 把vue实例渲染成字符串(组件字符串)
server.get('*', (req, res) => {
// 实例化vue
const app = new Vue({
data: {
url: req.url
},
template: `
<div>
访问的 URL 是:{{ url }}
<button @click="alert(123)">click me!</button>
</div>`,
})
// 把vue实例渲染成字符串
renderer.renderToString(app, (err, html) => {
if (err) throw err
res.send(html)
})
})
// 监听一个服务 端口8888
server.listen(8888,() => console.log(`Example app listening on port 8888!`))
// 1.通过访问源代码 我们发现确实是服务端渲染 保证了首屏渲染时间短
// 2.我们还继续使用了vue的组件开发模式
运行发现,页面成功显示了button按钮,但是可惜的是,没有成功绑定事件,点击无效
事实上除了事件没有绑定之外,服务器端虽然完成了vue的渲染,但是给到客户端的时候已经成了字符串了,一系列我们熟悉的vue应用的特性,我们都无法使用,比如数据响应式更新,那该怎么办呢?
为了解决以上问题,我们需要引入一个新的概念,称作 同构
五、理解同构理念
一套vue(react)代码,在服务端执行一次,在客户端再执行一次,就做同构
const app = new Vue({
data: {
url: req.url
},
template:
`<div>
访问的 URL 是:{{ url }}
<button @click="alert('123')">click me!</button>
</div>`
})
上面所示的vue代码,我们在服务端的执行保持不变,只要我们把这段代码在客户端再重新执行一遍,不就可以拥有原本vue应用的所有特性了么,确实如此,不过这个过程的难度太大,我们现在只需要理解,所谓的同构是指:同一套vue代码在服务端执行一次在客户端再执行一次
- 服务端执行完成渲染解决了首次加载速度慢的问题
- 浏览器执行解决了绑定事件及恢复vue本身特性的问题
六、Nuxt.js框架使用
nuxt.js是一套使用vue框架开发应用的服务端渲染框架,提供了开箱即用的功能
6.1. 使用nuxt.js创建一个ssr项目
在根目录下也就是 ssr 目录下通过 npm create nuxt-app <项目名> 创建项目
按照提示选择项目之后完成创建,需要注意,这一步要选择ssr
6.2 启动项目
进入到项目根目录,通过 npm run dev 运行项目
在浏览器里输入 http://localhost:3000 查看
6.3 查看源代码
显然,我们看到了网页上有实际渲染的内容,这是服务端负责的渲染
6.4 搭建首页
pages/index.vue
在nuxt.js生成的项目中我们依旧像之前一样写单文件组件.vue代码,ElementUI组件也可以正常使用
<template>
<div class="container">
<Logo />
<div class="articleList">
<el-collapse>
<el-collapse-item title="一致性 Consistency" name="1">
<div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
export default {}
</script>
<style>
.container{
padding:0 200px;
}
.articleList{
margin-top:30px;
}
</style>
6.5 异步数据获取
1. 认识asyncData方法
asyncData
方法会在组件(限于页面组件)每次加载之前被调用。它可以在服务端或路由更新之前被调用,你可以利用 asyncData
方法来获取数据,Nuxt.js 会将 asyncData
返回的数据融合组件 data
方法返回的数据一并返回给当前组件
官网推荐使用的请求方式 https://axios.nuxtjs.org/
async asyncData({ $axios }) {
const ip = await $axios.$get('http://icanhazip.com')
return { ip }
},
data(){
return {
name:'cp'
}
}
----合并完之后的data数据----
{
name:'cp',
ip
}
2. 获取文章列表(移动端项目)
async asyncData ({ $axios }) {
const url = 'http://ttapi.research.itcast.cn/app/v1_1/articles?channel_id=0×tamp=1606309443970&with_top=1'
const res = await $axios.$get(url)
// eslint-disable-next-line no-console
console.log('文章数据列表:', res)
return {
list: res.data.results
}
}
3. 渲染接口数据
<el-collapse>
<el-collapse-item v-for="item in list" :key="item.id" :title="item.title.slice(0,40)">
<div>评论数:{{ item.comm_count }} 点赞数:{{ item.like_count }}</div>
</el-collapse-item>
</el-collapse>
4. 预览效果并查看源代码
结论:我们完成了既使用vue开发模式,又实现了服务端渲染模式,nice~
完整 inde.vue代码
<template>
<div class="container">
<Logo /> {{ name }}
<div class="articleList">
<el-collapse>
<el-collapse-item
v-for="article in articleList"
:key="article.art_id"
:title="article.title"
>
<div>评论数:{{ article.comm_count }}点赞数:{{ article.like_count }}</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
export default {
// asyncData
// 调用时机:页面组件中组件渲染之前 自动调用
// 特点:nuxtjs会帮助我们把asyncData函数的返回值 和 data配置项返回的响应式数据做融合处理 融合起来之后一同交给
// 当前的组件使用
// api调用写法:async + await的形式 正常使用axios就可以
async asyncData ({ $axios }) {
// 使用axios发起get请求
const url = 'http://ttapi.research.itcast.cn/app/v1_1/articles?channel_id=0×tamp=1606309443970&with_top=1'
const res = await $axios.$get(url)
// eslint-disable-next-line no-console
console.log('返回值为:', res.data.results)
return {
articleList: res.data.results
}
},
data () {
return {
name: 'cp'
}
}
}
// 1.如何加入路由功能呢?
// 2.如果在nuxtjs中使用状态管理工具呢?vuex
// 3.使用nuxt.js开发的项目如何完成部署上线呢? 依赖什么? 客户端 客户端 服务端
</script>
<style>
.container{
padding:0 200px;
}
.articleList{
margin-top:30px;
}
</style>
七、总结
7.1 服务端渲染和客户端渲染各自指什么?有什么特点?
SSR 服务端渲染 网页内容由服务端生成 首屏时间短 有利于seo CSR 客户端渲染 vue、react框架渲染方式 spa都是客户端渲染 首屏渲染时间长不利于seo
7.2 同构的本质是什么?
一份vue代码在服务端渲染一遍 然后在客户端再渲染一遍 服务端渲染解决了首屏显示快 客户端渲染是需要把事件、响应式特性等vue经典的特性都绑回去 我们既可以使用vue的开发模式 又可以享受俩种渲染方式的优势
7.3 Nuxt.js中如何实现异步数据获取(asyncData方法)?
asyncData函数时Nuxtjs框架内置的一个函数 执行结果和和data进行融合 一起返给当前组件