在开发 Web 应用程序时,有时候我们需要获取当前的网络状态,然后根据不同的网络状态显示不同的提示消息或显示不同页面内容。对于原生应用、混合应用或提供 JS-SDK 的第三方平台来说,我们可以通过相关的 Network API 来获取当前的网络连接状态。但对于 Web 应用来说,虽然也有相关的 Network Information API,但兼容性并不是太好,具体的兼容情况,可以参考 Can I Use - netinfo。
Network Information API
此 API 是由 WICG 编辑的草案,目前可在 Chrome 61 的版本中使用。开发者可以通过 navigator.connection
对象来访问与当前网络连接相关的属性:
- connection.type:返回当前 User Agent 的物理网络类型,可能的值为 “cellular”,”ethernet”,”wfi” 或 “none” 等;
- connection.downlink:返回基于最近观察到的活动连接的有效带宽(以 Mb/s 为单位);
- connection.rtt:返回基于最近观察到的活动连接估计平均往返时间(以毫秒为单位);
- connection.saveData:如果用户在其浏览器启用 “reduced data mode” 模式,则返回 true;
- connection.effectiveType:返回有效的网络连接类型,可能的值为 slow-2g,2g,3g 或 4g。该值的是基于 rtt 和 downlink 的值测算出来的。
下面是网络连接类型的评估标准:
CT | Minimum RTT (ms) | Maximum downlink (Kbps) | Explanation |
---|---|---|---|
slow-2g | 2000 | 50 | The network is suited for small transfers only such as text-only pages. |
2g | 1400 | 70 | The network is suited for transfers of small images. |
3g | 270 | 700 | The network is suited for transfers of large assets such as high resolution images, audio, and SD video. |
4g | 0 | ∞ | The network is suited for HD video, real-time video, etc. |
讲到这里突然想起之前看到的一篇文章 JS 检测网络带宽,有兴趣的小伙伴可以了解一下。
开发网络连接组件
通过结合 Network Information API 与 Angular,我们可以创建一个组件,实现根据不同网络连接速度渲染不同的内容。比如,当我们检测到弱网络的时候,我们可以渲染一个占位符或一个低分辨率的图片或视频,从而提高页面的加载速度。
首先,让我们把连接变化事件封装为一个 Observable 对象:
代码语言:javascript复制const connection$ = new Observable((observer) => {
const { effectiveType } = navigator.connection;
observer.next(effectiveType);
const onConnectionChange = () => {
const { effectiveType } = navigator.connection;
observer.next(effectiveType);
}
navigator.connection.addEventListener('change', onConnectionChange)
return () => {
navigator.connection.removeEventListener('change', onConnectionChange);
observer.complete();
}
});
在页面初始化和连接网络状态发生变化的时候,可观察的 connection$ 对象将会自动通知我们当前的网络连接状态。
接下来,我们来创建 ConnectionComponent 组件和相关的 Connection 指令:
connection.component.ts
代码语言:javascript复制@Component({
selector: 'connection',
template: `
<ng-template [ngTemplateOutlet]="fast.tpl" *ngIf="isFast"></ng-template>
<ng-template [ngTemplateOutlet]="slow.tpl" *ngIf="!isFast"></ng-template>
`,
})
export class ConnectionComponent {
isFast = true;
@ContentChild(FastDirective) fast: FastDirective;
@ContentChild(SlowDirective) slow: SlowDirective;
private subscription: Subscription;
ngOnInit() {
const connection = navigator.connection;
if (!connection) {
// if the browser doesn't support it, we render the fast template
return;
}
this.subscription = connection$
.subscribe((effectiveType: string) => {
if (/slow-2g|2g|3g/.test(effectiveType)) {
this.isFast = false;
} else {
this.isFast = true;
}
})
}
ngOnDestroy() {
this.subscription && this.subscription.unsubscribe();
}
}
fast.directive.ts
代码语言:javascript复制@Directive({
selector: '[fast]'
})
export class FastDirective {
constructor(public tpl: TemplateRef<any>) { }
}
slow.directive.ts
代码语言:javascript复制@Directive({
selector: '[slow]'
})
export class SlowDirective {
constructor(public tpl: TemplateRef<any>) { }
}
在上面的示例中,ConnectionComponent 会根据 effectiveType
属性的值,然后显示 slow 或 fast 指令实例所关联的模板。现在我们来看一下如何使用:
<connection>
<ng-container *fast>
Fast connection - Render a video
</ng-container>
<ng-container *slow>
Slow connection - Render a placeholder
</ng-container>
</connection>
正如前面提到的,基于 Network Information API ,我们也可以实现一个简单的指令,根据不同的网络状态显示不同分辨率的图片。
代码语言:javascript复制<img connection
slowSrc="https://via.placeholder.com/350x150?text=slow"
fastSrc="https://via.placeholder.com/350x150?text=fast">
对应的 connection 指令的具体实现如下:
代码语言:javascript复制import { Directive, Attribute, ElementRef } from '@angular/core';
@Directive({
selector: '[connection]'
})
export class ConnectionDirective {
constructor(@Attribute('slowSrc') private slowSrc,
@Attribute('fastSrc') private fastSrc,
private host: ElementRef<HTMLImageElement>
) {
}
ngOnInit() {
const { effectiveType } = navigator.connection;
let src;
if (/slow-2g|2g|3g/.test(effectiveType)) {
src = this.slowSrc;
} else {
src = this.fastSrc;
}
this.host.nativeElement.setAttribute('src', src)
}
}
查看线上完整的示例:Stackblitz
总结
本文是在过完 “10·24 — 爱码士” 节后,参考 connection-aware-components-in-angular 这篇文章整理的。其中主要介绍了 Network Information API 涉及的相关属性及每个属性的作用。对于使用 Ionic 或 Cordova 项目来说,可以使用 cordova-plugin-network-information 这个库来获取网络信息,有需要的小伙伴可以了解一下。
参考资源
- connection-aware-components-in-angular
- connection-aware-components