vue ssr 实现方式学习笔记

数字拓荒鹤
• 阅读 4805
为什么要写本文呢,话说现在 vue-ssr 官网上对 vue 服务端渲染的介绍已经很全面了,包括各种服务端渲染框架比如 Nuxt.js 、 集成 Koa 和vue-server-renderer 的 node.js 框架 egg.js,都有自己的官网和团队在维护,文档真是面面俱到功能强大,但是,我个人在刚开始看这些资料的时候,总是忍不住发起灵魂三问:“我是谁?我在哪?我在干什么?”,提前没有相关知识的人开始学这些,肯定是要走一些弯路或者卡在某个点一段时间的,所以我想把我的学习经验做下总结,一方面方便自己以后查阅,一方面也会在文中加一些针对官网上没有细说的点的理解,希望能帮助你减少些学习成本,毕竟这是一个知识共享的时代嘛。本文不涉及到源码解析,主要讲解如何实现 vue 的服务端渲染,比较适合 vue-ssr 小白阅读,下面我们进入正文:

先说下基本概念:

ssr 的全称是 server side render,服务端渲染,vue ssr 的意思就是在服务端进行 vue 的渲染,直接对前端返回带有数据,并且是渲染好的HTML页面;而不是返回一个空的HTML页面,再由vue 通过异步请求来获取数据,再重新补充到页面中。

这么做的最主要原因,就是搜索引擎优化,也就是SEO,这更利于网络爬虫去爬取和收集数据。

为什么这样就有利于网络爬虫爬取呢?

这里简单说一下爬虫的爬取方式,爬虫通过访问 URL 获取一个页面后,会获取当前HTML中已存在的数据,也可以理解为把拿到的 HTML 页面转为了字符串内容,然后解析、存储这些内容,但是如果页面中有些数据是通过异步请求获得的,那么爬虫是不会等待异步请求返回之后才结束对页面数据的解析的,这样就会没有爬取到这部分数据,很不利于其他搜索引擎的收录。

这也就是为什么单页面网站是不具备良好的SEO效果的,因为单页面返回的就是一个基本为空的 HTML 文件,里面就一个带有ID的元素等待挂载而已,页面的内容都是通过 js 后续生成的,比如这样:

<!DOCTYPE html>
<html lang="en">
  <head><title>Hello</title></head>
  <body><div id="app"></div></body>
  <script src="bundle.js"></script>
</html>

但对于很多公司来说,公司的产品是希望能被百度、谷歌等搜索引擎收录之后,进行排名,进一步的被用户搜索到,能更利于品牌的推广、流量变现等操作,要实现这些,就必须保证产品的网页是能够被网络爬虫爬取到的,显然一个完整的带有全部数据的页面更利于爬虫的爬取,当然现在也有很多方法可以去实现针对页面异步数据的爬取,github 上也开源了很多的爬虫代码,但是这显然对于爬虫来说更加的不友好、成本更高。

SSR 当然也是有着其他的好处的,比如首屏页面加载速度更快,用户等待时间更短等,其他更多概念可以查看官网 https://ssr.vuejs.org/zh/ ,这些官网上都有介绍。

代码实现

下面我们结合官网上的代码,做一下代码实操,来加深下理解:

在官网中,提供了一个使用模块 vue-server-renderer 简单实现 vue 服务端渲染的示例:
新建一个文件夹vue-ssr-demo,进入其中执行如下命令:

// 安装模块 
npm install vue vue-server-renderer --save

创建文件 server.js

// vue-ssr-demo/server.js 示例代码

//第一步,创建vue实例
const Vue = require('vue');
const app = new Vue({
    template: "<div>hello world</div>"
});

//第二步,创建一个renderer
const renderer = require('vue-server-renderer').createRenderer();
//第三步,将vue渲染为HTML
renderer.renderToString(app, (err, html)=>{
  if(err){
      throw err;
  }
  console.log(html);
});

保存以上代码后,在 vue-ssr-demo 文件夹下打开命令行工具,执行 node server.js 命令,可得到如下 HTML 内容:

➜ vue-ssr-demo node server.js
<div data-server-rendered="true">hello world</div>

好,上面的例子中我们已经让 vue 在服务端,也就是 node 环境下运行起来了,到这里其实已经实现了 vue 的服务端渲染了。

可是,实际项目中使用哪有这么简单,起码数据还没渲染啊,那接下来我们看看如何渲染数据:

vue-ssr 渲染数据的方式有两种,我们先看下第一种:

// server.js
const data_vue = {
    word: 'Hello World!'
};
//第一步,创建vue实例
const Vue = require('vue');
//vue 实例化过程中插入数据
const app = new Vue({
    data: data_vue,
    template: "<div>{{word}}</div>"
});

//第二步,创建一个renderer
const renderer = require('vue-server-renderer').createRenderer();

//第三步,将vue渲染为HTML
renderer.renderToString(app, (err, html)=>{
    if(err){
        throw err;
    }
    console.log(html);
}); 

第一种方式,在创建 vue 实例时,将需要的数据传入 vue 的模板,使用方法与客户端 vue 一样;运行 server.js 结果如下,数据 data_vue 已经插入到 vue 模板里面了:

➜  vue-ssr-demo node server.js
<div data-server-rendered="true">Hello World!</div> 

第二种,模板插值,这里我们也直接先放代码:

const data_vue = {
  word: 'Hello World!'
};
const data_tpl = {
  people: 'Hello People!'
};
//第一步,创建vue实例
const Vue = require('vue');
const app = new Vue({
  data: data_vue,
  template: "<div>{{word}}</div>"
});

