in JavaScript

Bigpipe的启示

Bigpipe 是一种异步渲染方案,能有效提升页面加载速度。它也是Facebook前端渲染的主力,据说为网站提速近一倍。

Bigpipe是怎么做到的?

它的思路:将页面分割成多个部分(pagelet),先向用户输出没有数据的布局(框架),然后将每个部分逐步输出到前端,再最终渲染填充框架,完成整个网页的渲染。这个过程需要前端JavaScript渲染,后端异步输出相互配合。

了解这些,容易联想到的是Node的异步输出能力。

在并发的情况下,数据库IO往往会成为一个瓶颈,如果使用传统的模板渲染(EJS/Jade),恐怕还是个同步的思路:请求DB取数据 -> 串行等待数据到位 -> 渲染。完全没有发挥异步IO的长处。

事实上,Node的http.ServerResponse提供了异步输出的能力: 如果只是response.write数据,没有指示response.end,那么这个响应就没有结束,浏览器会保持这个请求。这意味着,在响应end之前,我们都有机会持续输出响应到客户端。注意,是持续交付,可以理解为一个长连接,并且不阻塞UI线程。

知道这些,就可以模拟Bigpipe了:

场景

有一个页面,需要连到Redis中去取队列数据,取到一条显示一条,全部取完时,页面完成输出。

实验

首先封装一个操作Redis 队列的模块(队列使用 list 的  lpush/rpop 实现):

取数据方法是 pop(callback, done),由于使用的node_redis模块,在pop时会有一个Context的切换,闭包引用this 到 that,在完成时 delete掉这个引用以防止泄漏 。其中的 callback 为取每一个出栈元素的回调,它会调用response.write;而 done 就是指示队列已空,取元素完成,它会调用response.end,并关闭 Redis 连接。

页面后端调用就相对简单了,这里只是做演示,所以把数据生成代码也写了进去:

就是一普通的 express 路由模块,为了省事,直接读取一个ejs 模板文件做渲染用,这个模块文件长这样:

运行效果:

asynchronous render

当然,实验中齐刷刷出现四个div的效果,没什么说服力,应用到真实数据规模的场景中,才能体现它的价值。

打赏作者
您的支持将激励我继续创作!

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

Write a Comment

Comment