2017 年 7 月 26 日

网站建设中 SSR 服务端渲染方案解析

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 的特性,用完就走,便宜,轻量,解耦。

最新文章

  1. 找网站公司设计网站要注意哪些问题?
  2. 几百和上万的网站建设相差在什么地方
  3. 低价网站建设的负面影响有哪些
  4. 网站建设如何做才能留住用户?这几点要注意!
  5. 定制型网站有什么特点 为什么受欢迎
  6. 北京网页设计要注意哪些问题
  7. 网站优化如何做才能提升转化率 这几点十分重要
  8. 网站首页设计的要求都有哪些
  9. 怎么才能把网站设计得更为完美?
  10. 北京网站建设过程当中容易出现的错误做法 一定要避免