从单页应用(SPA)到服务器渲染(SSR)

个人博客之前已经将 vue-router 的模式改为了 history,即 url 中不包含 hash,再通过将所有的静态请求转发到 index.html,使它看上去似乎像一个静态多页的网站。

然而,它其实和其他的 SPA (Single Page Application 单页应用)来说没有任何的区别,最终是通过前端的路由去控制页面的显示。单页应用虽然在交互体验上比传统多页更友好,但它也有一个天生的缺陷,就是对搜索引擎不友好,不利于爬虫爬取数据。

正所谓成也萧何,败也萧何。

讲人话就是,搜索引擎搜不到我的博客啊~哭...

那什么对搜索引擎和爬虫友好的哪?答案就是静态页,而非浏览器渲染,这就需要服务器直接渲染,也就 SSR(Server Side Render)。

当然不是这个 SSR

SSR,服务器渲染。简单来说就是,服务器将每个要展示的页面都运行完成后,将整个相应流传送给浏览器,所有的运算在服务器端都已经完成,浏览器只需要解析 HTML 就行。

说起来简单,那到底该如何着手将项目改造成 SSR,和曾经的多页又有什么区别哪?既然自己在 SSR 方面是个小白,自然要先从查资料看文档入手,Vue 2.0 的文档中有一章就是关于 SSR

看了文档之后,它给了我一个新思路,可以在无须大幅修改原先代码的情况下做到 SSR,又不失单页良好的体验。

听上去很酷是不是,具体怎么做继续看下去。

SSR Architecture

一个普通的单页应用通常是通过 webpack 将源代码打包后插入到 html 中,当页面请求时,返回 html 再加载打包后的 js 文件,也就是下图中的 Application Code,Webpack build 和 browser 这三大块。

SSR Architecture

剩下的那几部分就是 SSR 需要额外新加的部分,一个个来看。

Server entry & Client entry

Server entry & client entry 两者的有共同的词尾 entry,对应的是 webpack.config 中的 entry,即打包入口文件,也就是分别代表服务器端所运行代码的入口和浏览器端所运行代码的入口文件。

入口文件自然不用多复杂。

  • server entry: 根据路由状态,返回渲染完成后相应的组件
  • clinet entry: 将应用直接挂载到 DOM 上

OK。它俩的事就做完啦,是不是很简单。

Webpack build

有了不同的 entry,打包的内容也有不同,自然就要两套配置。

配置 webpack 的配置文件的确很麻烦,但有个好消息就是原先的打包文件不需要修改,只需加一个 server 端的配置文件就可以了。server 端的配置文件也相当简单,基本可以沿用客户端的配置,改改 entryoutput 基本就差不多了。

不过,有一点要注意,一定要将 target 属性设置成 node,不然打包完了也没法在 node 环境下跑。还可以将所有依赖都设置成 externals(跑在服务器本地嘛,依赖自然都拿得到),这只是个优化点,不加也没有任何问题。

有了配置文件,也就能生成 Server Bundle 了,只剩下最后一块 Bundle Renderer 了。

Bundle Renderer

到这里才要用上 vue 为支持 ssr 所依赖的库 vue-server-renderer

通过 vue-server-renderer 提供的 API 就能容易地根据 url 生成对应的组件树,然后将它返回给客户端。

这里要注意,因为用的是 webpack 打包后的文件,所以只能用 createBundleRenderer 而不能用 createRenderer 来创建 renderer。

创建 renderer 的时候还可以为它配置 cache,方法在 README 中也写得很清楚了,由于我个人博客的场景不适合添加 cache 就没有添加。

这样从 SPA 到 SSR 的变更就完成了,通过浏览器访问看看是不是已经将页面整个返回了。

Tips

  • 遇到控制台 ⚠️

The client-side rendered virtual DOM tree is not matching server-rendered content.

当然,可能是你的标签不对应,也有可能是 text node 中的空格字符长度不对应,我个人遇到的都是空格不对应造成的问题,很是尴尬(可能是使用 template 语法造成的)...

  • Memory-fs

在开发环境下,由于使用服务器渲染,自然不能使用 webpack-dev-server,而是要用 webpack-dev-middleware。然而,webpack-dev-middleware 所创建的文件都是在内存里的,server 就无法读到 server bundle 文件,这里就要用到 memory-fs 来从内存中读文件。

  • KOA 2

用 koa 2 作为服务器时,在 renderToStringrenderToStream 时,记得外面要加 await,否则,程序就不等组件渲染好,就直接跑下个 middleware 去了。

(奉劝大家不要用 koa 作 SSR 服务器,koa 和 webpack-dev-middleware 天生水土不服,不要问我为什么~😭)

  • document

在 Server 端渲染时,node 环境下是没有 document 对象的。当一个界面的显示依赖于 document 对象(比如,页面滚动监听事件),那么,在 node 端运行时就会报错。这时,有两个解决的办法。

  1. 根据运行时的环境变量,通过添加逻辑来判断是否依赖 document
  2. 使用 jsdom mock document 对象(个人偷懒的做法)

当然,从设计的角度移除对 document 的依赖就最好啦。

  • $root._isMounted:组件中可以用这个参数来判断应用是否为第一次挂载

完成

这样当浏览器请求时,返回的页面是服务器渲染之后的,浏览器解析后,页面仍就是一个单页应用。

最后,看效果的戳这里,看代码的戳这里,原先 SPA 的代码依旧保留在了 SPA 分支

对 Vue SSR 有兴趣的童鞋,一定要看看 vue hackernews 2.0,大神的水准比我可是高多了。

最后的最后,吐槽下 Daocloud,最近老挂我服务器,枉我一直为它说好话。

自己写完,看看感觉好简单,为什么还搞了那么久...

常言道:饭不能一日不吃,博客不能一月不发...差点就破例了(🏃

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 在实现 egg + vue 服务端渲染工程化实现之前,我们先来看看前面两篇关于Webpack构建和Egg的文章: ...
    hubcarl阅读 5,999评论 0 19
  • 作者:威威(沪江前端开发工程师)本文原创,转载请注明作者及出处。 背景 最近, 产品同学一如往常笑嘻嘻的递来需求文...
    iKcamp阅读 3,246评论 0 33
  • 转载 :OpenDiggawesome-github-vue 是由OpenDigg整理并维护的Vue相关开源项目库...
    果汁密码阅读 23,083评论 8 124
  • 来源:github.com Vue.js开源项目速查表:https://www.ctolib.com/cheats...
    zhangtaiwei阅读 11,593评论 1 159
  • 1 git 命令 上面这条命令会显示你工作目录与上次提交时(本地仓库)之间的所有差别,这条命令所显示的内容都会在执...
    torres9gogogo阅读 217评论 0 0