了解我们提议的 Portals API 如何改进导航体验。
确保网页快速加载是提供良好用户体验的关键。但我们经常忽略的一个方面是页面转换,即用户在页面之间移动时看到的内容。
一项名为门户的新 Web 平台 API 提案旨在帮助解决此问题,通过简化用户在您网站中的导航体验来实现这一目标。
了解门户的实际运作方式:
门户支持的功能
单页应用 (SPA) 可提供出色的转换效果,但代价是构建复杂性更高。多页面应用 (MPA) 的构建难度要低得多,但最终会在页面之间出现空白页面。
门户兼具 MPA 的低复杂性和 SPA 的流畅转换,可让您两全其美。可以将它们视为 <iframe>
,因为它们允许嵌入,但与 <iframe>
不同的是,它们还附带了用于导航到其内容的功能。
眼见为实:请先查看我们在 2018 年 Chrome 开发者峰会上展示的内容:
使用传统导航时,用户必须在空白屏幕上等待,直到浏览器完成目标页面的呈现。借助门户,用户可以体验动画,而 <portal>
会预渲染内容并创建无缝的导航体验。
在门户推出之前,我们可以使用 <iframe>
渲染另一个页面。我们还可以添加动画,让框架在页面中移动。但您无法通过 <iframe>
浏览其内容。门户可弥补这一差距,从而实现有趣的用例。
试用门户
通过 about://flags 启用
如需在 Chrome 85 及更高版本中试用门户,请启用以下实验性标志:
- 为同源导航启用
about://flags/#enable-portals
标志。 - 如需测试跨源导航,请另外启用
about://flags/#enable-portals-cross-origin
标志。
在门户实验的这个早期阶段,我们还建议您通过设置 --user-data-dir
命令行标志,为测试使用完全独立的用户数据目录。启用门户后,请在 DevTools 中确认您是否看到了新的闪亮 HTMLPortalElement
。
实现门户
我们来看一个基本的实现示例。
// Create a portal with the wikipedia page, and embed it
// (like an iframe). You can also use the <portal> tag instead.
portal = document.createElement('portal');
portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web';
portal.style = '...';
document.body.appendChild(portal);
// When the user touches the preview (embedded portal):
// do fancy animation, e.g. expand …
// and finish by doing the actual transition.
// For the sake of simplicity, this snippet will navigate
// on the `onload` event of the Portals element.
portal.addEventListener('load', (evt) => {
portal.activate();
});
就是这么简单!在开发者工具控制台中试用此代码,系统应该会打开维基百科页面。
如果您想构建一个像我们在 Chrome 开发者峰会上展示的那样,并且其运作方式与上述演示完全相同的功能,请参阅以下代码段。
// Adding some styles with transitions
const style = document.createElement('style');
style.innerHTML = `
portal {
position:fixed;
width: 100%;
height: 100%;
opacity: 0;
box-shadow: 0 0 20px 10px #999;
transform: scale(0.4);
transform-origin: bottom left;
bottom: 20px;
left: 20px;
animation-name: fade-in;
animation-duration: 1s;
animation-delay: 2s;
animation-fill-mode: forwards;
}
.portal-transition {
transition: transform 0.4s;
}
@media (prefers-reduced-motion: reduce) {
.portal-transition {
transition: transform 0.001s;
}
}
.portal-reveal {
transform: scale(1.0) translateX(-20px) translateY(20px);
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
`;
const portal = document.createElement('portal');
// Let's navigate into the WICG Portals spec page
portal.src = 'https://wicg.github.io/portals/';
// Add a class that defines the transition. Consider using
// `prefers-reduced-motion` media query to control the animation.
// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion
portal.classList.add('portal-transition');
portal.addEventListener('click', (evt) => {
// Animate the portal once user interacts
portal.classList.add('portal-reveal');
});
portal.addEventListener('transitionend', (evt) => {
if (evt.propertyName == 'transform') {
// Activate the portal once the transition has completed
portal.activate();
}
});
document.body.append(style, portal);
您还可以轻松执行功能检测,以便使用门户网站逐步改进网站。
if ('HTMLPortalElement' in window) {
// If this is a platform that have Portals...
const portal = document.createElement('portal');
...
}
如果您想快速体验门户的风格,请尝试使用 uskay-portals-demo.glitch.me。请务必使用 Chrome 85 或更高版本访问该版本,并启用实验性标记!
- 输入要预览的网址。
- 然后,该页面将作为
<portal>
元素嵌入。 - 点击预览。
- 预览将在动画播放后激活。
查看规范
我们正在网络孵化社区群组 (WICG) 中积极讨论门户规范。如需快速上手,请查看一些关键场景。以下是需要熟悉的三项重要功能:
<portal>
元素:HTML 元素本身。该 API 非常简单。它由src
属性、activate
函数和用于消息传递的接口 (postMessage
) 组成。activate
接受一个可选参数,以便在激活时将数据传递给<portal>
。portalHost
接口:将portalHost
对象添加到window
对象。这样,您就可以检查网页是否已作为<portal>
元素嵌入。它还提供了一个接口,用于向主机发送消息 (postMessage
)。- PortActivateEvent 接口:在
<portal>
激活时触发的事件。有一个名为adoptPredecessor
的简洁函数,可用于将上一页检索为<portal>
元素。这样,您就可以在两个网页之间创建流畅的导航和组合体验。
我们来看看基本用法之外的内容。以下列出了您可以使用门户实现的功能(并非详尽无遗),以及示例代码。
将其嵌入为 <portal>
元素时自定义样式
// Detect whether this page is hosted in a portal
if (window.portalHost) {
// Customize the UI when being embedded as a portal
}
<portal>
元素和 portalHost
之间的消息传递
// Send message to the portal element
const portal = document.querySelector('portal');
portal.postMessage({someKey: someValue}, ORIGIN);
// Receive message via window.portalHost
window.portalHost.addEventListener('message', (evt) => {
const data = evt.data.someKey;
// handle the event
});
激活 <portal>
元素并接收 portalactivate
事件
// You can optionally add data to the argument of the activate function
portal.activate({data: {somekey: 'somevalue'}});
// The portal content will receive the portalactivate event
// when the activate happens
window.addEventListener('portalactivate', (evt) => {
// Data available as evt.data
const data = evt.data;
});
检索前驱
// Listen to the portalactivate event
window.addEventListener('portalactivate', (evt) => {
// ... and creatively use the predecessor
const portal = evt.adoptPredecessor();
document.querySelector('someElm').appendChild(portal);
});
知道您的网页被采用为前身
// The activate function returns a Promise.
// When the promise resolves, it means that the portal has been activated.
// If this document was adopted by it, then window.portalHost will exist.
portal.activate().then(() => {
// Check if this document was adopted into a portal element.
if (window.portalHost) {
// You can start communicating with the portal element
// i.e. listen to messages
window.portalHost.addEventListener('message', (evt) => {
// handle the event
});
}
});
通过组合门户支持的所有功能,您可以打造出非常出色的用户体验。例如,以下演示展示了门户如何在网站和第三方嵌入内容之间实现流畅的用户体验。
使用情形和方案
我们希望你喜欢这个简短的门户导览!我们迫不及待地想要看到您能做出什么样的作品。例如,您可能需要开始使用门户网站来实现非琐碎的导航,例如:从商品类别列表页预渲染畅销商品的页面。
另一个重要事项是,门户可以用于跨源导航,就像 <iframe>
一样。因此,如果您有多个网站互相交叉引用,还可以使用门户在两个不同的网站之间打造无缝导航。这种跨源用例是门户独有的,甚至可以改善 SPA 的用户体验。
欢迎提供反馈
您可以在 Chrome 85 及更高版本中使用门户进行实验。社区的反馈对新 API 的设计至关重要,因此请试用该 API,并告诉我们您的想法!如果您有任何功能请求或反馈,请访问 WICG GitHub 代码库。