如何高效的加载网络资源
- 页面加载的流程
- 解析渲染的过程
- 页面加载的时间
- 资源加载的时间
- 资源加载的优先级
- css 和 script 的阻塞情况
- 预加载系列
- 图片加载
页面加载流程
- 页面卸载 => DNS解析 => TCP链接 => HTTP请求 => 服务器响应 => 浏览器解析
页面渲染流程
页面加载时间
浏览器开发者工具
Navigation Timing API
- 提供了可用于衡量一个网站性能的数据
- JS的对象模型:
PerformanceTiming
等 - 页面加载所需的总时长:
loadEventEnd - navigationStart
- 请求返回时长:
responseEnd - requestStart
- DNS解析时间:
domainLookupEnd - domainLookupStart
资源加载时间
- 获取和分析应用资源加载的详细网络计时数据,比如XMLHttpRequest、 <SVG>、图片、或者脚本
- JS对象模型为
PerformanceResourceTiming
使用代码统计页面和资源的记载性能
- 获取全部的加载性能数据
performance.getEntries()
<link rel="stylesheet" href="./index.css" />
<body>
<img src="./logo.png" />
<script src="./index.js"></script>
<div>
资源加载时间:
<div id="result"></div>
</div>
<script>
// https://developer.mozilla.org/zh-CN/docs/Web/API/PerformanceEntry
function getPerformanceEntries() {
const p = performance.getEntries();
for (let i = 0; i < p.length; i++) {
console.log(p[i]);
printPerformanceEntry(p[i]);
}
}
function printPerformanceEntry(perfEntry) {
const properties = ["name", "entryType", "startTime", "duration"];
if (perfEntry.entryType === "navigation") {
result.innerHTML += `
<div>页面资源:${perfEntry.name}</div>
<div>加载时间:${perfEntry.responseEnd - perfEntry.startTime}</div><hr>
`;
} else if (perfEntry.entryType == "resource") {
result.innerHTML += `
<div>其他资源:${perfEntry.name}</div>
<div>加载时间:${perfEntry.duration}</div>
<hr>
`;
}
}
getPerformanceEntries();
</script>
输出如下:
资源加载优先级
大概从高到低如下:
html
、css
、font
、同步的XMLHttpRequest
这三种类型的资源优先级最高- 在可视区的图片,
script
标签,异步XMLHttpRequest
和fetch
等 - 图片,音视频
prefetch
预读取的资源
注意事项
css
在head
和在body
里的优先级不一样- 可视区的图片优先级高于
js
,但是js
会优先加载 - 图片,视频虽然优先级较高,但是是属于可推迟加载资源
自定义优先级
link
、image
、iframe
、script
标签均有一个属性importance
,试验性的功能
<img src="./assets/dragon.png?p=low" importance="low" width="30px">
阻塞渲染
CSS不阻塞DOM的解析,阻塞页面渲染
- 当
CSS
没有回来之前,我们的页面没有渲染出任何东西 - 但是请求其实和
HTML
文件是同一时间发出来了,说明其解析了DOM
后来的内容
JS的执行阻塞DOM解析
<!-- js执行会阻塞DOM解析, index.js执行3秒,下面的内容就会延时3秒出现 -->
<script src="./index1.js"></script>
<div>内容</div>
Pre系列
preload
:表示用户十分有可能需要在当前浏览中加载目标资源,所以浏览器必须预先获取和缓存对应资源prefetch
:是为了提示浏览器,用户未来的浏览有可能需要加载目标资源,所以浏览器有可能通过事先获取和缓存对应资源,优化用户体验。主要用于预取将在下一次导航/页面加载中使用的资源prerender
:内容被预先取出,然后在后台被浏览器渲染,就好像内容已经被渲染到一个不可见的单独的标签页preconnect
:预先建立连接(TCP)
<!-- 预先加载链接文档的资源 -->
<link rel="prerender" href="xxx.html" />
<!-- 资源预加载,优先级低 -->
<link rel="prefetch" href="xxx.css" />
<!-- DNS预解析 -->
<link rel="dns-prefetch" href="xxx.js" />
<!-- 预加载,优先级高 -->
<link rel="preload" href="xxx.js" as="script" />
<!-- 预先建立TCP连接 -->
<link rel="preconnect" href="xxx.com" />
dns-prefetch
:是尝试在请求资源之前解析域名,仅对跨域域上的DNS
查找有效,避免指向自己的站点dns-prefetch
与preconnect
(预连接)一起搭配使用效果更好,一个解析DNS
一个预先建立TCP
连接
图片的加载
<style>
#imgContainers {
border: 1px solid #333;
height: 400px;
width: 500px;
overflow: auto;
}
#imgContainers img {
border: 1px solid #666;
width: 400px;
height: 400px;
display: block;
}
</style>
</head>
<body>
<div id="imgContainers">
<img data-src="./images/dragon.png?t=1" />
<img data-src="./images/dragon.png?t=2" />
<img data-src="./images/dragon.png?t=3" />
<img data-src="./images/dragon.png?t=4" />
<img data-src="./images/dragon.png?t=5" />
<img data-src="./images/dragon.png?t=6" />
<img data-src="./images/dragon.png?t=7" />
<img data-src="./images/dragon.png?t=8" />
</div>
<script>
window.onload = function () {
const imagesCol = imgContainers.querySelectorAll("img[data-src]");
const options = {
threshold: 0,
rootMargin: "0px",
root: null,
};
const ioCallBack = function (entries, obs) {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// 可见
entry.target.src = entry.target.dataset.src;
obs.unobserve(entry.target); // 停止观察
}
});
};
const observer = new IntersectionObserver(ioCallBack, options);
imagesCol.forEach(function (item) {
console.log("observer", item.dataset.src);
observer.observe(item);
});
};
</script>
</body>
资源加载器
- 通过程序加载JS、CSS、视频等资源以便重复使用
类似下面这种
// 加载
this.load.image("yun")
// 使用
this.add.image(400, 400, "yun")
- 资源加载库:PreloadJS
资源加载的基本原理
- 发送请求获取资源
- 用
key
标记资源 URL.createObjectURL
生成url
以便复用
资源加载缺陷
- 没有显式的版本问题
- 没有缓存
- 资源之间没有依赖关系
改进资源加载器
- 支持版本:用属性字段标记版本
- 支持缓存:
indexedDB
- 支持依赖关系:一个字段标记前置依赖,比如
react-dialog
依赖[react, react-dom]
资源属性设计
key
:资源的唯一标记url
:资源的地址ver
:资源的版本标记pre
:资源加载的前置项,比如react-dialog
的依赖项["react", "react-dom"]
资源加载器组成
示例代码
工具方法-资源下载
工具方法-版本比较
工具方法-对象克隆
工具方法-生成资源地址
工具方法-验证key
消息通知
本地缓存管理
完整代码请查看git地址:xixixiaoyu/resource-load: 资源加载器 (github.com)