vuessr
SSR的优缺点
与传统 SPA (单页应用程序 (Single-Page Application)) 相比,服务器端渲染 (SSR) 的优势主要在于:
更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。
请注意,截至目前,Google 和 Bing 可以很好对同步 JavaScript 应用程序进行索引。在这里,同步是关键。如果你的应用程序初始展示 loading 菊花图,然后通过 Ajax 获取内容,抓取工具并不会等待异步完成后再行抓取页面内容。也就是说,如果 SEO 对你的站点至关重要,而你的页面又是异步获取内容,则你可能需要服务器端渲染(SSR)解决此问题。
更快的内容到达时间 (time-to-content),特别是对于缓慢的网络情况或运行缓慢的设备。无需等待所有的 JavaScript 都完成下载并执行,才显示服务器渲染的标记,所以你的用户将会更快速地看到完整渲染的页面。通常可以产生更好的用户体验,并且对于那些「内容到达时间(time-to-content) 与转化率直接相关」的应用程序而言,服务器端渲染 (SSR) 至关重要。
使用服务器端渲染 (SSR) 时还需要有一些权衡之处:
开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数 (lifecycle hook) 中使用;一些外部扩展库 (external library) 可能需要特殊处理,才能在服务器渲染应用程序中运行。
涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序 (SPA) 不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 (high traffic) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
编写同构代码
在纯客户端应用程序 (client-only app) 中,每个用户会在他们各自的浏览器中使用新的应用程序实例。对于服务器端渲染,每个请求应该都是全新的、独立的应用程序实例(为每个请求创建一个新的根 Vue 实例)。Node.js 服务器是一个长期运行的进程,当我们的代码进入该进程时,它将进行一次取值并留存在内存中。这意味着如果创建一个单例对象,它将在每个传入的请求之间共享。如果我们在多个请求之间使用一个共享的实例,很容易导致交叉请求状态污染 (cross-request state pollution)。同样的规则也适用于 router
、store
和 event bus
实例。
因为实际的渲染过程需要确定性,所以我们也将在服务器上“预取”数据 ("pre-fetching" data) - 这意味着在我们开始渲染时,我们的应用程序就已经解析完成其状态。也就是说,将数据进行响应式的过程在服务器上是多余的,所以默认情况下禁用。禁用响应式数据,还可以避免将「数据」转换为「响应式对象」的性能开销。
由于没有动态更新,所有的生命周期钩子函数中,只有 beforeCreate
和 created
会在服务器端渲染 (SSR) 过程中被调用。这就是说任何其他生命周期钩子函数中的代码(例如 beforeMount
或 mounted
),只会在客户端执行。
在服务器端渲染(SSR)期间,我们本质上是在渲染我们应用程序的"快照",所以如果应用程序依赖于一些异步数据,那么在开始渲染过程之前,需要先预取和解析好这些数据。
在挂载 (mount) 到客户端应用程序之前,需要获取到与服务器端应用程序完全相同的数据。否则,客户端应用程序会因为使用与服务器端应用程序不同的状态,然后导致混合失败。为了解决这个问题,获取的数据需要位于视图组件之外,即放置在专门的数据预取存储容器(data store)或"状态容器(state container)"中。首先,在服务器端,我们可以在渲染之前预取数据,并将数据填充到 store
中。此外,我们将在 HTML 中序列化(serialize)和内联预置(inline)状态。这样,在挂载(mount)到客户端应用程序之前,可以直接从 store
获取到内联预置(inline)状态。
在客户端,处理数据预取有两种不同方式:
- 在路由导航之前解析数据
- 匹配要渲染的视图后,再获取数据
SSR的构建
事实上,我们可能需要在服务器上使用 webpack 打包 Vue 应用程序,因为:
- 通常 Vue 应用程序是由
webpack
和vue-loader
构建,并且许多webpack
特定功能不能直接在 Node.js 中运行(例如通过file-loader
导入文件,通过css-loader
导入 CSS)。 - 尽管 Node.js 最新版本能够完全支持 ES2015 特性,我们还是需要转译客户端代码以适应老版浏览器。这也会涉及到构建步骤。
所以基本看法是,对于客户端应用程序和服务器应用程序,我们都要使用 webpack
打包。服务器需要「服务器 bundle」然后用于服务器端渲染(SSR),而「客户端 bundle」会发送给浏览器,用于混合静态标记。
vue-server-renderer
createRenderer()
使用(可选的)选项创建一个 Renderer 实例。选项有:
template
为整个页面的 HTML 提供一个模板。此模板应包含注释<!--vue-ssr-outlet-->
,作为渲染应用程序内容的占位符。
renderer.renderToString()
renderer.renderToString(vm, context?, callback?): ?Promise<string>
将 Vue 实例渲染为字符串。
- 第一个参数
vm
为Vue实例 - 第二个参数为上下文对象
- 第三个参数是典型的 Node.js 风格回调,其中第一个参数是可能抛出的错误,第二个参数是渲染完毕的字符串。在不传递
callback
时,此方法返回一个Promise
对象,在其resolve
后返回最终渲染的 HTML。