BigPipe 技术是FaceBook 提出的新型浏览器加载模式。提高了网站加载速度和用户体验。
对于业务逻辑比较复杂的社区,电子商务类网站。Bigpipe是一个高性价比提升网站速度方案。
BigPipe 介绍
http://baike.baidu.com/view/4601904.htm
bigpipe技术的ppt:http://twork.taobao.net/books/237
bigpipe的java实现:http://codemonkeyism.com/facebook-bigpipe-java/
一篇介绍bigpipe的文章:http://www.54chen.com/architecture/rose-pipe-http-54chen.html
实现方案
当前的实现方式大多基于JAVA 和PHP实现方式,通过ASP.NET MVC 实现BIGPIPE
仿新浪微博
JS 基本框架代码,base.Js
var CMB = function () {
var js = {},
logs = [],
cachedBrowser;
js.inc = function (js, logs) {
return !0
};
// 注册多个命名空间
js.register = function (ns, fun) {
var e = ns.split("."),
f = js,
g = null;
while (g = e.shift()) {
if (e.length) {
f[g] === undefined && (f[g] = {});
f = f[g]
} else {
if (f[g] === undefined) {
try {
f[g] = fun(js)
} catch (h) {
logs.push(h)
}
}
}
}
};
js.regShort = function (b, c) {
if (js[b] !== undefined)
throw "[" + b + "] : short : has been register";
js[b] = c
};
js.IE = /msie/i.test(navigator.userAgent);
js.E = function (a) {
return typeof a == "string" ? document.getElementById(a) : a
};
js.C = function (a) {
var b;
a = a.toUpperCase();
a == "TEXT" ? b = document.createTextNode("") : a == "BUFFER" ? b = document.createDocumentFragment() : b = document.createElement(a);
return b
};
js.log = function (a) {
logs.push("[" + (new Date).getTime() % 1e5 + "]: " + a)
};
js.getErrLogInfoList = function (a) {
return logs.splice(0, a || logs.length)
};
js.existElm = function (id) {
return typeof document.getElementById(id) !== 'undefined';
};
js.browser = function () {
if (!cachedBrowser) {
var ua = navigator.userAgent.toLowerCase();
var match = /(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
!/compatible/.test(ua) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) ||
[];
cachedBrowser = match[1];
}
return cachedBrowser;
}
return js
} ();
$Import = CMB.inc;
CMB.register("comm.LoadCss", function (obj) {
return function (i,url, cb) {
var cl = obj.C("link");
cl.setAttribute("rel", "Stylesheet");
cl.setAttribute("type", "text/css");
cl.setAttribute("charset", "utf-8");
cl.setAttribute("href", url);
if (obj.browser() == "msie")
cl.onreadystatechange = function () {
/loaded|complete/.test(cl.readyState) && cb(i);
}
else if (obj.browser() == "opera")
cl.onload = cb(i);
else
//FF, Safari, Chrome
(function () {
try {
cl.sheet.cssRule;
} catch (e) {
setTimeout(arguments.callee, 20);
return;
};
cb(i);
})();
document.getElementsByTagName("head")[0].appendChild(cl);
}
});
CMB.register("comm.LoadJs", function (obj) {
return function (jsLinks) {
if (jsLinks && jsLinks.length) {
for (var i = 0; i < jsLinks.length; i++) {
var spt = obj.C("script");
spt.setAttribute("type", "text/javascript");
spt.setAttribute("src", jsLinks[i]);
document.getElementsByTagName("head")[0].appendChild(spt);
}
}
};
});
CMB.register("pageletM.view", function (obj) {
return function (vjson) {
var defVjson = { pid: "", html: "", css: [], js: [] };
vjson = $.extend(defVjson, vjson);
if (obj.existElm(vjson.pid)) {
if (vjson.css && vjson.css.length) {
for (var i = 0; i < vjson.css.length; i++) {
CMB.comm.LoadCss(i, vjson.css[i], function (j) {
if (j == vjson.css.length-1) {
$("#" + vjson.pid).html(vjson.html);
CMB.comm.LoadJs(vjson.js);
}
});
}
} else {
$("#" + vjson.pid).html(vjson.html);
CMB.comm.LoadJs(vjson.js);
}
}
}
});
var js = {},
logs = [],
cachedBrowser;
js.inc = function (js, logs) {
return !0
};
// 注册多个命名空间
js.register = function (ns, fun) {
var e = ns.split("."),
f = js,
g = null;
while (g = e.shift()) {
if (e.length) {
f[g] === undefined && (f[g] = {});
f = f[g]
} else {
if (f[g] === undefined) {
try {
f[g] = fun(js)
} catch (h) {
logs.push(h)
}
}
}
}
};
js.regShort = function (b, c) {
if (js[b] !== undefined)
throw "[" + b + "] : short : has been register";
js[b] = c
};
js.IE = /msie/i.test(navigator.userAgent);
js.E = function (a) {
return typeof a == "string" ? document.getElementById(a) : a
};
js.C = function (a) {
var b;
a = a.toUpperCase();
a == "TEXT" ? b = document.createTextNode("") : a == "BUFFER" ? b = document.createDocumentFragment() : b = document.createElement(a);
return b
};
js.log = function (a) {
logs.push("[" + (new Date).getTime() % 1e5 + "]: " + a)
};
js.getErrLogInfoList = function (a) {
return logs.splice(0, a || logs.length)
};
js.existElm = function (id) {
return typeof document.getElementById(id) !== 'undefined';
};
js.browser = function () {
if (!cachedBrowser) {
var ua = navigator.userAgent.toLowerCase();
var match = /(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
!/compatible/.test(ua) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec(ua) ||
[];
cachedBrowser = match[1];
}
return cachedBrowser;
}
return js
} ();
$Import = CMB.inc;
CMB.register("comm.LoadCss", function (obj) {
return function (i,url, cb) {
var cl = obj.C("link");
cl.setAttribute("rel", "Stylesheet");
cl.setAttribute("type", "text/css");
cl.setAttribute("charset", "utf-8");
cl.setAttribute("href", url);
if (obj.browser() == "msie")
cl.onreadystatechange = function () {
/loaded|complete/.test(cl.readyState) && cb(i);
}
else if (obj.browser() == "opera")
cl.onload = cb(i);
else
//FF, Safari, Chrome
(function () {
try {
cl.sheet.cssRule;
} catch (e) {
setTimeout(arguments.callee, 20);
return;
};
cb(i);
})();
document.getElementsByTagName("head")[0].appendChild(cl);
}
});
CMB.register("comm.LoadJs", function (obj) {
return function (jsLinks) {
if (jsLinks && jsLinks.length) {
for (var i = 0; i < jsLinks.length; i++) {
var spt = obj.C("script");
spt.setAttribute("type", "text/javascript");
spt.setAttribute("src", jsLinks[i]);
document.getElementsByTagName("head")[0].appendChild(spt);
}
}
};
});
CMB.register("pageletM.view", function (obj) {
return function (vjson) {
var defVjson = { pid: "", html: "", css: [], js: [] };
vjson = $.extend(defVjson, vjson);
if (obj.existElm(vjson.pid)) {
if (vjson.css && vjson.css.length) {
for (var i = 0; i < vjson.css.length; i++) {
CMB.comm.LoadCss(i, vjson.css[i], function (j) {
if (j == vjson.css.length-1) {
$("#" + vjson.pid).html(vjson.html);
CMB.comm.LoadJs(vjson.js);
}
});
}
} else {
$("#" + vjson.pid).html(vjson.html);
CMB.comm.LoadJs(vjson.js);
}
}
}
});
C#后台主要代码
注册方法 RegisterPagelet
var context = helper.ViewContext.HttpContext;
bool isBigpipe = context.Request.Browser.Cookies &&
context.Request.Browser.Browser.Length>0;
if (!isBigpipe)
{
if (pagelet.Data.css != null)
foreach (string css in pagelet.Data.css)
helper.IncludeCss(css);
pagelet.Execute();
context.Response.Write(string.Format("<div id=\"{0}\">{1}</div>", pagelet.Container, pagelet.Data.html));
context.Response.Flush();
if (pagelet.Data.js != null)
foreach (string js in pagelet.Data.js)
helper.IncludeJs(js);
return;
}
List<Pagelet> pagelets = (List<Pagelet>)context.Items["Pagelets"];
if (pagelets == null)
{
pagelets = new List<Pagelet>();
context.Items["Pagelets"] = pagelets;
}
pagelets.Add(pagelet);
//输出块
context.Response.Write("<div id=\"" + pagelet.Container + "\"></div>");
bool isBigpipe = context.Request.Browser.Cookies &&
context.Request.Browser.Browser.Length>0;
if (!isBigpipe)
{
if (pagelet.Data.css != null)
foreach (string css in pagelet.Data.css)
helper.IncludeCss(css);
pagelet.Execute();
context.Response.Write(string.Format("<div id=\"{0}\">{1}</div>", pagelet.Container, pagelet.Data.html));
context.Response.Flush();
if (pagelet.Data.js != null)
foreach (string js in pagelet.Data.js)
helper.IncludeJs(js);
return;
}
List<Pagelet> pagelets = (List<Pagelet>)context.Items["Pagelets"];
if (pagelets == null)
{
pagelets = new List<Pagelet>();
context.Items["Pagelets"] = pagelets;
}
pagelets.Add(pagelet);
//输出块
context.Response.Write("<div id=\"" + pagelet.Container + "\"></div>");
BIGPIPE输出前端JS代码
static readonly object _locker = new object();
public static void ExecutePagelets(this HtmlHelper helper)
{
var context = helper.ViewContext.HttpContext;
List<Pagelet> pagelets = (List<Pagelet>)context.Items["Pagelets"];
if (pagelets == null) return;
Parallel.For(0, pagelets.Count, (i) =>
{
var pagelet = pagelets[i];
pagelet.Execute();
lock(_locker) {
context.Response.Write(pagelet.Serialize());
context.Response.Flush();
}
});
}
public static void ExecutePagelets(this HtmlHelper helper)
{
var context = helper.ViewContext.HttpContext;
List<Pagelet> pagelets = (List<Pagelet>)context.Items["Pagelets"];
if (pagelets == null) return;
Parallel.For(0, pagelets.Count, (i) =>
{
var pagelet = pagelets[i];
pagelet.Execute();
lock(_locker) {
context.Response.Write(pagelet.Serialize());
context.Response.Flush();
}
});
}
代码下载地址,已调试,优化