node.js实现BigPipe详解


BigPipe 是 Facebook 开发的优化网页加载速度的技术。网上几乎没有用 node.js 实现的文章,实际上,不止于 node.js,BigPipe 用其他语言的实现在网上都很少见。以至于这技术出现很久以后,我还以为就是整个网页的框架先发送完毕后,用另一个或几个 ajax 请求再请求页面内的模块。直到不久前,我才了解到原来 BigPipe 的核心概念就是只用一个 HTTP 请求,只是页面元素不按顺序发送而已。

了解了这个核心概念就好办了,得益于 node.js 的异步特性,很容易就可以用 node.js 实现 BigPipe。本文会一步一步详尽地用例子来说明 BigPipe 技术的起因和一个基于 node.js 的简单实现。

我会用 express 来演示,简单起见,我们选用 jade 作为模版引擎,并且我们不使用引擎的子模版(partial)特性,而是以子模版渲染完成以后的 HTML 作为父模版的数据。

先建一个 nodejs-bigpipe 的文件夹,写一个 package.json 文件如下:

\\/script>') + '")</script>')

以上我们便说明了 BigPipe 的原理和用 node.js 实现 BigPipe 的基本方法。而在实际中应该怎样运用呢?下面提供一个简单的方法,仅供抛砖引玉,代码如下:

\\/script>') +
    '")</script>')
}
function PipeName (res, name) {
  res.pipeCount = res.pipeCount || 0
  res.pipeMap = res.pipeMap || {}
  if (res.pipeMap[name]) return
  res.pipeCount++
  res.pipeMap[name] = this.id = ['pipe', Math.random().toString().substring(2), (new Date()).valueOf()].join('_')
  this.res = res
  this.name = name
}
resProto.pipeName = function (name) {
  return new PipeName(this, name)
}
resProto.pipeLayout = function (view, options) {
  var res = this
  Object.keys(options).forEach(function (key) {
    if (options[key] instanceof PipeName) options[key] = '<span id="' + options[key].id + '"></span>'
  })
  res.render(view, options, function (err, str) {
    if (err) return res.req.next(err)
    res.setHeader('content-type', 'text/html; charset=utf-8')
    res.write(str)
    if (!res.pipeCount) res.end()
  })
}
resProto.pipePartial = function (name, view, options) {
  var res = this
  res.render(view, options, function (err, str) {
    if (err) return res.req.next(err)
    res.pipe('#'+res.pipeMap[name], str, true)
    --res.pipeCount || res.end()
  })
}
app.get('/', function (req, res) {
  res.pipeLayout('layout', {
      s1: res.pipeName('s1name')
    , s2: res.pipeName('s2name')
  })
  getData.d1(function (err, s1data) {
    res.pipePartial('s1name', 's1', s1data)
  })
  getData.d2(function (err, s2data) {
    res.pipePartial('s2name', 's2', s2data)
  })
})

还要在 layout.jade 把两个 section 添加回来:

https://github.com/undozen/bigpipe-on-node ,我把每一步做成一个 commit 了,希望你 clone 到本地实际运行并 hack 一下看看。因为后面几步涉及到加载顺序了,确实要自己打开浏览器才能体验到而无法从截图上看到(其实应该可以用 gif 动画实现,但是我懒得做了)。

关于 BigPipe 的实践还有很大的优化空间,比如说,要 pipe 的内容最好设置一个触发的时间值,如果异步调用的数据很快返回,就不需要用 BigPipe,直接生成网页送出即可,可以等到数据请求超过一定时间才用 BigPipe。使用 BigPipe 相比 ajax 既节省了浏览器到 node.js 服务器的请求数,又节省了 node.js 服务器到数据源的请求数。不过具体的优化和实践方法,等到雪球网用上 BigPipe 以后再分享吧。


« 
» 

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3