Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

从图片预加载谈起浏览器资源加载顺序 #46

Open
jiangjiu opened this issue May 24, 2018 · 0 comments
Open

从图片预加载谈起浏览器资源加载顺序 #46

jiangjiu opened this issue May 24, 2018 · 0 comments

Comments

@jiangjiu
Copy link
Owner

jiangjiu commented May 24, 2018

思考一个业务场景:

一个运营活动页面,某些图片相对较大(可能有背景图),20-100k不等,如何让这些图片尽可能快的加载,来保证页面的体验呢?

回想

很久前看过一篇从Chrome源码看浏览器如何加载资源,写的非常好,强烈建议读完这篇文章再继续阅读。

DL;DR

我知道,其实你没好好思考上面的文章😬,那么问题就来了

对浏览器加载资源有很多不确定性:

  1. css/font的资源的优化级会比img高,资源的优化级是怎么确定的呢?
  2. 资源优先级又是如何影响加载的先后顺序的?
  3. 有几种情况可能会导致资源被阻止加载?
第一个问题

第二个问题
  1. 同域每次最多同时加载6个资源(http/1.1)
  2. css/js 优先级最高,即使出现的晚
  3. css加载完毕,才会加载其他资源,即使此时没有达到6个的限制
第三个问题
  1. csp 手动设置的一些限制
  2. Mixed Content https不许加载http内容
  3. Origin Block主要是svg的href只能是同源的资源

测试

说了这么多,写个小demo测试一下:

<!DOCTYPE html>
<html lang="en">
<head>
    <link href="http://cdn.bootcss.com/animate.css/3.5.2/animate.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.5.2/animate.min.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.5.1/animate.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.5.1/animate.min.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.5.0/animate.css" rel="stylesheet">

    <script src="http://cdn.bootcss.com/animejs/2.2.0/anime.js"></script>
    <script src="http://cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script>
    <script src="http://cdn.bootcss.com/animejs/2.1.0/anime.js"></script>
    <script src="http://cdn.bootcss.com/animejs/2.1.0/anime.min.js"></script>
    <link href="http://cdn.bootcss.com/animate.css/3.5.0/animate.min.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.4.0/animate.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.4.0/animate.min.css" rel="stylesheet">
    <link href="http://cdn.bootcss.com/animate.css/3.3.0/animate.css" rel="stylesheet">

    <script>
    var cssSelector = anime({
        targets: '#cssSelector .el',
        translateX: 250
    });
    </script>
</head>
<body>
<p id="title">z</p>
<script>
document.getElementById('title').innerHTML = 'xxx';
</script>
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_20,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_10,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_24,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_26,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_27,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_28,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_30,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_31,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_32,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_33,f_webp" alt="">
<img src="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_90" alt="">
<link rel="preload" as="image" href="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_90">
<link rel="preload" as="image" href="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_99">
</body>
</html>

这个html页面中,有css、js、image资源,有些资源使用了preload进行加载。

结果:

很有意思的一点:
使用了preloadimage.jpg@q_90这张图片,在html解析后的第一时间被加载了,但是挨着它的另一张preloadq_99图片,是在所有的css、js加载后才开始加载的。

这是为什么呢?

上文其实已经写出了答案,复习一下:

区分 delayable / non-delayable 请求

在优先级在Medium以下的为delayable,即可推迟的,而大于等于Medium的为不可delayable的。从刚刚我们总结的表可以看出:css/js是不可推迟的,而图片、preload的js为可推迟加载。

还有一种是layout-blocking的请求:

这是当还没有渲染body标签,并且优先级在Medium之上的如CSS的请求。

深入

Chrome Resource Priorities and Scheduling

Chrome 45 以前:

chrome 46 后:

兼容性

preload技术比较新,通常只有高版本chrome才有支持,好在link标签不支持preload就回忽略。

preload vs prefetch

prefetch的优先级相当的低,通常是用来抓取下一屏的资源,这对SPA应用有相当的好处,但对本页面的资源顺序很难影响。

dns-prefetch这个link标签,浏览器后台会开多线程去解析dns,可以用来对域名预读取,也能一定程度上提高页面性能。

避免同一资源混用preload prefetch

<link rel="preload" as="image" href="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_99">
<link rel="prefetch" as="image" href="http://imgdoc.bce.baidu.com/bce-documentation/BOS/image.jpg@q_99">

当你对同一资源,同时使用两种预加载方式时,结果可能并不是你想要的那样:

字体资源使用preload

字体资源在浏览器加载顺序表现的有些耐人寻味,来看下面的代码:

  <style type="text/css" media="screen, print">
    @font-face {
        font-family: "Bitstream Vera Serif Bold";
        src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2");
    }

    body {
        font-family: "Bitstream Vera Serif Bold", serif
    }
    </style>
    <link rel="preload" as="font"
          href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2">

head标签中,写入了一段css,使用font-face加载一个字体文件,同时下面一个link标签preload了相同的字体资源。

结果:

第一个问题

同一资源被加载了两次,发起了两个请求,而且前后顺序不一。

第二个问题

从上图可以看出,preload的字体资源竟然率先加载了,写在内联css中的字体资源更靠后加载,从资源加载的优先级属性可以看到。

探究

第一个问题,需要给link标签设置一个crossorigin属性,就可以解决这个加载两次的问题。

第二个问题,这也是为什么很多公司对字体文件使用了preload加载后,可以明显提高用户感知体验的原因。

网页字体和关键渲染路径

字体延迟加载带有一个可能会延迟文本渲染的重要隐藏影响:浏览器必须构建渲染树(它依赖 DOM 和 CSSOM 树),然后才能知道需要使用哪些字体资源来渲染文本。因此,字体请求的处理将远远滞后于其他关键资源请求的处理,并且在获取资源之前,可能会阻止浏览器渲染文本。

网页内容的首次绘制(可在渲染树构建后不久完成)与字体资源请求之间的“竞赛”产生了“空白文本问题”,出现该问题时,浏览器会在渲染网页布局时遗漏所有文本。

参考

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

1 participant