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 文件如下:

复制代码 代码如下:


{
    "name": "bigpipe-experiment"
  , "version": "0.1.0"
  , "private": true
  , "dependencies": {
        "express": "3.x.x"
      , "consolidate": "latest"
      , "jade": "latest"
    }
}

运行 npm install 安装这三个库,consolidate 是用来方便调用 jade 的。

先做个最简单的尝试,两个文件:

app.js:

复制代码 代码如下:


var express = require('express')
  , cons = require('consolidate')
  , jade = require('jade')
  , path = require('path')

var app = express()

app.engine('jade', cons.jade)
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')

app.use(function (req, res) {
  res.render('layout', {
      s1: "Hello, I'm the first section."
    , s2: "Hello, I'm the second section."
  })
})

app.listen(3000)

views/layout.jade

复制代码 代码如下:


doctype html

head
  title Hello, World!
  style
    section {
      margin: 20px auto;
      border: 1px dotted gray;
      width: 80%;
      height: 150px;
    }

section#s1!=s1
section#s2!=s2

效果如下:

node.js实现BigPipe详解

接下来我们把两个 section 模版放到两个不同的模版文件里:

views/s1.jade:

复制代码 代码如下:


h1 Partial 1
.content!=content

views/s2.jade:

复制代码 代码如下:


h1 Partial 2
.content!=content

在 layout.jade 的 style 里增加一些样式

复制代码 代码如下:


section h1 {
  font-size: 1.5;
  padding: 10px 20px;
  margin: 0;
  border-bottom: 1px dotted gray;
}
section div {
  margin: 10px;
}

将 app.js 的 app.use() 部分更改为:

复制代码 代码如下:


var temp = {
    s1: jade.compile(fs.readFileSync(path.join(__dirname, 'views', 's1.jade')))
  , s2: jade.compile(fs.readFileSync(path.join(__dirname, 'views', 's2.jade')))
}
app.use(function (req, res) {
  res.render('layout', {
      s1: temp.s1({ content: "Hello, I'm the first section." })
    , s2: temp.s2({ content: "Hello, I'm the second section." })
  })
})

之前我们说“以子模版渲染完成以后的 HTML 作为父模版的数据”,指的就是这样,temp.s1 和 temp.s2 两个方法会生成 s1.jade 和 s2.jade 两个文件的 HTML 代码,然后把这两段代码作为 layout.jade 里面 s1、s2 两个变量的值。

现在页面看起来是这样子:

node.js实现BigPipe详解

一般来说,两个 section 的数据是分别获取的——不管是通过查询数据库还是 RESTful 请求,我们用两个函数来模拟这样的异步操作。

复制代码 代码如下:


var getData = {
    d1: function (fn) {
        setTimeout(fn, 3000, null, { content: "Hello, I'm the first section." })
    }
  , d2: function (fn) {
        setTimeout(fn, 5000, null, { content: "Hello, I'm the second section." })
    }
}

这样一来,app.use() 里的逻辑就会比较复杂了,最简单的处理方式是:

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgsygj.html