bigpipe
是facebook
提出的一种优化页面加载速度的解决方案,原理大概是先加载页面的整个框架,不需要数据的一部分,然后把需要数据的部分分成多个pagelet
,后台不断的传递数据到前端来,一步步的render
页面.
下面我们来说说怎么来实现这种方案,后端以nodejs
来实现
因为
nodejs
是异步非阻塞的,实现这种方案非常有优势.假如是php
的话则有难度,因为它是同步阻塞的,一次请求内不允许异步出现,除非自定义扩展.
利用Nodejs来实现bigpipe
首先nodejs
需要express
,async
第三方模块,我们先来初始化package.json
文件,然后安装依赖
- npm init
- npm install express --save
- npm install async --save
然后我们来实现这些东西
- 1.建立一个web服务器
- 2.创建用于前端bigpipe效果的静态页
- 3.定义两个静态文件用于数据源
- 4.定义前端模拟的一个bigpipe请求
首先建立一个web服务器名为
app.js
var express = require('express'),
fs = require('fs'),
async = require('async');
var server = express();
server.use('/bigpipe', express.static(__dirname + '/static'));
server.use('/lib', express.static(__dirname + '/bower_components'));
server.listen(4000, function(){
console.log('server is listen on port 4000......');
})
上面的/bigpipe
目录是用来前端页面内资源加载的路径,/lig
是用来加载第三方前端库,这个下面会说到
最后打开监听4000端口
建立一个前端请求的页面为
bigpipe.html
<!doctype html>
<html>
<head>
<title>bigpipe</title>
<meta charset="utf8">
<link rel="stylesheet" href="/bigpipe/css/base.css">
</head>
<body>
<div class="container">
<h1>this is bigpipe demo</h1>
<div class="content" id="body1">
<!-- 此处数据异步加载 -->
</div>
<div class="content" id="body2">
<!-- 此处数据异步加载 -->
</div>
<footer>
by feenan
</footer>
</div>
<!-- 模板1 -->
<script type="text/template" id="fileinfo1">
<p>
<%= info%>
</p>
</script>
<!-- 模板2 -->
<script type="text/template" id="fileinfo2">
<p>
<%= info%>
</p>
</script>
<script src="/lib/jquery/dist/jquery.min.js"></script>
<script src="/lib/underscore/underscore.js"></script>
<script src="/bigpipe/js/bigpipe.js"></script>
</body>
</html>
<script>
var bigpipe = new Bigpipe();
bigpipe.ready('users', function(data){
// console.log(data);
var template = _.template($('#fileinfo1').html());
$('#body1').html(template({info: data}));
});
bigpipe.ready('info', function(data){
console.log(data);
var template = _.template($('#fileinfo2').html());
$('#body2').html(template({info: data}));
});
</script>
上面的第三方前端库,可以利用bower
来安装
- bower init
- bower install jquery --save
- bower install underscore --save
在根目录下建立static
目录,然后分别建立js
,css
文件夹,这个static
为前端资源文件夹
然后在js
文件夹里建立bigpipe.js
文件,这个主要是用来在前端定义事件,有两方法ready
,set
,ready
主要是前端用来注册事件,set
是用来后端触发事件
var Bigpipe = function(){
this.callbacks = {};
}
Bigpipe.prototype.ready = function(key, callback){
this.callbacks[key] = this.callbacks[key] || [];
this.callbacks[key].push(callback);
}
Bigpipe.prototype.set = function(key, data){
console.log(key, data);
var callbacks = this.callbacks[key] || [];
for (var i = callbacks.length - 1; i >= 0; i--) {
callbacks[i].call(this, data);
};
}
建立一个请求来模拟
bigpipe
,代码写在app.js
里
server.get('/info', function(req, res, next){
var content = fs.readFileSync(__dirname + '/bigpipe.html', 'utf8');
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(content);
// 下面开始加载文件内容
// 开始并行加载多个文件
async.parallel([function(callback){
fs.readFile(__dirname + '/data/users.txt', 'utf8', function(err, data){
data = data.replace(/\n/g, '\\n');
res.write('<script>bigpipe.set("users","'+ data +'");</script>');
callback(err, data);
});
},function(callback){
fs.readFile(__dirname + '/data/info.txt', 'utf8', function(err, data){
data = data.replace(/\n/g, '\\n');
console.log(data);
res.write('<script>bigpipe.set("info","' + data + '");</script>');
callback(err, data);
})
}],function(err, result){
// if(err) res.end();
res.end();
});
});
上面的/info
代表客户端的一个请求,里面先读取bigpipe.html
文件就是上面定义的,然后先输出到浏览器,但是此时这个连接没有关闭,后面接着异步请求两个文件分别来用render
页面内的两个需要数据填充的部分
async.parallel
是用来并行执行方法的,上面的例子里只要有一个方法成功就输出到页面,都成功则结束此次请求调用res.end()
,注意这里输出的脚本内容,bigpipe.set
,这里就会触发前端页面内注册的相关事件,最后把数据render
到页面内.
因为nodejs
天生的异步非阻塞的特点,所以利用bigpipe
方式可以有效的解决页面初始化空白的问题,而且哪怕页面内有多个数据加载的部分也可以有效的解决.
总结
bigpipe
能够有效的解决重数据页面的加载问题,这个比ajax
异步加载的好处就是利用一个http
连接就能解决多数据加载的问题,相比一个连接一个ajax
的方式明显能提高不少服务器端性能.