SPA项目SEO方案比较:SSR、Prerender和Nuxt
一、首先我们来科普一下几个概念:
1)SPA:单页面应用,基于vue框架开发的项目很多都属于单页面应用;
2)SSR :server side rendering, 服务端渲染,额,这个不是订阅那个SSR,我一开始就弄混了;
3)Prerender:预渲染,Prerender.io是基于Node.js的程序,它可以让你的JavaScript网站支持搜索引擎,社交媒体,并且它兼容所有的JavaScript框架和库。它采用PhantomJS渲染JavaScript的网页然后呈现为HTML。此外,我们可以实现的prerender服务层来缓存访问过的页面,这将大大提高性能;
4)SEO:搜索引擎优化,指通过对网站进行站内优化、修复和站外优化,从而提高网站的网站关键词排名以及公司产品的曝光度。
5)Nuxt:是一个基于 Vue.js 的通用应用框架,预设了利用Vue.js开发服务端渲染的应用所需要的各种配置,可以为基于 Vue.js 的应用提供生成对应的静态站点的功能。
二、下面就是详细说明喽:
SPA 进化到如今,已经从「拼能力」逐渐过渡到「拼体验」,大家也把目光转向了 SPA 相对多页后端渲染应用的两个最大缺陷:
- 首屏时间
- SEO(Search Engine Optimization)
在这样的背景下,主流框架都开始推出 SSR (server side rendering, 服务端渲染)方案,这里主要谈一下 科赛网 在迁移到 vue 2.x 的同时,对其 SSR 方案的评估及结论。
vue 的 SSR 思路比较清晰,使用服务端、客户端两个入口分别打包出适合在两端运行的应用,服务端会在 SSR 阶段预先注入页面所需的数据及输出对应的 DOM 字符串,客户端应用的起始状态就是服务端渲染的结果,完成状态混合之后的部分和普通的客户端渲染一致。
看起来没什么问题,实际操作了一下,好处不谈了,说一下潜在的坑:
API 稳定性 & vue 版本要求
SSR 相关 API 还未完全稳定,如果要入坑,建议升级到最新的 vue, vue-router, vuex 的发布版本。
服务器端要求
服务端必须是 Node.js,或者专门跑一个 Node.js 来支持 SSR。
库依赖
Polyfill 之类的对应用逻辑无影响的库可以放在客户端入口文件中,为客户端独有。其他的依赖库则要和应用打包在一起,也会在 SSR 阶段跑在服务端的 Node 环境里。但是服务端的 JS 环境和浏览器的环境不一致,有些库会在初始化时访问 window 对象或者使用 DOM API,造成异常。
这里点名批评一下 iView (A high quality UI Toolkit based on Vue.js),在 SSR 时会直接挂掉,和其定位与愿景并不相符。
注:当时测试的 iView 版本是 v2.0.0-rc.17,最新的 v2.0.0-rc.19 已经完善了对于 SSR 的支持, 见 iview/iview
应用状态管理
SSR 的标准实践是使用 vuex 来管理应用状态,也只有 vuex 的状态可以在服务端渲染后直接传递给客户端。在一个比较复杂的应用中,核心数据使用 vuex 管理没有任何问题,但同时有大量的状态不适于放在 vuex 体系内,在 SSR 场景下要做额外考量。
对开发的额外约束
在 SSR 环境下,某些组件和路由的钩子不会被调用,而在 SSR 阶段调用的钩子(如 created)中使用 DOM API 会导致异常。
在官方 demo 里,每个组件都要实现一个类似的 getAsyncData 方法,来配合服务端进行数据预取。这无可厚非,但是对于习惯使用路由及组件钩子完成数据获取的开发者,会带来额外的约束。
数据请求&用户登录态
应用中 API 请求的逻辑也需要既运行在服务端,也运行在客户端。所以 vue-resource 不再可用,需要 axios。
在 SSR 阶段,如果数据依赖用户的登录状态,需要手动将用户 Cookie 传递到 SSR 的渲染器,才能在数据请求时从 context 中获取用户 Cookie,走普通的 API 身份验证逻辑。
例如
// 这是服务端 router 代码
app.get('/my-ssr-app', (req, res) => {
const context = {
url: req.url,
cookies: req.cookies
}
const renderStream = renderer.renderToStream(context)
// 其他略
})
PS: 之前我测试的时候,如果 router 有 base 的话, 在 SSR 阶段不能正确去掉 base 再解析前端路由,需要手动处理,不知道现在是否修复
服务端性能
SSR 是一个 CPU 密集型的应用,如果有扛高并发的需求,请慎用。
应用部署
对于纯 SPA, 如果在构建时将打包后的 bundle 上传到 CDN,则仅需要部署一个 index.html 到服务器。以至于我之前专门写了一个工具,可以实现 SPA 入口的热部署和版本切换。 但是在 SSR 场景下,应用代码和服务器端有了耦合,所以典型的部署需要重启 Node.js 服务
注: 使用 createBundleRenderer,则可以实现服务端的热部署,详见文档
结论
花了一天时间研究以后,我否决了将科赛新版迁移到 SSR 的想法,目前 vue SSR 方案仅适用于重内容展示,并且规模有限的应用,科赛前端约 2w 行代码,并且新版增强了工具属性,强行 SSR 会带来更多问题。
另外,我本人非常认可 vue 团队在 SSR 上的努力,然而 vue 在浏览器里面写起来太爽,增加很多方便的特性,但是在 SSR 中某些特性会带来实现的困难,这是一枚硬币的两面。
另辟蹊径
那么首屏时间怎么办呢,SEO 呢,我是不是需要去知乎发帖「如何有效地糊弄产品经理,急,在线等」呢?
首先,对于以桌面端为主的前端应用,首屏时间不是瓶颈,只要以正确的姿势实现功能,首屏基本在 1s 左右。另外,CDN 和网站服务器升级到 HTTP/2 对加载速度的提升非常大。
其次,对于 SPA 的 SEO,有一个无痛的方案,叫做 prerender,代表实现就是prerender.io 。
广告插入: 无痛 SEO, 今天 Prerender,明天就上线。
其原理是在你的网站服务器上判断请求来源,如果是来自搜索引擎的爬虫,则交给 prerender去处理。prerender 利用一个 无头(headless,无界面)的浏览器,模拟打开一个 SPA 应用,然后将 JS 渲染出的 DOM 抓下来,喂给搜索引擎,从而实现一种伪 SSR 的效果。
目前 prerender 的实现都是基于 PhantomJS ,但是 Chome 59 也加入了 headless,还有一众和它相关的特性,有心人可以去看一下。
既然说到这了,我最近自己动手撸了一个 spa-renderer,并且支持开箱即用地部署到阿里云函数计算或者 AWS Lambda。
为什么不用和 prerender.io 一样的常驻服务器的方案呢,因为搜索引擎抓取是一个频次较低的场景,完美契合函数计算或者 Lambda 的特性,用完就走,便宜,轻量,解耦。
Nuxt.js【服务端渲染方案】
从头搭建一个服务端渲染的应用是相当复杂的。幸运的是,我们有一个优秀的社区项目 Nuxt.js 让这一切变得非常简单。Nuxt 是一个基于 Vue 生态的更高层的框架,为开发服务端渲染的 Vue 应用提供了极其便利的开发体验。更酷的是,你甚至可以用它来做为静态站生成器。推荐尝试。【PS:额,这个是Vue官方推荐的,详见】
来源:https://zhuanlan.zhihu.com/p/28014454