网络信息 API 实现自适应能力
在当下同时有2G、3G、4G、5G等网络条件的情况下,用户对网页的体验也出现了层出不穷的情况。例如在手机上浏览视频网站,能够根据用户当前使用的网络类型,在必要的时候给出提示,是一种很好的体验。
从1G到5G的演进
有兴趣的同学可以看一下,一部波澜壮阔的移动通信史
简单的说就是:
1G采用的技术是模拟通信系统。只能用于打电话,短信什么的都不可以;信号不稳定,通话质量差;不同国家通信系统不兼容。
2G能发短信、浏览最简单网页、甚至玩游戏(记得以前玩过一款猫扑网的文字游戏)
3G主要提升了传输数据的速度,3G手机能快速处理图像、音乐、视频等媒体(3G版qq,偷菜)
4G具备速度快、通信质量高、费用便宜等特点,几乎满足了所有用户对于无线服务的需求
5G拥抱未来,超高速传输会给用户带来更好的体验,能提供更多的服务,一切皆有可能
简单列举JS在浏览器中判断当前网络连接状态的方法
navigator.onLine
demo:http://html5-demos.appspot.com/static/navigator.onLine.html
通过window.navigator.onLine是否为true来判断当前的网络状态
根据MDN的描述:机器未连接到局域网或路由器时返回false,其他情况下均返回true。
也就是说,机器连接上路由器后,即使这个路由器没联通网络,navigator.onLine仍然返回true。
这里做了一个简单的小实验,电脑连接手机热点,此时手机断开流量,电脑无法打开网页,但是navigator.onLine仍然返回true。
除了主动的触发navigator.onLine进行网络状态检测以外,还提供了相关的事件进行监听:
function updateOnlineStatus(event) {
console.log(event)
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
可以看到伴随着网络的打开和关闭,会触发相应的事件,我们也可以在回调中去进行我们想要的操作。
同样的,路由器的断网无法侦测到。
兼容性方面是非常好的
https://caniuse.com/#search=online
获取网络资源
常用的方法是可以创建一张隐藏图片去发送get请求,通过onerror来判断网络是否异常。
Network Information API
Navigator.connection 是只读的,提供一个NetworkInformation 对象来获取设备的网络连接信息。例如用户设备的当前带宽或连接是否被计量, 这可以用于基于用户的连接来选择高清晰度内容或低清晰度内容。
如何应对网络连接质量的变化呢?
https://googlechrome.github.io/samples/network-information/index.html
我们可以通过 connection.onchange 事件监听器来监听网络变化:
function onConnectionChange() {
const { rtt, downlink, effectiveType, saveData } = navigator.connection;
console.log(`Effective network connection type: ${effectiveType}`);
console.log(`Downlink Speed/bandwidth estimate: ${downlink}Mb/s`);
console.log(`Round-trip time estimate: ${rtt}ms`);
console.log(`Data-saver mode on/requested: ${saveData}`);
}
navigator.connection.addEventListener('change', onConnectionChange)
type 返回设备正在与网络进行通信的连接类型
·在概念上类似WiFi,WiMAX传送速率更快,传送范围距离更大,简单理解为一种“大WiFi”。
downlink 有效带宽估计
该属性表示基于最近观察到的活动连接的有效带宽估计值(以Mb / s为单位)。
effectiveType
effectiveType 可取值有 'slow-2g'、'2g'、'3g' 或者 '4g'。在网速慢的时候,此功能可以让你通过提供较低质量的资源来提高页面的加载速度。
enum EffectiveConnectionType {
"2g",
"3g",
"4g",
"slow-2g"
};
rtt来回通信延迟
在通信(Communication)、电脑网络(Computer network)领域中,意指:在双方通信中,发讯方的信号(Signal)传播(Propagation)到收讯方的时间(意即:传播延迟(Propagation delay)),加上收讯方回传消息到发讯方的时间(如果没有造成双向传播速率差异的因素,此时间与发讯方将信号传播到收讯方的时间一样久)
saveData
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/save-data
正确解决用户对快速轻便的应用程序和页面的高需求,一种相当简单的技术是让浏览器使用Save-Data请求标头提供帮助 。通过标识此标头,网页可以自定义并向受成本和性能限制的用户提供最佳的用户体验。
兼容性
大体看来Chrome对它的兼容性会较好一些,其他的浏览器兼容性参差不齐,不过,它是渐进式增强的一个很好的候选者,并且对其他平台的支持也在不断增加。
https://caniuse.com/#search=NetworkInformation
网络感知组件https://codepen.io/mxbck/pen/JZxyoa?editors=0010
function withConnectionType(WrappedComponent, respondToChange = false) {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
connectionType: undefined
}
// Basic API Support Check.
this.hasNetworkInfoSupport = Boolean(
navigator.connection && navigator.connection.effectiveType
)
this.setConnectionType = this.setConnectionType.bind(this)
}
componentWillMount() {
// Check before the component first renders.
this.setConnectionType()
}
componentDidMount() {
// optional: respond to connectivity changes.
if (respondToChange) {
navigator.connection.addEventListener(
'change',
this.setConnectionType
)
}
}
componentWillUnmount() {
if (respondToChange) {
navigator.connection.removeEventListener(
'change',
this.setConnectionType
)
}
}
getConnectionType() {
const connection = navigator.connection
// check if we're offline first...
if (!navigator.onLine) {
return 'offline'
}
// ...or if reduced data is preferred.
if (connection.saveData) {
return 'saveData'
}
return connection.effectiveType
}
setConnectionType() {
if (this.hasNetworkInfoSupport) {
const connectionType = this.getConnectionType()
this.setState({
connectionType
})
}
}
render() {
// inject the prop into our component.
// default to "undefined" if API is not supported.
return (
)
}
}
}
// Now we can reuse the function to enhance all kinds of components.
const ConnectionAwareMedia = withConnectionType(Media)
结合localForage的增强体验
localForage 是一个 JavaScript 库,通过简单类似 localStorage API 的异步存储来改进你的 Web 应用程序的离线体验。它能存储多种类型的数据,而不仅仅是字符串。
localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。在所有主流浏览器中都可用:Chrome,Firefox,IE 和 Safari(包括 Safari Mobile)。
默认情况下,localForage 按照以下顺序选择数据仓库的后端驱动:
浏览器本地存储的大小测试
http://dev-test.nemikor.com/web-storage/support-test/
getItem(key, successCallback)
localforage.getItem('somekey').then(function(value) {
// 当离线仓库中的值被载入时,此处代码运行
console.log(value);
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
// 回调版本:
localforage.getItem('somekey', function(err, value) {
// 当离线仓库中的值被载入时,此处代码运行
console.log(value);
});
setItem(key, value, successCallback)
将数据保存到离线仓库。你可以存储如下类型的 JavaScript 对象:
Array、ArrayBuffer、Blob、Float32Array、Float64Array、Int8Array、Int16Array、Int32Array、Number、Object、Uint8Array、Uint8ClampedArray、Uint16Array、Uint32Array、String
localforage.setItem('somekey', 'some value').then(function (value) {
// 当值被存储后,可执行其他操作
console.log(value);
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
// 不同于 localStorage,你可以存储非字符串类型
localforage.setItem('my array', [1, 2, 'three']).then(function(value) {
// 如下输出 `1`
console.log(value[0]);
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
// 你甚至可以存储 AJAX 响应返回的二进制数据
req = new XMLHttpRequest();
req.open('GET', '/photo.jpg', true);
req.responseType = 'arraybuffer';
req.addEventListener('readystatechange', function() {
if (req.readyState === 4) { // readyState 完成
localforage.setItem('photo', req.response).then(function(image) {
// 如下为一个合法的 标签的 blob URI
var blob = new Blob([image]);
var imageURI = window.URL.createObjectURL(blob);
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
}
});
removeItem(key, successCallback)
localforage.removeItem('somekey').then(function() {
// 当值被移除后,此处代码运行
console.log('Key is cleared!');
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
clear(successCallback)
localforage.clear().then(function() {
// 当数据库被全部删除后,此处代码运行
console.log('Database is now empty.');
}).catch(function(err) {
// 当出错时,此处代码运行
console.log(err);
});
IndexedDB 浏览器存储限制和清理标准
储存限制
浏览器的最大存储空间是动态的——它取决于硬盘大小。
全局限制——可用磁盘空间的50%。 如果您的硬盘驱动器是500GB,那么浏览器的总存储容量为250GB。如果超过此范围,则会发起称为源回收的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。
组限制——这被定义为全局限制的20%,但它至少有10 MB,最大为2GB。 每个源都是一组(源组)的一部分。 每个eTLD+1域都有一个组。 例如:
mozilla.org——组1,源1
www.mozilla.org——组1,源2
joe.blogs.mozilla.org——组1,源3
firefox.com ——组2,源4
在这个组中,mozilla.org、www.mozilla.org和joe.blogs.mozilla.org可以聚合使用最多20%的全局限制。 firefox.com单独最多使用20%。
达到限制后有两种不同的反应:
LRU策略
https://developers.google.com/web/updates/2011/11/Quota-Management-API-Fast-Facts
当可用磁盘空间已满时,配额管理器将根据LRU策略开始清除数据——最近最少使用的源将首先被删除,然后是下一个,直到浏览器不再超过限制。
使用临时存储跟踪每个源的“上次访问时间”。 一旦达到临时存储的全局限制(之后会有更多限制),系统将尝试查找所有当前未使用的源(即没有打开选项卡/应用程序的那些来保持打开的数据存储)。 然后根据“上次访问时间”对它们进行排序。 然后删除最近最少使用的源,直到有足够的空间来满足触发此源回收的请求。
总结
通过网络感知提供一个自适应的能力,结合离线存储方案以及渐进式体验等方式,在当下网络质量参差不齐的情况下,可以更好的增强用户体验。