几天前,我重新审视了令人敬畏的2021年JS状态调查。JS状态是一项在线调查,它从世界各地的开发者那里收集数据,以了解JavaScript社区的最新和即将出现的趋势。在它收集的数据中,有一节专门介绍了JavaScript提供的原生功能,按其使用情况和认知度列出。你可以想象,在使用最多的功能中,有一些流行的功能,如Optional chaining、Nullish coalescing、Websockets,等等。
然而,我对最常用的或已知的API不感兴趣。相反,我在寻找最不为人所知的那些。我想知道哪些API我们谈论的还不够多,在这些API中,我发现有四个相当不同的API非常有用:
在这篇文章中,我们将看到它们是什么,我们应该在哪里使用它们,以及如何使用它们。
注意:这些API在这个演示中都是可用的。
页面可见性API
这是一个鲜为人知的网络API,在JS现状调查中的认知度排名倒数第四。它可以让你知道用户何时离开了页面。准确地说,只要页面可见性状态发生变化,无论是用户最小化、最大化窗口还是切换标签,该API都会触发一个事件。
在过去,你不得不使用一些噱头来了解用户是否切换了标签页或最小化了窗口。最流行的是使用 blur和 focus浏览器事件。使用这些事件会产生类似以下的结果:
window.addEventListener("focus", function () {
// User is back on the page
// Do Something
});
window.addEventListener("blur", function () {
// User left the page
// Do Something
});
前面的代码可以工作,但不像预期的那样。由于blur 事件是在页面失去焦点时触发的,所以当用户点击搜索栏、警报对话框、控制台或窗口边框时都可以触发。所以,blur 和focus 只能告诉我们页面是否处于活动状态,而不能告诉我们页面的内容是否隐藏或可见。
使用案例
一般来说,我们希望使用页面可见性API,在用户没有看到页面时停止不必要的进程,或者在另一方面,执行后台操作。一些具体的案例可以是:
- 当用户离开页面时,暂停视频、图像传送带或动画。
- 如果页面显示来自API的实时数据,在用户离开时暂时停止这一行为。
- 发送用户分析信息。
如何使用它?
页面可见性API带来了两个属性和一个事件来访问页面可见性状态:
document.hidden
它是全局可用的,并且是只读的。尽量避免使用它,因为它现在已经被废弃了,但是当被访问时,如果页面是隐藏的,它会返回true,如果是可见的,会返回false。document.visibilityState
它是document.hidden的更新版本,但是当被访问时,它会根据页面的可见性状态返回四个可能的值: -visible
页面是可见的,或者准确地说,它没有被最小化,也没有在另一个标签中。-hidden
页面不可见;它被最小化或在另一个标签中。-prerender
这是一个可见页面在预渲染时的初始状态。一个页面的可见性状态可以从prerender开始,然后改变到另一个状态,但不能从另一个状态改变到prerender。 -unloaded
页面正在从内存中卸载。visibilitychange
这是一个由document对象提供的事件,当页面visibilityState变化时被触发。
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
// page is visible
} else {
// page is hidden
}
});
为了了解如何使用页面可见性API,让我们用它来暂停一个视频,并在用户离开页面时停止从API获取资源。为了开始,我将使用vite.js,这是一个快速启动新项目的神奇工具。
npm create vite@latest unknown-web-apis
当被要求选择一个框架时,选择vanilla来创建一个vanilla javascript项目。而一旦完成,就到新的文件夹中,安装必要的npm 包,并启动开发者服务器。
cd unknown-web-apis
npm install
npm run dev
转到localhost:3000/,你会看到你的Vite项目已经启动并运行了

首先,我们将直接进入/main.js 文件,删除所有的模板。其次,我们将打开/index.html ,在#app div标签内,我们将添加一个视频元素,其中包含你想要的任何视频文件。我使用了一个跳舞的Yoshi的视频。)
<div id="app">
<video controls id="video">
<source src="./yoshi.mp4" />
</video>
</div>

