# 资源加载顺序

## 渲染事件

![](/assets/ca232a82-b4dc-11e6-88b8-bc4a8704c543.png)

1. **domLoading **表示开始解析第一批收到的 HTML 文档的字节
2. **domInteractive **表示完成全部 HTML 的解析并且 DOM 构建完毕
3. **domContentLoaded **表示 DOM 与 CSSOM 皆已准备就绪
4. **domComplete **表示所有的处理都已完成并且所有的附属资源都已经下载完毕
5. **loadEvent **作为网页加载的最后一步以便触发附加的应用逻辑

## CSS阻塞

CSS会阻塞渲染树的构建，不阻塞DOM构建，但是在CSSOM构建完成之前，页面不会开始渲染（一片空白），与js不一样，js虽然会阻塞后续DOM构建，但是前面已经就绪的内容会进行渲染。CSS虽然不阻塞DOM构建，但是会阻塞后面js的执行，从而间接阻塞完整DOM的构建，举个例子：

在资源服务器端，设定css和js的返回延时，且js先返回，css后返回，但是文档中，css在js之前：

```js
app.use('/css/:cssName',(req,res)=>{
    let fileName = req.params.cssName;
    setTimeout(()=>{
        res.sendFile(fileName,{
            root: path.join(__dirname,'../css')
        });
    },4000);
});

app.use('/js/:jsName',(req,res)=>{
    let fileName = req.params.jsName;
    setTimeout(()=>{
        res.sendFile(fileName,{
            root: path.join(__dirname,'../js')
        });
    },2000);
})
```

然后html：

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/css/animateTop.css">
</head>
<body>
    <p>body content before script</p>
    <script type="text/javascript" src="/js/script1.js"></script>
    <script type="text/javascript" src="/js/script2.js"></script>
    <script type="text/javascript" src="/js/script3.js"></script>
    <p>body after script</p>
</body>
</html>
```

测试发现，后边的js虽然先下载下来，但是只有等css执行完毕后，才得到执行。

然后进一步js又会阻塞后续DOM树构建，就这样导致整个渲染过程话费更多时间。所以我们要尽快加载CSS并构建CSSOM。

### 针对性优化

* 尽早加载css文件并生成CSSOM.
* 不使用@import
* 利用媒体查询，处理特定设备下的CSS，以减少不必要的阻塞.

## JS阻塞

JS默认也是会阻塞DOM和渲染树的构建的。HTML解析器在遇到脚本文件时，默认为停下来去获取脚本（不考虑资源预加载优化），然后执行，期间阻塞DOM构建。

## defer脚本

声明为defer的脚本会延迟到DOM构建完成后\(DOMInteractive事件\)，`DOMContentLoaded`和`window.onload`事件之前执行\(但依然会被浏览器的预加载策略提前下载）。只针对外联脚本有效。多个defer会按照先后顺序串行执行。

```js
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script type="text/javascript" src="/js/script1.js" defer></script>
    <script defer>
        console.log('inserted');
    </script>
</head>
<body>
    <script type="text/javascript" src="/js/script2.js"></script>
    <p>conten after script</p>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            console.log('DOMContentLoaded');
        }, false);

        window.addEventListener('load', function() {
            console.log('window loaded');
        }, false);
    </script>
</body>
</html>
```

我们在控制台会看到先执行`script2.js`其后面的p标签渲染没有受到阻塞。内联脚本按照正常顺序执行，依然阻塞DOM。它们都在`DOMContentLoaded`和`window.onload`事件之前执行。

### async脚本

声明 为异步的脚本会异步地下载和执行，会在window.onload事件之前执行，但是可能会在DOMContenLoaded事件前后执行。

一张图：

![](/assets/20160503104416135)
*图片来源：[http://blog.csdn.net/liuhe688/article/details/51247484](http://blog.csdn.net/liuhe688/article/details/51247484)*

实际上上面的图并不完全正确。现代浏览器对脚本和样式资源还会有预加载策略。这个后面会讲。

### 多个defer和async脚本

当我们不使用defer和async，我们会看到现代浏览器的资源预加载策略会并行地下载多个脚本和样式文件：

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/css/animateTop.css">
</head>
<body>
    <script type="text/javascript" src="/js/script1.js"></script>
    <script type="text/javascript" src="/js/script2.js"></script>
    <script type="text/javascript" src="/js/script3.js"></script>
    <p>conten after script</p>
</body>
</html>
```

资源加载顺序：![](/assets/screenhot11.png)我们看到各个样式和脚本文件都是并行下载的，这得益于浏览器的资源预加载。

而当我们加入多个defer或者async时，情况有所变化，注意查看三个脚本的属性：

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/css/animateTop.css">
</head>
<body>
    <script type="text/javascript" src="/js/script1.js" async></script>
    <script type="text/javascript" src="/js/script2.js" async></script>
    <script type="text/javascript" src="/js/script3.js"></script>
    <p>conten after script</p>
</body>
</html>
```

资源加载顺序：![](/assets/screenhot12.png)我们发现第一个声明为async的script1和没有声明async的script3是和css同步加载的，而第二个声明为async的脚本即script2会排队，等待第一个async脚本加载完毕，defer也如此。

## 资源优先级

当我们网页上有许多图片，脚本和样式资源时，它们的加载顺序是怎样的呢？举个例子进行测试：

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/css/animateTop.css">
</head>
<body>
    <img src="/images/img1.png"/>
    <img src="/images/img2.png"/>
    <img src="/images/img3.png"/>
    <img src="/images/img4.png"/>
    <script type="text/javascript" src="/js/script1.js"></script>
    <script type="text/javascript" src="/js/script2.js"></script>
    <script type="text/javascript" src="/js/script3.js"></script>
    <p>conten after script1</p>
</body>
</html>
```

加载顺序：![](/assets/screenhot13.png)我们可以看到，样式和脚本的优先级是比图片更高的，因为这两个都具有阻塞性，浏览器让它们尽快下载下来。

第一个图片资源会和css一起下载，但浏览器发现后面还有许多图片就会优先去下载脚本文件。

