亲身体验门户:在网络上实现无缝导航

了解我们提议的 Portals API 如何改进导航体验。

Yusuke Utsunomiya
Yusuke Utsunomiya

确保网页快速加载是提供良好用户体验的关键。但我们经常忽略的一个方面是页面转换,即用户在页面之间移动时看到的内容。

一项名为门户的新 Web 平台 API 提案旨在帮助解决此问题,通过简化用户在您网站中的导航体验来实现这一目标。

了解门户的实际运作方式:

借助门户实现流畅的嵌入和导航。创建者:Adam Argyle

门户支持的功能

单页应用 (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

显示 HTMLPortalElement 的 DevTools 控制台的屏幕截图

实现门户

我们来看一个基本的实现示例。

// 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();
});

就是这么简单!在开发者工具控制台中试用此代码,系统应该会打开维基百科页面。

预览门户网站样式演示的 GIF

如果您想构建一个像我们在 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 或更高版本访问该版本,并启用实验性标记

  1. 输入要预览的网址。
  2. 然后,该页面将作为 <portal> 元素嵌入。
  3. 点击预览。
  4. 预览将在动画播放后激活。

一张 GIF,显示了使用门户的 Glitch 演示

查看规范

我们正在网络孵化社区群组 (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 代码库