最新消息:

让我们再聊聊浏览器资源加载优化

hg0088开户 浏览 评论

来源:程序员人生   发布时间:2014-10-11 08:00:01 阅读次数:2273次

  几乎每一个前端程序员都知道应该把script标签放在页面底部。关于这个经典的论述可以追溯到Nicholas的 High Performance Javasript 这本书的第一章Loading and Execution中,他之所以建议这么做是因为:

Put all <script> tags at the bottom of the page, just inside of the closing </body> tag. This ensures that the page can be almost completely rendered before script execution begins.

  简而言之,如果浏览器加载并执行脚本,会引起页面的渲染被暂停,甚至还会阻塞其他资源(比如图片)的加载。为了更快的给用户呈现网页内容,更好的用户体验,应该把脚本放在页面底部,使之最后加载。

  为什么要在标题中使用“再”这个字?因为在工作中逐渐发现,我们经常谈论的一些页面优化技巧,比如上面所说的总是把脚本放在页面的底部,压缩合并样式或者脚本文件等,时至今日已不再是最佳的解决方案,甚至事与愿违,转化为性能的毒药。这篇文章所要聊的,便是展示某些不被人关注的浏览器特性或者技巧,来继续完成资源加载性能优化的任务。

  一. Preloader   什么是Preloader

  首先让我们看一看这样一类资源分布的页面:

  这类页面的特点是,一个外链脚本置于页面头部,三个外链脚本置于页面的底部,并且是故意跟随在一系列img之后,在Chrome中页面加载的网络请求瀑布图如下:

  值得注意的是,虽然脚本放置在图片之后,但加载仍先于图片。为什么会出现这样的情况?为什么故意置后资源能够提前得到加载?

  虽然浏览器引擎的实现不同,但原理都十分的近似。不同浏览器的制造厂商们(vendor)非常清楚浏览器的瓶颈在哪(比如network, javascript evaluate, reflow, repaint)。针对这些问题,浏览器也在不断的进化,所以我们才能看到更快的脚本引擎,调用GPU的渲染等一推陈出新的优化技术和方案。

  同样在资源加载上,早在IE8开始,一种叫做lookahead pre-parser(在Chrome中称为preloader)的机制就已经开始在不同浏览器中兴起。IE8相对于之前IE版本的提升除了将每台host最高并行下载的资源数从2提升至6,并且能够允许并行下载脚本文件之外,最后就是这个lookahead pre-parser机制

  但我还是没有详述这是一个什么样的机制,不着急,首先看看与IE7的对比:

  以上面的页面为例,我们看看IE7下的瀑布图:

  底部的脚本并没有提前被加载,并且因为由于单个域名最高并行下载数2的限制,资源总是两个两个很整齐的错开并行下载。

  但在IE8下,很明显底部脚本又被提前:

  并没有统一的标准规定这套机制应具备何种功能已经如何实现。但你可以大致这么理解:浏览器通常会准备两个页面解析器parser,一个(main parser)用于正常的页面解析,而另一个(preloader)则试图去文档中搜寻更多需要加载的资源,但这里的资源通常仅限于外链的js、stylesheet、image;不包括audio、video等。并且动态插入页面的资源无效。

  但细节方面却值得注意:

  • 比如关于preloader的触发时机,并非与解析页面同时开始,而通常是在加载某个head中的外链脚本阻塞了main parser的情况下才启动;

  • 也不是所有浏览器的preloader会把图片列为预加载的资源,可能它认为图片加载过于耗费带宽而不把它列为预加载资源之列;

  • preloader也并非最优,在某些浏览器中它会阻塞body的解析。因为有的浏览器将页面文档拆分为head和body两部分进行解析,在head没有解析完之前,body不会被解析。一旦在解析head的过程中触发了preloader,这无疑会导致head的解析时间过长。

  •   Preloader在响应式设计中的问题

      preloader的诞生本是出于一番好意,但好心也有可能办坏事。

      filamentgroup有一种著名的响应式设计的图片解决方案Responsive Design Images:

      它的工作原理是,当responsive-images.js加载完成时,它会检测当前显示器的尺寸,并且设置一个cookie来标记当前尺寸。同时你需要在服务器端准备一个.htaccess文件,接下来当你请求图片时,.htaccess中的配置会检测随图片请求异同发送的Cookie是被设置成medium还是large,这样也就保证根据显示器的尺寸来加载对于的图片大小。

      很明显这个方案成功的前提是,js执行先于发出图片请求。但在Chrome下打开,你会发现执行顺序是这样:

      responsive-images.js和图片几乎是同一时间发出的请求。结果是第一次打开页面给出的是默认小图,如果你再次刷新页面,因为Cookie才设置成功,服务器返回的是大图。

      严格意义上来说在某些浏览器中这不一定是preloader引起的问题,但preloader引起的问题类似:插入脚本的顺序和位置或许是开发者有意而为之的,但preloader的这种“聪明”却可能违背开发者的意图,造成偏差。

      如果你觉得上一个例子还不够说明问题的话,最后请考虑使用picture(或者@srcset)元素的情况:

      在preloader搜寻到该元素并且试图去下载该资源时,它应该怎么办?一个正常的paser应该是在解析该元素时根据当时页面的渲染布局去下载,而当时这类工作不一定已经完成,preloader只是提前找到了该元素。退一步来说,即使不考虑页面渲染的情况,假设preloader在这种情形下会触发一种默认加载策略,那应该是"mobile first"还是"desktop first"?默认应该加载高清还是低清照片?

      二. JS Loader

      理想是丰满的,现实是骨感的。出于种种的原因,我们几乎从不直接在页面上插入js脚本,而是使用第三方的加载器,比如seajs或者requirejs。关于使用加载器和模块化开发的优势在这里不再赘述。但我想回到原点,讨论应该如何利用加载器,就从seajs与requirejs的不同聊起。

    hg0088:让我们再聊聊浏览器资源加载优化

    发表我的评论
    取消评论

    表情

    您的回复是我们的动力!

    • 昵称 (必填)
    • 验证码 点击我更换图片

    网友最新评论