整体目标:
用户从浏览器地址栏中输入地址访问服务器上的页面时,服务器可以用两种不同的策略来生成这个页面,对应着程序员写的不同的实现方式。
打开给出的示意代码,观察效果。
(1)服务器端渲染Server Side Render
渲染是指:从数据到dom的过程(从json到html结构)
数据到dom的过程是发生在服务器端
客户端浏览器在地址栏中输入网页的地址,取回来的就是html页面。
(2)客户器端渲染 Client Side Render
渲染是指:数据到dom的过程 (从json到html结构)
数据到dom的过程是发生在客户端浏览器
客户端浏览器在地址栏中输入网页的地址,取回来的只是html骨架,
客户端渲染技术对SEO不友好,爬虫无法获取有效内容!
Search Engine Optimization ,搜索引擎优化。
加入我们有个学习JavaScript网页,希望被更多人的知道,而推广自已的网页的方式之一是借助搜索引擎的力量,让其它人在百度中搜索某个关键字时就能找到你的网页。
那么,百度是如何得知http://xxxx.com/abc·········这个页面中有关键字javascript
的呢?
百度服务器会使用一些程序来获取网页的内容,分析内容,以提取出关键字,便 于在搜索时能找到网页。 这个过程一般称为爬虫。
SEO的目标是更明确地告诉百度,你的网页上的内容,以便更好地被收录。
我们平常上网都是在浏览器中请求网页地址,而爬虫代码是通过代码的方式去请求网页地址。
下面就是一个简陋的爬虫:
步骤:
// 引入http模块
const http = require("http")
// 定义要爬虫程序 访问的 网页
let url = "http://localhost:8080/index_csr.html"
// let url = "http://localhost:8080/index_ssr.html"
// 用http模块中的get方法去请求 url 这个网页,把请求到的结果显示出来。
http.get(url, (res) => {
let result = ""
res.setEncoding('utf8');
res.on('data', (chunk) => {
result += chunk
});
res.on('end', () => {
console.log(`爬虫得到的数据是: ${
result}`);
});
});
运行js文件
node spider.js
这一段代码会去请求代码中url指定的网页(可在这里下载运行示例代码),并打印出结果。
小结
对于采用客户端渲染的网页:爬虫程序无法获取有效的数据。(因为有效的数据是通过ajax在客户端发出来,去取回来的) 所以客户端渲染对seo不友好,这就是为什么vue单页面应用不利于seo优化的原因。
在网页开发的初期 (2000-2010年),没有前后端的概念的,也没有前端工程师的概念。所有的网页上的内容:html,css,js,数据 , 包括提供数据的服务器代码 全是程序员写的。
我们的技术全是属于SSR范筹。 (php,asp,asp.net, jsp…)
优点 | 缺点 | |
---|---|---|
客户器渲染(通过ajax求数据) | 适合前后端分离开发,方便维护,单页应用(Singe Page Application)中几乎都是客户端渲染 | 首屏加载慢,不利于 SEO |
服务器渲染 | 响应速度快,有利于 SEO | 前后端代码混合在一起,难以开发和维护,不适合进行前后端分离开发 |
我们使用vue开发的项目都是典型的SPA(单页面应用程序single page application),是标准的前后端分离的,很显然是属于客户端渲染的范畴。
是否有相对折中的解决方案,或者是两全其美的解决方案呢?
即vue做的项目也可以达到seo优化等实现服务器端渲染的优点的实现
Vue的SSR文档
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为**服务器端的 HTML 字符串
**,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
创建一个名为vue-ssr的空目录进入,并运行:
npm init --yes
来做初始化。
参考
npm install vue vue-server-renderer --save
新建文件为01.js,内容如下:
const Vue = require('vue')
// 第 1 步:创建一个 vue实例
const app = new Vue({
template: `{
{title}}`,
data:{
title:"hello,vue ssr!"
}
})
// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()
// 第 3 步:将 Vue 实例渲染为 HTML
// 方法一:回调函数
renderer.renderToString(app, (err, html) => {
if (err) throw err
console.log(html)
// => Hello World
})
运行结果:
node 01.js
输出结果如下:
<div data-server-rendered="true">hello,vue ssr!</div>
<div data-server-rendered="true">hello,vue ssr!</div>
小结:
vue-server-renderer
就可以把Vue实例解析成 它对应的dom结构目标:
请求网页http://localhost:3000/index.html
,在服务器端使用vue-server-renderer来渲染生成html文档,并返回。
涉及npm包:
npm i express
创建02.js,代码如下:
/**
* 目标: 用户输入http://localhost:3000/index.html
* 在服务器使用vue-server-renderer来把vue实例渲染生成html文档,并返回
*
* 1) 先安装express
*/
const express = require('express')
const app = express()
const Vue = require('vue')
app.get('/index.html',(req, res) => {
// 1. 创建vue实例
const vm = new Vue({
template: `
{
{title}}
{
{item.author}} - {
{item.content}}
` ,
data: {
title: 'vue ssr',
list: [{
author:"李白",content:"举杯邀明月"},
{
author:"杜甫",content:"喝酒不开车"},
{
author:"杜甫",content:"喝酒不开车"}]
}
})
// res.send('ok')
// 2. 创建一个renderer
const renderer = require('vue-server-renderer').createRenderer()
// 3. 渲染:把vue实例渲染成HTML字符串
renderer.renderToString(vm, (err, html) => {
if (err) {
console.log(err)
} else {
res.send(html)
}
})
})
app.listen(3000, ()=>{
console.log('3000.....')
})
运行
node 02.js
访问:
http://localhost:3000/index.html
小结:
在服务器使用vue,通过vue-server-render包,来把vue实例转成html字符串,再返回给客户端,这就是用vue技术的ssr。
如果我们实际项目中需要使用vue做服务器端渲染那么我们可以使用Nuxt服务器端渲染框架完成服务器端适配
基于 Vue、Webpack 和 Babel Nuxt.js 集成了以下组件/框架,用于开发完整而强大的 Web 应用:
Vue 2 / Vue3
Vue-Router
Vuex
Vue 服务器端渲染
Vue-Meta
注意:
不是
Vue官方**提供的基于Vue
**的服务端渲染的框架服务端渲染
**的应用所需要的各种配置。next.js : react 服务器渲染框架
nuxt.js : vue 服务器渲染框架
第一步:全局安装脚手架工具
npm i -g create-nuxt-app
----------------------------------
C:\Users\fanyoufu\AppData\Roaming\npm\create-nuxt-app -> C:\Users\fanyoufu\AppData\Roaming\npm\node_modules\create-nuxt-app\lib\cli.js
> [email protected] postinstall C:\Users\fanyoufu\AppData\Roaming\npm\node_modules\create-nuxt-app\node_modules\ejs
> node ./postinstall.js
Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)
npm WARN notsup Unsupported engine for [email protected]: wanted: {
"node":">=10.20.0"} (current: {
"node":"10.16.3","npm":"6.13.4"})
npm WARN notsup Not compatible with your version of node/npm: [email protected]
+ [email protected]
added 38 packages from 4 contributors, removed 3 packages and updated 162 packages in 61.806s
第二步:创建项目
# 格式: create-nuxt-app 项目名
create-nuxt-app nuxt-demo
本示例中的采用的方案
如果你选择了某个ui框架,则在创建项目时,就会自动给你配置好。
运行启动命令npm run dev
启动成功:
在浏览器查看效果:
|--components:组件
|--layouts:布局
|--middleware: 中间件
|--plugins:插件
|--static:静态资源
|--store: vuex
|--nuxt.config.js
最重要的就是pages!
npm run dev
如果没有什么错误,你应该可以在localhost:3000
中看到代码运行的效果了。
在pages文件夹,新建一个普通的page1.vue文件。
放在pages下面的.vue文件,nuxt会自动去给它创建路由
/pages/page1.vue
<template>
<div>
<h1>page1</h1>
<p>{
{
title }}</p>
<nuxt-link to="/">
首页
</nuxt-link>
</div>
</template>
<script>
export default {
data () {
return {
title: '剑客'
}
}
}
</script>
注意:
上面的例子中,http://localhost:3000/page1 得到的内容是
也就是说,从服务器取回来的是包含数据的网页。这时就满足单页面应用特点同时也满足服务端渲染。
在从地址:http://localhost:3000/page1 转到 http://localhost:3000/ 时,页面并不刷新。
这就是典型的单页应用特点。
在我们前面学习的vue项目中,都是在客户端渲染:
服务器端渲染是:
讨论发ajax取数据的过程。
由于在创建项目时,已经选择了axios,所以,在vue的实例中,可以直接通过this.$axios来使用axios.
在page1.vue中改写代码如下。
就是一个最简单的ajax请求,并用v-for渲染数据的例子。
<template>
<div>
<h3>{
{ title }}h3>
<div>
<p v-for="item in books" :key="item.id">
{
{ item.bookname }}
p>
div>
<nuxt-link to="/">
主页
nuxt-link>
div>
template>
<script>
export default {
name: 'MyComponent',
components: {
},
props: {
},
data () {
return {
title: '剑客',
books: [],
list: [
'html', 'css', 'js'
]
}
},
created () {
this.loadData()
},
methods: {
async loadData () {
const result = await this.$axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks'
})
// eslint-disable-next-line no-console
console.log(result)
this.books = result.data.data
}
}
}
script>
运行代码。并在浏览器中观察数据的请求来源。
created中的代码是会在浏览器发出ajax请求的。
其它测试用的接口也可以使用的。
https://github.com/typicode/jsonplaceholder
这里还是一个典型客户端渲染的效果。
不难发现:以上渲染数据还是发生在客户端,那么如何让渲染的数据发生在服务端?
asyncData是Nuxt中额外增加的 vue 生命周期的钩子函数。在这个钩子函数中,代码可以在服务端执行。
没有 Vue 实例的 this,this 指向 undefined
发请求,以获取数据。数据要求以对象格式返回,并最终会附加到vue组件的data数据项中,可以正常在template中使用。
<template>
<div>
<h3>{
{
title }}</h3>
<div>
<p v-for="item in serList" :key="item">
{
{
item }}
</p>
</div>
<div>
<p v-for="item in books" :key="item.id">
{
{
item.bookname }}
</p>
</div>
<!-- 在页面之间跳转 -->
<nuxt-link to="/">
主页
</nuxt-link>
</div>
</template>
<script>
export default {
name: 'MyComponent',
components: {
},
props: {
},
asyncData () {
// eslint-disable-next-line no-console 忽略单行代码检测
console.log('asyncData 是在服务器端执行的')
return {
serList: ['服务器端数1', '服务器端数2']
}
},
data () {
return {
title: '剑客',
books: [],
list: [
'html', 'css', 'js'
]
}
}
}
</script>
上例是获取同步数据。
目标:让数据请求发生在服务端,asyncData钩子函数中处理请求
在asyncData钩子函数中,去发ajax请求,获取数据。
<template>
<div>
<h3>{
{
title }}</h3>
<div>
<p v-for="item in newBooks" :key="item.id">
{
{
item.bookname }}
</p>
</div>
<!-- 在页面之间跳转 -->
<nuxt-link to="/">
主页
</nuxt-link>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'MyComponent',
async asyncData () {
// 在asyncData中发ajax请求
const result = await axios({
method: 'GET',
url: 'http://www.liulongbin.top:3006/api/getbooks '
})
// eslint-disable-next-line no-console
// 获取到的数据,通过return {} 的格式 把数据附加在当前Vue实例中的data上。
return {
newBooks: result.data.data
}
},
data () {
return {
title: '剑客',
books: [],
list: [
'html', 'css', 'js'
]
}
}
}
</script>
<style scoped lang='less'></style>
在asyncData()中的写的代码会在服务器端执行,也会在客户端执行(路由切换)。
假设在pages/index.vue这个页面中使用asyncData钩子函数,在这个函数中去请求接口,获取数据,并交给vue来渲染。
按如下顺序执行这三个钩子
vue的中的钩子函数正常执行。
asyncData 也会执行:只在路由切换时
重点:
发请求取数据的操作:
那么为什么我们还需要vuecli框架呢?直接使用nuxt框架不好么?
我们学习的vue知识,可以直接在这两个脚手架创建的项目中使用。
区别:
vuecli 打包之后得到的是一个dist 目录,就是index.html,.js,css… 。是可以双击打开的(不包含服务器代码,与服务器没有任何的关系,它就是发ajax,获取数据,并显示)。
nuxt项目打包之后会得到客户端与服务器端的代码,在上线时,需要把服务器代码也上线。
spa-vue-cli: 客户端渲染, 缺点:首屏比较慢(路由懒加载,… 图片…,打包优化), 对SEO不友好。