回到/main.js ,我们将在document 对象上添加一个事件监听器,监听visibilitychange 事件。然后我们可以访问document.visibilityState 属性来查看页面是可见还是隐藏。
document.addEventListener("visibilitychange", () => {
console.log(document.visibilityState);
});
你可以进入页面的控制台,看到当你最小化窗口或切换到另一个标签时,页面的可见性状态发生了变化。现在,在事件监听器里面,我们可以检查document.visibilityState 属性,在hidden 时暂停视频,在visible 时播放视频。(当然,我们先用video 元素选择 document.querySelector().)
const video = document.querySelector("#video");
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
video.play();
} else {
video.pause();
}
});
现在只要用户离开页面,视频就会停止。页面可见性API的另一个用途是在用户没有看到页面时停止获取不必要的资源。为了看到这一点,我们将写一个函数来不断地从quotable.ioAPI中获取一个随机的报价,并在页面被隐藏时暂停这一行为。首先,我们将创建一个新的div标签,将报价存储在/index.html 。
<div id="app">
<video controls id="video">
<source src="./yoshi.mp4" />
</video>
<div id="quote"></div>
</div>
回到/main.js 中,我们将使用 获取API来调用quotable.io 端点api.quotable.io/random,然后将其插入到quote div。
const quote = document.querySelector("#quote");
const getQuote = async () => {
try {
const response = await fetch("https://api.quotable.io/random");
const {content, author, dateAdded} = await response.json();
const parsedQuote = <q>${content}</q> <br> <p>- ${author}</p><br> <p>Added on ${dateAdded}</p>;
quote.innerHTML = parsedQuote;
} catch (error) {
console.error(error);
}
};
getQuote();
让我们简短地解释一下这里发生了什么。我们首先从DOM中选择quote 元素。然后我们声明getQuote 函数,这是一个async 函数,允许我们使用await关键字来等待我们从API中获取数据。取来的数据是JSON格式的,所以我们再使用一次await 关键字来等待,直到数据被解析成一个JavaScript对象。quotable.io API给了我们--除其他外--content 、author 和dateAdded 属性,我们将把这些属性注入并显示在quote div中。这样做是有效的,但报价只被获取一次,所以我们可以用 setInterval()来调用这个函数,每隔10秒调用一次。
const quote = document.querySelector("#quote");
const getQuote = async () => {
try {
const response = await fetch("https://api.quotable.io/random");
const {content, author, dateAdded} = await response.json();
const parsedQuote = <q>${content}</q> <br> <p>- ${author}</p><br> <p>Added on ${dateAdded}</p>;
quote.innerHTML = parsedQuote;
} catch (error) {
console.error(error);
}
};
getQuote();
setInterval(getQuote, 10000);
如果用户最小化窗口或切换标签,页面仍然会获取报价,从而产生不必要的网络负载。为了解决这个问题,我们可以在获取报价之前检查页面是否可见。
const getQuote = async () => {
if (document.visibilityState === "visible") {
try {
const response = await fetch("https://api.quotable.io/random");
const {content, author, dateAdded} = await response.json();
const parsedQuote = <q>${content}</q> <br>
<p>- ${author}</p><br>
<p>Added on ${dateAdded}</p>;
quote.innerHTML = parsedQuote;
} catch (error) {
console.error(error);
}
}
};
getQuote();
setInterval(getQuote, 10000);
现在,只有当页面对用户可见时,我们才会获取报价。
支持
网络共享API
它是什么?
网络共享API也是最不为人所知的API之一,但却非常有用。它可以让你访问操作系统的本地共享机制,这对移动用户特别有用。通过这个API,你可以分享文本、链接和文件,而不需要创建你自己的分享机制或使用第三方的机制。
使用案例
它们是相当不言自明的。你可以用它来分享你的页面内容到社交媒体,或将其复制到用户的剪贴板。
如何使用它?
Web Share API给了我们两个接口来访问用户的分享系统:
navigator.canShare()
接受你想分享的数据作为参数,并根据它是否可以分享,返回一个布尔参数。navigator.share()
返回一个承诺,如果共享成功,将解决这个问题。它调用本地共享机制,并接受你想共享的数据作为参数。请注意,只有在用户按下链接或按钮时才能调用它,也就是说,它需要 瞬时激活.共享数据是一个对象,可以有以下属性。
- `url`: URL to be shared,
- `text`: text to be shared,
- `title`: title to be shared,
- `files`: array of `File` objects representing files to be shared.
为了看看如何使用这个API,我们将回收我们之前的例子,做一个使用网络分享API分享我们的报价的选项。要开始,我们首先要在/index.html 中做一个分享按钮。
<div id="app">
<video controls id="video">
<source src="./yoshi.mp4" />
</video>
<div id="quote"></div>
<button type="button" id="share-button">Share Quote</button>
</div>
我们直接到/main.js ,从DOM中选择分享按钮。然后,我们创建一个async 函数来分享我们想要的数据。
const shareButton = document.querySelector("#share-button");
const shareQuote = async (shareData) => {
try {
await navigator.share(shareData);
} catch (error) {
console.error(error);
}
};
现在,我们可以给shareButton 元素添加一个点击事件监听器来回调shareQuote 函数。我们的shareData.text 的值将是 quote.textContent属性,而shareData.url 将是页面的URL,即 location.href属性。
const shareButton = document.querySelector("#share-button");
const shareQuote = async (shareData) => {
try {
await navigator.share(shareData);
} catch (error) {
console.error(error);
}
};
shareButton.addEventListener("click", () => {
let shareData = {
title: "A Beautiful Quote",
text: quote.textContent,
url: location.href,
};
shareQuote(shareData);
});
现在你可以通过你的本地操作系统与任何人分享你的报价。然而,需要注意的是,网络共享API只有在上下文安全的情况下才会起作用,也就是说,如果页面是通过https:// 或wss:// URL来提供的。
它是什么?
我想谈的另一个API是广播频道API。它允许浏览上下文相互发送和接收基本数据。浏览上下文是像标签、窗口、iframe或任何可以显示页面的元素。由于安全原因,浏览上下文之间的通信是不允许的,除非它们是同一来源并使用广播通道API。对于两个浏览上下文来说,它们必须在其URL中共享相同的协议(例如:http/https )、域(例如:example.com )和端口(例如::8080 )。
使用案例
广播通道API通常被用来保持一个页面的状态在不同的标签和窗口中同步,以提高用户体验或安全原因。它也可以用来知道一个服务在另一个标签页或窗口中何时完成。一些例子是:
- 在所有标签页上记录一个用户的进入或退出。
- 检测一个资产何时被上传,并在所有页面上显示它。
- 指示一个服务人员做一些后台工作。
如何使用它?
广播频道API涉及一个BroadcastChannel 对象,可以用来向其他上下文发送消息。它的构造函数只有一个参数:一个字符串,它将作为一个标识符,用于从其他上下文连接到通道。
const broadcast = new BroadcastChannel("new_channel");
一旦我们在两个上下文中创建了一个具有相同标识符的BroadcastChannel 对象,新的BroadcastChannel 对象将有两个可用的方法来开始通信:
BroadcastChannel.postMessage()在所有连接的上下文中发送一个消息。它接受任何类型的对象作为其唯一的参数,这样你就可以发送各种各样的数据。
broadcast.postMessage("Example message");
BroadcastChannel.close()关闭通道,向浏览器表明它不会再收到任何消息,这样它就可以把这些消息收集到垃圾箱中。
为了接收消息,BroadcastChannel 有一个message 事件,我们可以使用addEventListener 或其onmessage 属性来监听。message 事件有一个data 属性,包含发送的数据和其他属性,用于识别发送消息的上下文,如origin,lastEventId,source, 和ports:
broadcast.onmessage = ({data, origin}) => {
console.log(`${origin} says ${data}`);
};
让我们通过之前的例子来看看如何使用广播频道API。我们的目标是制作另一个具有相同起源的浏览上下文,并在两个上下文中显示相同的报价。要做到这一点,我们将创建一个名为new-origin 的新文件夹,里面有一个新的/index.html 和/main.js 文件。
/new-origin/index.html 将是一个新的HTML模板,里面有一个#quote div:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="../favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="quote"></div>
<script type="module" src="./main.js"></script>
</body>
</html>
在/new-origin/main.js 文件中,我们将创建一个新的广播频道,并从DOM中选择#quote 元素:
const broadcast = new BroadcastChannel("quote_channel");
const quote = document.querySelector("#quote");
而在我们之前的/main.js 文件中,我们将创建一个新的BroadcastChannel 对象,并将其连接到"quote_channel" 。我们还将修改getQuote 函数,以将报价作为消息发送到其他上下文:
const broadcast = new BroadcastChannel("quote_channel");
//...
const getQuote = async () => {
try {
const response = await fetch("https://api.quotable.io/random");
const {content, author, dateAdded} = await response.json();
const parsedQuote = <q>${content}</q> <br> <p>- ${author}</p><br> <p>Added on ${dateAdded}</p>;
quote.innerHTML = parsedQuote;
broadcast.postMessage(parsedQuote);
} catch (error) {
console.error(error);
}
};
回到/new-origin/main.js 文件中,我们将监听message 事件并在每次发送新的报价时改变quote.innerHTML 。
const broadcast = new BroadcastChannel("quote_channel");
const quote = document.querySelector("#quote");
broadcast.onmessage = ({data}) => {
quote.innerHTML = data;
};
现在你可以看到http://localhost:3000/new-origin/中的报价是如何改变为http://localhost:3000 中的报价。你也可以注意到当http://localhost:3000标签被隐藏时,报价是如何变化的,因为它只在其页面可见性状态可见时获取报价。
它是什么?
当开发一个网页或应用程序时,需要将其内容翻译成其他语言以达到更广泛的受众是极为常见的。然而,仅仅将你的网页文本翻译成你所需要的任何语言,并不足以使你的内容对讲该语言的人可用,因为像日期、数字、单位等东西在不同国家是不同的,可能会给你的用户带来混乱。
假设你想在你的网页上显示日期 "2022年11月8日",如 "11/8/22"。根据读者的国家,这个数据可以有三种不同的解读方式:
- "2022年11月8日 "或美国人的MM/DD/YY。
- 来自欧洲和拉美的人读 "2022年8月11日 "或DD/MM/YY。
- 来自日本、中国和加拿大的人称之为 "2011年8月22日 "或YY/MM/DD。
这就是国际化API(或I18n API)来解决不同语言和地区的格式问题的地方。I18n API是一个了不起的工具,有多种用途,但我们不会深入研究,以免使这篇文章过于复杂。
如何使用它?
I18n API使用locale identifiers来工作。地区标识符是一个字符串,表达了用户的语言、国家、地区、方言和其他偏好。准确地说,locale标识符是一个由连字符分隔的子标签组成的字符串。子标签代表用户的偏好,如语言、国家、地区或脚本,并以下列方式格式化:
- "zh":中文(语言)。
- "zh-Hant"。中文(语言)用繁体字(脚本)书写。
- "zh-Hant-TW"。中文(语言)用繁体字(文字)书写,在台湾(地区)使用。
还有更多的子标签来解决更多用户的偏好(如果你想了解更多,你可以查看RFC对语言标签的定义),但简而言之,I18n API使用这些地区标识符来知道如何格式化所有对语言敏感的数据。
更准确地说,I18n API提供了一个Intl 对象,它带来了一堆专门的构造函数来处理对语言敏感的数据。在我看来,一些对国际化最有用的Intl 构造函数是:
Intl.DateTimeFormat()
用于格式化日期和时间。Intl.DisplayNames()
用于格式化语言、地区和脚本显示名称。Intl.Locale()
用于构造和操作locale标识符标签。Intl.NumberFormat()
用于格式化数字。Intl.RelativeTimeFormat()
用于格式化相对时间描述。
在我们的例子中,我们将重点关注Intl.DateTimeFormat() 构造函数,以根据用户的区域设置来格式化报价的dateAdded 属性。Intl.DateTimeFormat() 构造函数需要两个参数:定义日期格式化惯例的locale 字符串和用于自定义如何格式化日期的options 对象。
Intl.DateTimeFormat() 创建的对象有一个format() 方法,该方法接受两个参数:我们想要格式化的Date 对象和自定义如何显示格式化日期的options 对象:
const logDate = (locale) => {
const newDate = new Date("2022-10-24"); // YY/MM/DD
const dateTime = new Intl.DateTimeFormat(locale, {timeZone: "UTC"});
const formatedDate = dateTime.format(newDate);
console.log(formatedDate);
};
logDate("en-US"); // 10/24/2022
logDate("de-DE"); // 24.10.2022
logDate("zh-TW"); // 2022/10/24
注意: 在Intl.DateTimeFormat 构造函数的options 参数上,我们将timeZone 属性设置为"UTC" ,这样日期就不会被格式化为用户的本地时间。在我的例子中,日期被解析为 "10/23/2022",没有timeZone 选项。
正如你所看到的,dateTime.format() ,根据当地的日期格式惯例改变日期。我们可以使用navigator.language 全局属性在报价单的日期上实现这一行为,该全局属性持有用户的首选地区设置。要做到这一点,我们将创建一个新的函数,它接收一个日期字符串(YYYY-MM-DD格式),并根据用户的地区设置返回格式化的日期:
const formatDate = (dateString) => {
const date = new Date(dateString);
const locale = navigator.language;
const dateTimeFormat = new Intl.DateTimeFormat(locale, {timeZone: "UTC"});
return dateTimeFormat.format(date);
};
我们可以在getQuote() 函数中加入这个函数,以解析dateAdded 日期:
const getQuote = async () => {
if (document.visibilityState === "visible") {
try {
const response = await fetch("https://api.quotable.io/random");
const {content, author, dateAdded} = await response.json();
const parsedQuote = <q>${content}</q> <br>
<p>- ${author}</p><br>
<p>Added on ${formatDate(dateAdded)}</p>;
quote.innerHTML = parsedQuote;
broadcast.postMessage(parsedQuote);
} catch (error) {
console.error(error);
}
}
};
有了这个,我们的引号就被本地化为用户的首选语言了!在我的例子中,我的navigator.language 值是"en" ,所以我的日期被格式化为MM/DD/YY。
总结
读完这篇文章后,你现在可以灵活地了解这些API的存在以及如何使用它们。尽管它们在JS现状调查中的认知度排名最后,但它们是非常有用的,知道如何使用它们肯定会提高你的开发经验。这些强大的API并不为人所知,这意味着还有一些你我都不知道的有用的API,所以现在是探索并找到那个可以简化你的代码并为你节省大量开发时间的API的最佳时机。
我希望你喜欢这篇文章,直到下一次!