10个鲜为人知但很有用的Web API
Lesser Known but Useful Web APIs
文章目录
Fullscreen
对指定元素进行全屏展示
<div className="wrapper">
<div id="fs_id">
<img src="path/to/santa.jpg" alt="santa" />
</div>
<button onclick="manageFullscreen()">
Enter Fullscreen with Santa
</button>
</div>
function manageFullscreen () {
document.getElementById('fs_id').requestFullscreen();
}
检查浏览器是否支持
if (document.fullscreenEnabled) {
// supported
} else {
// not supported
}
监听事件
onfullscreenchange
:全屏变化事件onfullscreenerror
:全屏失败事件
Clipboard
检查是否支持
if (navigator.clipboard && navigator.clipboard.read && navigator.clipboard.write) {
// supported
} else {
// not supported
}
将内容写入剪贴板
async function performCopy(event) {
event.preventDefault();
try {
await navigator.clipboard.writeText(copyText);
console.log(`${copyText} copied to clipboard`);
} catch (err) {
console.error('Failed to copy: ', err);
}
}
// 该方法不需要用户许可,但是最好也放在try...catch里面防止报错。
从剪贴板读取内容并对其进行处理
async function performPaste(event) {
event.preventDefault();
try {
const text = await navigator.clipboard.readText();
document.querySelector('#output').value = text;
console.log('Pasted content: ', text);
} catch (err) {
console.error('Failed to read clipboard contents: ', err);
}
// 用户点击页面后,就会输出剪贴板里面的文本。注意,浏览器这时会跳出一个对话框,询问用户是否同意脚本读取剪贴板。
// 如果用户不同意,脚本就会报错。这时,可以使用try...catch结构,处理报错。
}
由于用户可能把敏感数据(比如密码)放在剪贴板,允许脚本任意读取会产生安全风险,所以这个 API 的安全限制比较多。
首先,Chrome 浏览器规定,只有 HTTPS 协议的页面才能使用这个 API。不过,开发环境(localhost)允许使用非加密协议。
其次,调用时需要明确获得用户的许可。权限的具体实现使用了 Permissions API,跟剪贴板相关的有两个权限:clipboard-write(写权限)和clipboard-read(读权限)。"写权限"自动授予脚本,而"读权限"必须用户明确同意给予。也就是说,写入剪贴板,脚本可以自动完成,但是读取剪贴板时,浏览器会弹出一个对话框,询问用户是否同意读取。
另外,需要注意的是,脚本读取的总是当前页面的剪贴板。这带来的一个问题是,如果把相关的代码粘贴到开发者工具中直接运行,可能会报错,因为这时的当前页面是开发者工具的窗口,而不是网页页面。
Resize Observer
window.onresize
事件通常是浪费的,因为它会监听每个元素的大小变化(只有window对象才有resize事件),而不是具体到某个元素的变化。如果我们只想监听某个元素的变化的话,这种操作就很浪费性能了。
ResizeObserver API就可以帮助我们:监听一个DOM节点的变化,这种变化包括但不仅限于:
- 某个节点的出现和隐藏
- 某个节点的大小变化
const myObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
console.log('大小位置', entry.contentRect)
console.log('监听的DOM', entry.target)
});
});
// 监听DOM元素 一个ResizeObserver对象可监听多个DOM节点
myObserver.observe(document.body);
myObserver.observe(document.querySelector('#app'));
// 取消监听
window.setTimeout(() => {
myObserver.unobserve(document.body); // 需要接收一个参数
}, 2000)
// 取消全部监听
window.setTimeout(() => {
myObserver.disconnect() // 此时就不会再监听document.body,和#app节点了
}, 4000)
- ResizeObserverEntry
- target 被监听的DOM对象
- contentRect
- width:指元素本身的宽度,不包含padding,border值
- height:指元素本身的高度,不包含padding,border值
- top:指padidng-top的值
- left:指padding-left的值
- right:指left + width的值
- bottom: 值top + height的值
- x:大小与top相同,y:大小与left相同
Image Capture
ImageCapture
接口提供了从相机或其他摄影设备捕获图像或抓取帧的方法。它提供了一个接口,用于从通过有效.MediaStreamTrack
var imageCapture;
function onGetUserMediaButtonClick() {
navigator.mediaDevices.getUserMedia({video: true})
.then(mediaStream => {
document.querySelector('video').srcObject = mediaStream;
const track = mediaStream.getVideoTracks()[0];
imageCapture = new ImageCapture(track);
})
.catch(error => console.log(error));
}
function onGrabFrameButtonClick() {
imageCapture.grabFrame()
.then(imageBitmap => {
const canvas = document.querySelector('#grabFrameCanvas');
drawCanvas(canvas, imageBitmap);
})
.catch(error => console.log(error));
}
function onTakePhotoButtonClick() {
imageCapture.takePhoto()
.then(blob => createImageBitmap(blob))
.then(imageBitmap => {
const canvas = document.querySelector('#takePhotoCanvas');
drawCanvas(canvas, imageBitmap);
})
.catch(error => console.log(error));
}
/* Utils */
function drawCanvas(canvas, img) {
canvas.width = getComputedStyle(canvas).width.split('px')[0];
canvas.height = getComputedStyle(canvas).height.split('px')[0];
let ratio = Math.min(canvas.width / img.width, canvas.height / img.height);
let x = (canvas.width - img.width * ratio) / 2;
let y = (canvas.height - img.height * ratio) / 2;
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height, x, y, img.width * ratio, img.height * ratio);
}
document.querySelector('video').addEventListener('play', function() {
document.querySelector('#grabFrameButton').disabled = false;
document.querySelector('#takePhotoButton').disabled = false;
});
Broadcast Channel
Broadcast Channel API
允许浏览器上下文(窗口、选项卡、iframes)和同一来源的工作组之间进行通信。
// 发送
const CHANNEL_NAME = "greenroots_channel";
const bc = new BroadcastChannel(CHANNEL_NAME);
const message = 'I am wonderful!';
const sendMessage = () => {
bc.postMessage(message);
}
// 接收
const CHANNEL_NAME = "greenroots_channel";
const bc = new BroadcastChannel(CHANNEL_NAME);
bc.addEventListener('message', function(event) {
console.log(`Received message, "${event.data}", on the channel, "${CHANNEL_NAME}"`);
const output = document.getElementById('msg');
output.innerText = event.data;
});
performance
Performance
是前端性能监控的API。
performance
包含三个对象,分别为 memory
、navigation
、timing
. 其中 memory
是和内存相关的,navigation
是指来源相关的,也就是说从那个地方跳转过来的。timing
是关键点时间。
const performance = window.performance || window.msPerformance || window.webkitPerformance;
if(performance) {
console.log(performance);
}
performance.memory
含义是显示此刻内存占用的情况
- jsHeapSizeLimit该属性代表的含义是:内存大小的限制。
- totalJSHeapSize表示 总内存的大小。
- usedJSHeapSize表示可使用的内存的大小。
如果 usedJSHeapSize 大于 totalJSHeapSize的话,那么就会出现内存泄露的问题,因此是不允许大于该值的。
performance.navigation
含义是页面的来源信息,该对象有2个属性值,分别是:redirectCount 和 type。
- redirectCount:该值的含义是:如果有重定向的话,页面通过几次重定向跳转而来,默认为0;
- type:该值的含义表示的页面打开的方式。默认为0. 可取值为0、1、2、255.
- 0(TYPE_NAVIGATE):表示正常进入该页面(非刷新、非重定向)。
- 1(TYPE_RELOAD):表示通过 window.location.reload 刷新的页面。如果我现在刷新下页面后,再来看该值就变成1了。
- 2(TYPE_BACK_FORWARD ):表示通过浏览器的前进、后退按钮进入的页面。如果我此时先前进下页面,再后退返回到该页面后,查看打印的值,发现变成2了。
- 255(TYPE_RESERVED):表示非以上的方式进入页面的。
performance.onresourcetimingbufferfull
的含义是在一个回调函数。该回调函数会在浏览器的资源时间性能缓冲区满了的时候会执行的。
performance.timeOrigin
是一系列时间点的基准点,精确到万分之一毫秒。如上截图该值为:1559526951495.139,该值是一个动态的,刷新下,该值是会发生改变的。
performance.timing
是一系列关键时间点,它包含了网络、解析等一系列的时间数据。
按照如上图的顺序,我们来分别看下各个字段的含义如下:
navigationStart
含义为同一个浏览器上一个页面卸载结束时的时间戳。如果没有上一个页面的话,那么该值会和fetchStart的值相同。redirectStart
该值的含义是第一个http重定向开始的时间戳,如果没有重定向,或者重定向到一个不同源的话,那么该值返回为0.redirectEnd
最后一个HTTP重定向完成时的时间戳。如果没有重定向,或者重定向到一个不同的源,该值也返回为0.fetchStart
浏览器准备好使用http请求抓取文档的时间(发生在检查本地缓存之前)。domainLookupStart
DNS域名查询开始的时间,如果使用了本地缓存话,或 持久链接,该值则与fetchStart值相同。domainLookupEnd
DNS域名查询完成的时间,如果使用了本地缓存话,或 持久链接,该值则与fetchStart值相同。connectStart
HTTP 开始建立连接的时间,如果是持久链接的话,该值则和fetchStart值相同,如果在传输层发生了错误且需要重新建立连接的话,那么在这里显示的是新建立的链接开始时间。secureConnectionStart
HTTPS 连接开始的时间,如果不是安全连接,则值为 0connectEnd
HTTP完成建立连接的时间(完成握手)。如果是持久链接的话,该值则和fetchStart值相同,如果在传输层发生了错误且需要重新建立连接的话,那么在这里显示的是新建立的链接完成时间。requestStart
http请求读取真实文档开始的时间,包括从本地读取缓存,链接错误重连时。responseStart
开始接收到响应的时间(获取到第一个字节的那个时候)。包括从本地读取缓存。responseEnd
HTTP响应全部接收完成时的时间(获取到最后一个字节)。包括从本地读取缓存。unloadEventStart
前一个网页(和当前页面同域)unload的时间戳,如果没有前一个网页或前一个网页是不同的域的话,那么该值为0.unloadEventEnd
和 unloadEventStart 相对应,返回是前一个网页unload事件绑定的回调函数执行完毕的时间戳。domLoading
开始解析渲染DOM树的时间。domInteractive
完成解析DOM树的时间(只是DOM树解析完成,但是并没有开始加载网页的资源)。domContentLoadedEventStart
DOM解析完成后,网页内资源加载开始的时间。domContentLoadedEventEnd
DOM解析完成后,网页内资源加载完成的时间。domComplete
DOM树解析完成,且资源也准备就绪的时间。Document.readyState 变为 complete,并将抛出 readystatechange 相关事件。loadEventStart
load事件发送给文档。也即load回调函数开始执行的时间,如果没有绑定load事件,则该值为0.loadEventEnd
load事件的回调函数执行完毕的时间,如果没有绑定load事件,该值为0.
如上就是各个值的含义了,大家简单的看下,了解下就行了,不用过多的折腾。在使用这些值来计算白屏时间、首屏时间、用户可操作的时间节点,页面总下载的时间、DNS查询的时间、TCP链接的时间等之前,我们可以先看下传统方案是如何做的?
Battery Status
提供了有关系统充电级别的信息并提供了通过电池等级或者充电状态的改变提醒用户的事件。 这个可以在设备电量低的时候调整应用的资源使用状态,或者在电池用尽前保存应用中的修改以防数据丢失。
navigator.getBattery().then((battery) => {
console.log(battery);
// handle the charging change event
battery.addEventListener("chargingchange", () => {
console.log("Battery charging? " + (battery.charging ? "Yes" : "No"));
});
// handle charge level change
battery.addEventListener("levelchange", () => {
console.log("Battery level: " + battery.level * 100 + "%");
});
// handle charging time change
battery.addEventListener("chargingtimechange", () => {
console.log( "Battery charging time: " + battery.chargingTime + " seconds");
});
// handle discharging time change
battery.addEventListener("dischargingtimechange", () => {
console.log("Battery discharging time: " + battery.dischargingTime + " seconds");
});
});
Network Information
提供有关Network Information API网络类型(例如,“wifi”、“蜂窝”等)、保存数据模式、带宽等的信息。
console.log(navigator.connection);
// NetworkInformation
// downlink: 10
// effectiveType: "4g"
// onchange: null
// rtt: 50
// saveData: false
-
NetworkInformation.downlink 只读
返回以每秒兆比特为单位的有效带宽估计值,四舍五入到最接近的每秒 25 千比特的倍数。 -
NetworkInformation.downlinkMax 只读
返回底层连接技术的最大下行链路速度,以每秒兆位 (Mbps) 为单位。 -
NetworkInformation.effectiveType 只读
返回连接的有效类型,表示“slow-2g”、“2g”、“3g”或“4g”之一。该值是使用最近观察到的往返时间和下行链路值的组来确定的。 -
NetworkInformation.rtt 只读
返回当前连接的估计有效往返时间,四舍五入到最接近的 25 毫秒的倍数。 -
NetworkInformation.saveData 只读
如果用户在用户代理上设置了减少数据使用选项,则返回true
。 -
NetworkInformation.type 只读
返回设备用于与网络通信的连接类型。它将是以下值之一:- bluetooth
- cellular
- ethernet
- none
- wifi
- wimax
- other
- unknown
-
NetworkInformation.onchange 事件
当连接信息更改并且change在此对象上触发时触发的事件。
Navigator.vibrate
Navigator.vibrate() 方法使设备(有震动硬件)产生有频率的震动。若设备不支持震动,该方法将无效。若某震动方式已经在进行中(当该方法调用时),则前一个震动方式停止,新的取而代之。
该方法若因为提供无效的参数使得无法使设备震动,它将返回false,否则返回true。若振动方案导致长时间的震动,它会被截断:最大震动时长取决于每个浏览器的具体实现。
var successBool = window.navigator.vibrate(pattern);
- pattern
提供一个震动、暂停间隔的模式。每一个值表示交替震动或者暂停的毫秒数。你可以提供一个单个的值(震动一次的毫秒数)或者一个包含整数的数组来交替的震动、暂停、震动。
window.navigator.vibrate(200); // vibrate for 200ms
window.navigator.vibrate([100,30,100,30,100,200,200,30,200,30,200,200,100,30,100,30,100]); // Vibrate 'SOS' in Morse.
BlueTooth
此 Web API 允许您连接到蓝牙设备。
navigator.bluetooth.requestDevice({
acceptAllDevices: true
}).then(device => {
setDeviceName(device.name);
setDeviceId(device.id)
setDeviceConnected(device.connected);
}).catch(err => {
console.log(err);
setError(true);
})
语法
Bluetooth.requestDevice(options).then(function(bluetoothDevice) { ... })
- options
设置设备请求选项的对象. 可用的选项是:- filters[]: 一个BluetoothScanFilters数组。 此过滤器由一个BluetoothServiceUUID数组,一个名称参数和一个namePrefix参数组成。
- optionalServices[]: 一个BluetoothServiceUUID数组。
- acceptAllDevices: boolean 表示请求脚本可以接受所有蓝牙设备。 默认值为false。