bigpipe定义
bigpipe是由facebook提出的一种动态网页加载技术,简单来说就是将服务器端的响应分成块分多次传输,这么做的主要原因是可以提升首屏渲染时间,可以将网页的框架和主要内容先传输过来进行显示。
bigpipe案例
案例内容是,先将页面分位两块核心页面和动态渲染两部分,案例的话就是简单的innerHTML,html模板如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bigPipe</title>
</head>
<body>
<div id="app">
核心页面
</div>
<section>
<div id="part1">loading</div>
<div id="part2">loading</div>
</section>
<script>
function addHtml(id, content){
document.querySelector(`#${id}`).innerHTML=content;
}
</script>
</body>
</html>
接着使用koa和@koa/router新建一个简单服务器,根路由下首先读取模板index.html文件,然后将读取的文件通过ctx.res.write传输到客户端,新建两个异步的js标签,通过js动态修改模板中的内容,传输完成记得ctx.res.end()。刷新页面看到动态内容随着传输已经有了内容,查看响应头发现多了两个属性Transfer-Encoding: chunked 和Keep-Alive: timeout=5,前者可以证明内容是通过分片进行传输的,因为写了延迟两秒的函数发现传输时间为4.01s,Waterfal为4s。需要注意的是需要手动修改tx.status =200和ctx.type="html"。
服务端代码如下:
const Koa = require('koa');
var Router = require('@koa/router');
const fs = require('fs');
const app = new Koa();
const router = new Router();
const task1 = () => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(`<script> addHtml("part1", "第一次传输的内容")</script>`)
}, 2000)
})
}
const task2 = () => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(`<script> addHtml("part2", "第二次传输的内容")</script>`)
}, 2000)
})
}
router.get('/', async (ctx,next) => {
const file = fs.readFileSync("index.html", "utf-8");
ctx.status =200;
ctx.type="html";
ctx.res.write(file);
const part1 = await task1();
ctx.res.write(part1)
const part2 = await task2();
ctx.res.write(part2)
ctx.res.end();
})
app.use(router.routes()).use(router.allowedMethods());
app.listen(8085,() => {
console.l
小优化
如果首页文件很大,文件读取需要一定时间,现在的实现是读取了首页文件以后才会开始传输,这里可以进行优化,使用流的方式传输文件,读一点传输一点以提高效率。修改部分代码如下:
const filename = resolve(__dirname, 'index.html');
const stream = fs.createReadStream(filename);
ctx.status =200;
ctx.type="html";
stream.on('data',(chunk)=>{
ctx.res.write(chunk);
})
const part1 = await task1();
ctx.res.write(part1)
const part2 = await task2();
ctx.res.write(part2)
ctx.res.end();
