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

了解提议的 Portals API 如何提升您的导航用户体验。

宇都宫雄介
Yusuke Utsunomiya

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

我们推出名为 Portals 的新 Web 平台 API 提案,旨在简化用户在您网站间导航的体验,从而简化用户体验

查看门户的实际运行情况:

使用门户无缝嵌入和导航。由 Adam Argyle 创建。

门户能实现哪些功能

单页应用 (SPA) 可提供很好的过渡,但代价是构建更为复杂。多页面应用 (MPA) 的构建起来要容易得多,但页面之间最终会出现空白屏幕。

门户两全其美:MPA 的复杂度低,而且 SPA 可以无缝过渡。您可以将它们视为支持嵌入的 <iframe>,但与 <iframe> 不同的是,它们还附带了导航到其内容的功能。

“眼见为实”: 请先查看我们在 2018 年 Chrome 开发者峰会上展示的内容:

在传统导航中,用户必须等待浏览器完成目的地渲染,才能看到空白屏幕。借助门户,用户可以体验动画,而 <portal> 会预呈现内容并打造无缝的导航体验。

在 Portal 推出之前,我们可以使用 <iframe> 呈现另一个页面。我们还可以添加动画,使框架在网页中四处移动。但是,<iframe> 不允许您导航到其内容。门户缩小了这一差距,实现了有趣的用例。

试用传送门

通过 about://flags 启用

翻转实验性标志,在 Chrome 85 及更高版本中试用传送门:

  • 为同源导航启用 about://flags/#enable-portals 标志。
  • 如需测试跨源导航,请另外启用 about://flags/#enable-portals-cross-origin 标志。

在门户实验的早期阶段,我们还建议您设置 --user-data-dir 命令行标志,为测试使用完全独立的用户数据目录。启用 Portal 后,在开发者工具中确认你拥有全新的闪亮 HTMLPortalElement

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

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

预览门户样式演示的 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

查看规范

我们正在 Web Incubation 社区小组 (WICG) 中积极讨论门户规范。为了快速上手,我们来看一些关键场景。您需要熟悉以下三个重要功能:

  • <portal> 元素:HTML 元素本身。API 非常简单。它包含 src 属性、activate 函数和消息传递接口 (postMessage)。activate 采用一个可选参数在激活时将数据传递给 <portal>
  • portalHost 接口:将 portalHost 对象添加到 window 对象。这样,您就可以检查页面是否作为 <portal> 元素嵌入。它还提供了一个用于将消息 (postMessage) 发回给主机的接口。
  • PortalActivateEvent 界面:在 <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
    });
  }
});

通过结合门户支持的所有功能,您可以打造非常出色的用户体验。例如,下面的演示展示了门户如何在网站和第三方嵌入内容之间实现无缝用户体验。

应用场景和计划

希望你喜欢这个简短的传送门导览!我们已经迫不及待地想看看您可以提出哪些创意了。例如,您可能想开始使用门户进行复杂的导航,例如:从商品类别列表页面中预呈现畅销商品页面。

另外需要注意的一点是,Portal 可以像 <iframe> 一样用于跨源导航。因此,如果您有多个相互交叉引用的网站,您也可以使用门户在两个不同网站之间打造无缝导航。这种跨源用例是门户独有的,甚至可以改善 SPA 的用户体验。

欢迎提供反馈

门户已准备好在 Chrome 85 及更高版本中进行实验。来自社区的反馈对于设计新 API 至关重要,因此请试用一下并告诉我们您的想法!如果您有任何功能请求或反馈,请前往 WICG GitHub 代码库