//第二步,创建一个 renderer 实例
const renderer = require('vue-server-renderer').createRenderer({
  template: "<!--vue-ssr-outlet--><div>{{people}}</div>"
});

//第三步,将vue渲染为HTML
renderer.renderToString(app, data_tpl, (err, html)=>{
  if(err){
      throw err;
  }
  console.log(html);
});

这里我们增加了数据 data_tpl,你会发现,在 renderToString 方法中传入了这个参数,那么这个参数作用在哪里呢?这就要看下官网中关于 createRenderer 和 renderToString 方法的介绍了,

createRenderer: 使用(可选的)选项创建一个 Renderer 实例。
const { createRenderer } = require('vue-server-renderer')
const renderer = createRenderer({ / 选项 / })

在选项中,就有一个参数叫 template,看官网怎么说的:

template: 为整个页面的 HTML 提供一个模板。此模板应包含注释 <!--vue-ssr-outlet-->,作为渲染应用程序内容的占位符。

为整个页面的 HTML 提供一个模板。此模板应包含注释 <!--vue-ssr-outlet-->,作为渲染应用程序内容的占位符。

模板还支持使用渲染上下文 (render context) 进行基本插值:

使用双花括号 (double-mustache) 进行 HTML 转义插值 (HTML-escaped interpolation);
使用三花括号 (triple-mustache) 进行 HTML 不转义插值 (non-HTML-escaped interpolation)。

根据介绍,在创建 renderer 实例时,可以通过 template 参数声明一个模板,这个模板用来干嘛呢?就用来挂载 vue 模板渲染完成之后生成的 HTML。这里要注意一下,当创建 renderer 实例时没有声明 template 参数,那么默认渲染完就是 vue 模板生成的 HTML;当创建 renderer 实例时声明了 template 参数,一定要在模板中增加一句注释 “<!--vue-ssr-outlet-->” 作为 vue 模板插入的占位符,否则会报找不到插入模板位置的错误。

再次运行 server.js ,结果如下,vue 模板已成功插入,且 template 模板中的 {{people}} 变量也因在 renderToString 方法中第二位参数的传入,显示了数据:

➜  vue-ssr-demo node server.js
<div data-server-rendered="true">Hello World!</div><div>Hello People!</div>

如果我们把 template 换成一个 HTML 页面的基本架构,来包裹 vue 模板,是不是就能得到一个完整页面了呢?我们来试一下:

const data_vue = {
    word: 'Hello World!'
};
const data_tpl = {
    people: 'Hello People!'
};
//第一步,创建vue实例
const Vue = require('vue');
const app = new Vue({
    data: data_vue,
    template: "<div>{{word}}</div>"
});

//第二步,创建一个renderer
const renderer = require('vue-server-renderer').createRenderer({
    template: `<!DOCTYPE html>
    <html lang="en">
      <head><title>Hello</title></head>
      <body>
        <!--vue-ssr-outlet--><div>{{people}}</div>
      </body>
    </html>`
});

//第三步,将vue渲染为HTML
renderer.renderToString(app, data_tpl, (err, html)=>{
    if(err){
        throw err;
    }
    console.log(html);
});

运行 server.js ,结果如下,我们得到了一个完整的 HTML 页面,且成功插入了数据:

➜  vue-ssr-demo node server.js
<!DOCTYPE html>
<html lang="en">
      <head><title>Hello</title></head>
      <body>
        <div data-server-rendered="true">Hello World!</div><div>Hello People!</div>
      </body>
</html>

好,现在页面生成了,该怎么显示呢?这里我们借助下框架 Koa 实现,先来安装:

npm install koa -S

然后修改 server.js ,如下:

const data_vue = {
    word: 'Hello World!'
};
const data_tpl = {
    people: 'Hello People!'
};

const Koa = require('koa');
//创建 koa 实例
const koa = new Koa();

const Vue = require('vue');

//创建一个renderer
const renderer = require('vue-server-renderer').createRenderer({
    template: `<!DOCTYPE html>
    <html lang="en">
      <head><title>Hello</title></head>
      <body>
        <!--vue-ssr-outlet--><div>{{people}}</div>
      </body>
    </html>`
});

// 对于任何请求,app将调用该异步函数处理请求:
koa.use(async (ctx, next) => {
    // await next();
    
    //创建vue实例
    const app = new Vue({
        data: data_vue,
        template: "<div>{{word}}</div>"
    });

    //将vue渲染为HTML
    const body = await renderer.renderToString(app, data_tpl);
    ctx.body = body;
});

// 在端口3001监听:
koa.listen(3001);
console.log('app started at port 3001...');

运行 server.js :

➜  vue-ssr-demo node server.js
app started at port 3001...

然后打开浏览器,输入网址 http://localhost:3001/ ,即可看到运行后的效果。

这样就实现了一个简单的服务端渲染项目,但是我们在平常开发的时候,肯定不会这么简单的去构建一个项目,必然会用到一些,比如打包、压缩的工具,这篇就写到这里,下一篇我们尝试使用 webpack 来构建一个 vue 的服务端渲染项目。

如有问题,欢迎指正!谢谢!

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MBR笔记
<bochs:100000000000e\WGUI\Simclientsize(0,0)!stretchedsize(640,480)!<bochs:2b0x7c00<bochs:3c00000003740i\BIOS\$Revision:1.166$$Date:2006/08/1117