首屏加载优化

概述

页面加载的方式包括以下几种:

  1. 直接同步加载
    一次把服务端的渲染内容加载到浏览器,当页面内容比较少时可以考虑这种方式。
  2. 滚动同步加载
    服务端渲染首屏的内容,其他屏幕的内容放到textarea或者注释中,滚动时再渲染其他屏的内容,此种比较适合。
  3. 异步加载
    服务端渲染主 layout ,加载到客户端,通过 AJAX 获取其他页面内容,然后在客户端渲染。此种和淘宝无线 H5 的方案类似。
  4. 滚动异步加载
    服务端渲染首屏内容,加载到客户端,滚动时再通过 AJAX 获取次屏内容
  5. 分块加载
    服务端支持 chunk 输出,分块将内容传输到客户端,客户端渲染。

对比上述几种方式,1和2并不能加快首屏加载的速度;3和4需要通过ajax获取其余的内容,但是对首屏加载是有益的;5是最优方案,在Node中对应的是Bigpipe

分块传输

在讨论Bigpipe前,需要了解其技术支撑:分块传输编码。分块传输编码对应http中字段是:Transfer-Encoding,它是HTTP1.1版本中引入的新技术,目的是在已经建立的tcp连接上持续传递内容。

Persistent Connection

通过持久连接(persistent connection),TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。由于浏览器和服务器实现的问题,现在还需设置Connection: keep-alive去表明当前为长连接,但协议已经不需要了。
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接,也可以通过Connection: close明确指明去关闭链接。

Content-Length

一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的,这就是Content-length字段的作用,声明本次回应的数据长度。
在1.0版中,Content-Length字段不是必需的,因为浏览器发现服务器关闭了TCP连接,就表明收到的数据包已经全了。

在HTTP1.1协议下,如果在请求时不指定Content-Length会有什么情况呢?如下程序:

  require('net').createServer(function(sock) {
    sock.on('data', function(data) {
        sock.write('HTTP/1.1 200 OK\r\n');
        sock.write('Content-Length: 5\r\n'); // 指定数据包长度 考虑:1. 不添加此行程序  2. 小于实际报文长度 3. 大于实际报文长度
        sock.write('\r\n');
        sock.write('hello world!');
        // sock.destroy();
    });
}).listen(9090, '127.0.0.1');

如手动断开连接,那么不论我们怎么设置Content-Length,请求很快完成,只是浏览器能不能正常获取到内容。
如不手动断开连接,会有以下三种情况:

  1. 不添加Content-Length,客户端会一直等待;
  2. Content-Length设置的长度小于或等于实际报文长度,请求顺利完成,但是内容不全;
  3. Content-Length设置的长度大于实际报文长度,客户端会一直等待。

TTFB(Time To First Byte)是web性能优化一个很重要的指标,它代表客户端从发送请求到收到第一个字节所花费的时间。TTFB越短, 意味着用户可以越早看到页面内容,体验越好。通过上面的分析可知,为了让客户端顺利收到响应内容,需要一个正确的 Content-Length值,而为了计算此值需要服务端缓存所有内容,这就和TTFB背道而驰。在 HTTP 报文中,实体一定要在头部之后,顺序不能颠倒,为此我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界

Transfer-Encoding: chunked

分块传输的基本思想是:服务器产生一块数据,就发送一块,采用流模式(stream)取代缓存模式(buffer)。每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了:

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
25
This is the data in the first chunk
1C
and this is the second one
3
con
0

下面是采用Nodejs模拟Transfer-Encoding: chunked的例子:

require('net').createServer(function(sock) {
    sock.on('data', function(data) {
        sock.write('HTTP/1.1 200 OK\r\n');
        sock.write('Transfer-Encoding: chunked\r\n');
        sock.write('\r\n');

        sock.write('b\r\n');
        sock.write('01234567890\r\n');

        sock.write('5\r\n');
        sock.write('12345\r\n');

        sock.write('0\r\n');
        sock.write('\r\n');
    });
}).listen(9090, '0.0.0.0');

实战

Nodejs的http封装本身是按Transfer-Encoding: chunked进行传输的,如下面的例子:

var http = require('http');
http.createServer(function (request, response){
  response.writeHead(200, {'Content-Type': 'text/html'});
  response.write('hello');
  response.write(' world ');
  response.write('~ ');
  setTimeout(function(){
    response.write(' 大家好 ');
  }, 3000);
  // response.end();
}).listen(9090, "127.0.0.1");

我们虽然把response.end()注释掉,但是客户端还是能得到内容,而且在hello world输出后的3秒能接收到大家好这三字。可以说借助Node比较简单的实现了分块传输。
Express、koa等实现分块传输的思想是类似的,具体实现方式可以参考文章:新版卖家中心 Bigpipe 实践(二)

参考文章

新版卖家中心 Bigpipe 实践(一)
新版卖家中心 Bigpipe 实践(二)
HTTP 协议中的 Transfer-Encoding
HTTP 协议入门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,642评论 18 139
  • 本文转自:直播中的首屏加载优化 | www.samirchen.com 直播中的首屏加载时间指的是进入直播间时从播...
    SamirChen阅读 1,299评论 0 4
  • 用vue-cli打包创建的webapp工程,index.html中不包含dom内容结构,都是在js中,如下图 仅仅...
    fangdown阅读 1,498评论 2 3
  • https://segmentfault.com/a/1190000010042512
    程序员小布的养生之道阅读 796评论 0 47
  • 今天最后一次换药,把账结了,竟然有2000多,汗哪~ 娃跟哥哥玩了一天,俩娃作了个底朝天~今天的重头戏是~过油~从...
    圈_圈_阅读 184评论 0 0