Vue 2 服务端渲染初探

写这篇文章, Vue 2 还在 Beta 呢...vue

参考资料

官方文档写得很清楚node

彷佛 Vue 1 有看到过经过 jsdom 作后端渲染的例子, 性能不佳.
Vue 2 开始将 Virtual DOM 做为底层实现, 因而模块分离开始支持 SSR.git

渲染步骤

4 步走战略~缓存

安装 hackernews 的例子, 完整的 app 渲染的例子包括:app

  1. 用 Webpack 的 node 模式把整个应用单独打一个包

  2. Node 环境经过 API 将这个包加载到 vm 环境当中

  3. 应用在 vm 内部启动 HTTP 请求抓取当前路由依赖的数据

  4. 生成网页模板, 将 HTML 和初始数据嵌在中间

若是网页依赖的数据少或者不依赖, 能够简化一点,
好比中间抓取 HTTP 的步骤去掉, 能够简化很多,
也许还能够去掉 vm 那步, 直接经过引用文件来生成 HTML.

渲染 API

两套 API 哦... 好像只用带 bundle 那套...

https://github.com/vuejs/vue/...

  • createRenderer([rendererOptions])

  • renderer.renderToString(vm, cb)

  • renderer.renderToStream(vm)

  • createBundleRenderer(code, [rendererOptions])

  • bundleRenderer.renderToString([context], cb)

  • bundleRenderer.renderToStream([context])

后面三个 API 都带上了 bundle, 此外看上去和前面的同样,
bundle 是经过 Node.js 的 vm 模块运行的, 每次的都从新启动一遍代码,
做者解释这样能清空整个 app 的状态,
我推测这是由于用了 Vuex 以后, 数据会被缓存在内部没法清理,
若是是单纯经过 props 传递数据, 应该是能够用前一套 API.

服务端渲染原理

有了 Virtual DOM 就好办了

VNode 定义 https://github.com/vuejs/vue/...

HTML 渲染的代码, 经过 write 同时支持到了 Stream 输出:
https://github.com/vuejs/vue/...
https://github.com/vuejs/vue/...

若是用 bundle 模式, 注意每次都会运行 vm.runInNewContext 新建环境.
https://github.com/vuejs/vue/...
https://github.com/vuejs/vue/...

最后返回用户的 HTML 实际上是拼接出来的,
注意首屏的动态数据, 也经过 window.__INITIAL_STATE__ 发送到浏览器,
https://github.com/vuejs/vue-...

缓存

速度快是由于缓存呢吧...

文档 https://github.com/vuejs/vue/...

大体就是若是组件能够根据一个 key 来肯定, 就能够进行缓存,
静态的组件固然是有固定的 key, 动态的组件根据 id 等数据生成 key,

serverCacheKey: props => props.item.id + '::' + props.item.last_updated

若是组件能够找到缓存, 就直接返回缓存内容:
https://github.com/vuejs/vue/...

这也就意味着顶层的组件总之就是不能缓存的, 性能开销免不了.
hackernews 的例子本地用 ab 压了一下, Mac Pro 到 130+qps 了,

Concurrency Level:      100
Time taken for tests:   3.013 seconds
Complete requests:      400
Failed requests:        0
Total transferred:      11545200 bytes
HTML transferred:       11506000 bytes
Requests per second:    132.77 [#/sec] (mean)
Time per request:       753.205 [ms] (mean)
Time per request:       7.532 [ms] (mean, across all concurrent requests)
Transfer rate:          3742.21 [Kbytes/sec] received

可是这个 Demo 是用了缓存的, 破坏掉缓存性能落差很大,
我本身作的 Demo, 实际上加上缓存性能还不到这个一半...
看来跟应用的类型是有关的, 特别是节点偏多的应用影响更大.

数据策略

想象一下后端有个浏览器...

对于依赖数据, 目前的方案是在组件定义上提供 preFetch 函数,
服务端渲染时会主动查找挂载的部分, 调用进行数据抓取:
https://github.com/vuejs/vue-...
https://github.com/vuejs/vue-...

官方的例子当中 App 是带了 Vuex 跟 vue-router 的,
因此 preFetch 方案整个集成在这些库当中.
从实验看, 内部嵌套的 preFetch 是不会被调用的, 只能从路由开始,
同时中间要用到 Promise.all 合并请求, 脑补一下.

好吧我以为这是一个至关简单粗暴的获取数据的办法,
但其实也很难解耦, 否则就要从路由直接推算数据才行,
主要以为仍是不够清晰, 限制挺多, 实际操做能犯错的地方很多.

性能影响

反正比不上模板引擎

编译后大体还能看到 Virtual DOM 的影子, 会有一些性能开销,
不过话说回来 Virtual DOM 原本就很慢, 能优化一点已经不容易了...

module.exports={render:function(){with(this) {
  return _h('li', {
    staticClass: "news-item"
  }, [_h('span', {
    staticClass: "score"
  }, [_s(item.score)]), " ", _h('span', {
    staticClass: "title"
  }, [(item.url) ? [_h('a', {
    attrs: {
      "href": item.url,
      "target": "_blank"
    }
  }, [_s(item.title)]), " ", _h('span', {
    staticClass: "host"
  }, ["(" + _s(_f("host")(item.url)) + ")"])] : [_h('router-link', {
    attrs: {
      "to": '/item/' + item.id
    }

另外 vm.runInNewContext 有潜在的性能问题,
http://stackoverflow.com/q/98...
不清楚用在生产环境是怎样, 我我的对此没有多少经验..

小结

愈来愈像 React...

Vue 2 算是把这么多内容整合在一块儿至关不容易,
不过服务端渲染 React 那么久了, 仍是没普及开, 性能是大问题,
相比较而言, Vue 2 增长了 cache 机制, 这能够提升性能,
可是依赖数据时会带来启动 vm 开销, 要是代码量不小在么办?
具体效果仍是要等正式发布后, 等有权威的评测...

此外服务端抓取数据的策略须要挖一挖, 找找更漂亮的策略,我我的但愿能更好地解耦, 梳理出更加清晰的依赖,那样也能够适应更多的场景, 灵活地使用, 而不是限定死了这样用.固然也是由于服务端渲染, 这个原本存在的问题显得更明确了.

相关文章
相关标签/搜索