笔者曾在大量的关于页面加载分析的文章中提到过图片加载对页面展示(速度)的影响 —— 这是毋庸置疑的。
事实上,不管是通过实际感知还是各类 API 在遇到图片时提供的 onload
处理我们都能知道这样一件事:图片在网页中总是靠后被加载并渲染的,其速度受图片本身大小的影响,并且直接阻塞了页面的展现!
通过 network ,我们又知道了另一件事:图片的加载是一个 client 向 server 发送请求的过程!既然如此,它一定还受到当前网速的限制!
-----
过去很长时间内,我们针对网页优化提供了众多方法 —— 图片懒加载、响应式、代理、websocket、web 安全防御、缓存等,但在“网络连接”方面,目前为止我们似乎仍然只有两个概念:在线/离线。
但就常理来说,下载速度、位置情况、数据计划等都可能会与此有关联。对用户来说,仅仅有“离线”并不能完全取得他们的理解。针对这种情况,我想到能不能 根据当前的网络状态、用户连接情况调整网页的内容?就像我们针对不同宽度提出的“响应式”概念一样!
我查找了相关文档,并发现了其中令我满意的内容 —— 网络 API navigator.connection
:
- connection.downlink
:基于最近观察到的主动连接的有效宽带估计值(Mb/s)
- connection.rtt
:根据最近观察到的主动连接,估计平均往返时间(ms)
- connecon.effectiveType
:基于往返时间和下行链路属性对网络质量的综合估计,值为“2g”、“3g”、“4g”和“slow-2g”
- connection.saveData
:如果用户在其浏览器设置中请求"减少数据模式",则返回 true(否则返回 false)
- ...
这些都是 只读 属性!在 connection API 中,只有 onchange 是可写可读的。
除此,connection 上还挂了一个事件监听器,用于在网络质量发生变化时触发:
navigator.connection.addEventListener('change',()=>{
//...
})
复制代码
就是上面说的“唯一”可读可写的内置函数属性。
至此,你能否想到这样一个场景:在页面文字和 css 加载完成后图片、表格开始加载前检测当前网络状况,如果当前非“4g”或“wifi”且不满足一定情况则拒绝一些(非必须)图片的加载,以加快页面展现;而在网速变好时,自动进行图片的请求,无感加载,以提升用户体验。
写一个 test 页面测试(精华都在 JS 中):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>test-img-net-load</title>
<style>
.img{
width: 400px;
height: 300px;
border: 1px solid red;
overflow: hidden;
}
.opa_img{
width: 200px;
height: 200px;
position: relative;
}
.opa_img::before{
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: url(img/nan.png) no-repeat;
filter: opacity(.5);
overflow: hidden;
z-index: -1;
}
.box{
width: 200px;
height: 30px;
transition: height .5s ease;
background-color: red;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="img">
123
<div class="opa_img">
<div>12313123</div>
</div>
</div>
<input type="search" />
<div class="demo">
<svg width="0" height="0">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo" />
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
</div>
<div class="imgs">
<img data-src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fn1.itc.cn%2Fimg8%2Fwb%2Frecom%2F2017%2F04%2F12%2F149198330290730007.PNG&refer=http%3A%2F%2Fn1.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617513481&t=615ab4bbcdb9db17ccb10cf384174179" class="img" />
</div>
<main>
<h2 class="mao1">I was interested to see if I could make a scroll indicator with just CSS.</h2>
<p>You can! But maybe you shouldn't. This is an interesting consequence of a bunch of hacks held together with duct tape. It uses z-index hacks, gradient hacks and tricks with calc and viewport units.</p>
<hr>
<p>Cras mattis consectetur purus sit amet fermentum. Donec id elit non mi porta gravida at eget metus. Donec id elit non mi porta gravida at eget metus. Aenean lacinia bibendum nulla sed consectetur.</p>
<img data-src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180801%2Ffc885b2fc7914fa49dad4b23d3382e35.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=14ea29f833546f7975d357b1e02d71c5" />
<ul>
<li>Ullamcorper Aenean Ornare</li>
<li>Ridiculus Lorem Malesuada Consectetur</li>
</ul>
<img data-src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1797702397,4145137323&fm=26&gp=0.jpg" />
<p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo.</p>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Aenean lacinia bibendum nulla sed consectetur. Nulla vitae elit libero, a pharetra augue.</p>
<p>Donec sed odio dui. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Cras mattis consectetur purus sit amet fermentum. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
<ul>
<li>Ullamcorper Aenean Ornare</li>
<li>Ridiculus Lorem Malesuada Consectetur</li>
</ul>
<img data-src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.baike.soso.com%2Fp%2F20131031%2F20131031111950-125877005.jpg&refer=http%3A%2F%2Fpic.baike.soso.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=7654421a32492d66b70f5ae11fd4b4bc" />
<p>Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Nullam id dolor id nibh ultricies vehicula ut id elit. Aenean lacinia bibendum nulla sed consectetur. Nullam quis risus eget urna mollis ornare vel eu leo.</p>
<h2 class="mao3">Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</h2>
<p>Having said that, hacks are not always bad. I love hacks and many of us have made quite a good living selling floats and clearfixes.</p>
<hr>
<img data-src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20181108%2Fc9011f1d7722466ca18b1831d900f75b.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=8958044e1a1d4f8607999688885b0ee5" />
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Nulla vitae elit libero, a pharetra augue.</p>
<p>Donec sed odio dui. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Cras mattis consectetur purus sit amet fermentum. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
<ul>
<li>Ullamcorper Aenean Ornare</li>
<li>Ridiculus Lorem Malesuada Consectetur</li>
</ul>
</main>
<script>
console.log(navigator.connection)
console.log("分割线")
let imgs=[
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fn1.itc.cn%2Fimg8%2Fwb%2Frecom%2F2017%2F04%2F12%2F149198330290730007.PNG&refer=http%3A%2F%2Fn1.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617513481&t=615ab4bbcdb9db17ccb10cf384174179",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180801%2Ffc885b2fc7914fa49dad4b23d3382e35.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=14ea29f833546f7975d357b1e02d71c5",
"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1797702397,4145137323&fm=26&gp=0.jpg",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.baike.soso.com%2Fp%2F20131031%2F20131031111950-125877005.jpg&refer=http%3A%2F%2Fpic.baike.soso.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=7654421a32492d66b70f5ae11fd4b4bc",
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20181108%2Fc9011f1d7722466ca18b1831d900f75b.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617510904&t=8958044e1a1d4f8607999688885b0ee5"
];
document.title="loading";
document.addEventListener("DOMContentLoaded",function(){
document.title="content loaded";
console.log(1);
if(Number(navigator.connection.effectiveType.slice(0,1))<4 || navigator.connection.rtt>50){
navigator.connection.addEventListener('change', loadImg);
return;
}
loadImg();
})
// test:和DONContentLoaded比较
window.onload=function(){
console.log(2)
}
// load-img函数
function loadImg(){
let imgDoms=document.querySelectorAll('img')
let arr=[]
for(let i=0;i< imgDoms.length;i++){
let p=new Promise((resolve,reject)=>{
var img=new Image();
img.src=imgDoms[i].dataset.src;
img.onload=()=>{
resolve(img)
}
img.onerror=(e)=>{
reject(e)
}
})
arr.push(p)
}
Promise.all(arr).then((res)=>{
console.log(res)
for(let i in res){
imgDoms[i].setAttribute('src',res[i].getAttribute('src'))
}
document.title="这里写本来的标题"
}).catch((err)=>{
console.log(err)
console.log('图片加载失败')
})
}
</script>
</body>
</html>
复制代码
这里用到了最主要的就是 DOMContentLoaded
API 和 es6 的 promise
API(用来做无感加载)
就像这样~丝毫不受影响:
它还可以被用来做页面中图片的“延迟加载”
-----
兼容性
截止笔者写这篇文章时,浏览器对此 API 的兼容性如下:
相比几个月前已经算是非常迅速了,希望能普遍接纳吧!
-----
还能干什么
比如在一个新闻网站,或者有大视频文件的网站中,我们完全可以这样做,为不同速度呈现不同的元素:
- 离线:带替代文本的占位符
- 2g/减少数据模式:低分辨率图像
- 3g: 高分辨率网膜图像
- 4g: 高清视频
if(!navigator.onLine){
// 离线
}else{
switch(navigator.connection.effectiveType.slice(0,1)){
case 4:
//...
break;
case 3:
//...
break;
default:
//...
}
}
复制代码
想想看,这是个不错的决定!
评论