关于浏览器性能优化的总结

点击上方 前端瓶子君,关注公众号

回复算法,加入前端编程面试算法每日一题群

浏览器性能优化

正确的资源下载/执行优先级,并减少页面加载期间浏览器的空闲时间,是提升Web应用性能的最重要手段之一。在实际Web应用中,此优化方案被证明比减少代码大小更为直接有效,此类型的优化对产品开发节奏的影响比较小,它只需要少量的代码更改和重构。

Javascript,XHR,图片预加载

让浏览器在关键页面预加载动态资源:动态加载的JavaScript、预加载的XHR-GraphQL数据请求

<link rel="preload" href="index.js" as="script" type="text/javascript">

动态加载JavaScript,通常是指通过import('...')为指定客户端由路由加载的脚本。在服务端接收到请求时,可以知道这个特定的服务端入口文件将需要哪些客户端路由的动态脚本,并且在页面初始化渲染的HTML中,为这些脚本添加预加载逻辑。

在某个页面入口文件中,必然会执行1个特定的GraphQL请求,可以预加载这个XHR请求,这个点非常重要,因为在某些场景下GraphQL请求会消耗大量时间,页面必须要等到这些数据加载好才能开始渲染。

<link rel="preload" href="/graphql/query?id=12345" as="fetch" type="application/json"> 
调整预加载优先级的好处

除了更早地开始资源加载,预加载还有额外的好处:提升异步脚本加载的网络优先级,对于重要的[异步脚本]来说,这点非常重要,因为它们的网络优先级默认是low。这意味着它们的优先级和屏幕之外的图片一样(low),而页面的XHR请求和屏幕内的图片网络优先级则比它们要高(high)。这导致页面渲染所需的重要脚本的加载可能被阻塞,或和其他请求共享带宽。

调整预加载优先级的问题

预加载的问题:它提供的额外控制会带来额外的责任,即设置正确的资源优先级。当在低速移动网络区域、慢WIFI网络或丢包率比较高的场景中测试时,<link rel="preload" as="script">的网络请求优先级会比<script />标签的JavaScript脚本高,而<script />标签的脚本才是页面渲染首先需要的,这将增加整个页面的加载时间。

只预加载路由需要的异步JavaScript

通过客户端路由当前页面需要异步加载的。

  1. 预加载所有JavaScript资源。

  2. 控制JavaScript资源加载的顺序。

图片预加载

构建一个优先任务的抽象来处理异步加载的队列,这个预加载任务在初始化时优先级是idle(利用requestIdleCallback函数,window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。),所以它会到浏览器不执行任何其他的重要任务时才开始。提升优先级的方法是通过取消所有待执行的空闲任务,这样预加载任务就能立即执行。

使用early flush(提前刷新)和progressive HTML(渐进式HTML)来推送数据

如何让浏览器还没有任何服务端的HTML返回就发起请求?解决方案是服务器主动向浏览器推送资源,这有点像利用http/2push特性,它具有非常好的浏览器兼容性,并且不需要为实现此特性而增加服务端基础架构的复杂性。

它的主要实现包含两点:

  • HTTP分块传输编码

  • 浏览器渐进式渲染HTML

Chunked transfer encoding(分块传输编码)是HTTP/1.1协议中的一部分,从本质上来看,它允许服务端将HTTP的返回切碎成多个chunk(块),然后以分流的形式传输给浏览器。浏览器不断接收这些块,在最后一个块到达后将它们聚合在一起。

它允许服务器在完成每个chunk时,就将此时的HTML页面的内容流式传输到浏览器,不必等待整个HTML完成。服务器一收到请求,就可以将HTML的头部flush给浏览器(early flush),减少了处理HTML剩余内容的时间。对于浏览器来说,浏览器在收到HTML的头部时,就开始预加载静态资源和动态数据,此时服务器还在忙于剩余HTML内容的生成。

利用[分块传输编码]在传输完成的同时讲其它数据推送到客户端。对于服务端渲染的Web应用,一般采用HTML格式返回;对于SPA,可以用JSON格式数据推送到浏览器。

创建一个JSON缓存来存储服务端返回的数据。
// 服务端将会写下所有它已经在准备的请求路径
// 这样客户端就知道等待服务端返回数据即可,不需要自己发送XHR请求
window.__data = {
    '/my/api/path': {
        // 客户端发起请求后的回调都存在waiting数组中
        waiting: []
    }
};
window.__dataLoaded = function (path, data) {
    const cacheEntry = window.__data[path];
    if (cacheEntry) {
        cacheEntry.data = data;
        for (let i = 0; i < cacheEntry.waiting.length; i++) {
            cacheEntry.waiting[i].resolve(cacheEntry.data);
        }
        cacheEntry.waiting = [];
    }
}; 

在把HTML刷新到浏览器后,服务端就可以自己执行API请求的查询,完成后将JSON数据以[包含script标签的HTML片段]的形式刷新到页面中。当这个HTML片段被浏览器接收并解析后,它会被数据写入JSON缓存对象中。这里有个关键技术点:浏览器会在接收到chunks的时候就立即开始渲染。所以,可以再服务端并行生成一系列API数据,并在每个API数据准备好时,就立即将其刷新到JS块中。

当客户端JS准备好请求某个特定数据的时候,它将先检查JSON缓存对象中有没有数据,而不是发起一个XHR请求。如果JSON缓存对象中已经有数据,它将立即得到返回;如果JSON缓存对象已标记pending,它将把请求的resolve回调注册到对应的waiting数组中,请求完成后,执行对应的resolve回调。

function queryAPI(path) {
  const cacheEntry = window.__data[path];
  if (!cacheEntry) {
    // 没有缓存对象,直接发起一个普通的XHR请求
    return fetch(path);
  } else if (cacheEntry.data) {
    // 服务端已经推送好数据
    return Promise.resolve(cacheEntry.data);
  } else {
    // 服务端正在生成数据或推送中
    // 把请求成功的resolve回调放到cacheEntry.waiting队列中
    // 当接收到数据后,回调会按顺序执行
    const waiting = {};
    cacheEntry.waiting.push(waiting);
    return new Promise((resolve) => {
      waiting.resolve = resolve;
    });
  }
} 

此项优化的效果非常明显:桌面端用户访问页面的渲染完成的时间减少14%,移动端用户(有更高的网络延迟)更是减少了23%。

缓存优先

如何让页面更快地获取到数据?唯一的思路就是不经由网络的请求和推送数据。可以采用缓存优先的渲染机制来实现

关于本文

来源:寒青

https://segmentfault.com/a/1190000040043921

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

 》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值