发布于 2026-01-06 4 阅读
0

额外内容:使用 Web Components 解决 Vue 中的问题

额外内容:使用 Web Components 解决 Vue 中的问题

这是我正在撰写的关于 Web 组件系列文章的番外篇。

  1. 第一部分:标准
  2. 第二部分:填充物
  3. 第三部分:香草组件

第四部分,关于 Polymer 库的内容,正在撰写中。在此期间,不妨看看一位学生向我提出的这个有趣的问题,我们可以用 Web 标准来解决:

他们使用一个库在 Vue 组件中渲染 WebGL 地球仪。他们想要生成一组标记,然后跟踪哪些标记被打开,哪些被关闭。该 WebGL 库提供了一些 API,可以将字符串附加innerHTML到每个标记的弹出窗口中,但没有提供任何用于跟踪打开、关闭或点击事件的 API。

我突然冒出一个有点邪恶的想法😈。如果我们不能给库弹出窗口添加行为,但可以添加HTML,那么如果我们添加的HTML能够封装它自己的行为呢?

🎩 Web Components 来救场啦!!👨‍💻

定义<popup-tooltip>

我们需要的是一个 HTML 元素,当它包含的弹出窗口打开或关闭时,该元素会触发一个事件。WebGL 库过去常常用于style="visibility: visible"打开和关闭弹出窗口,所以我们将创建一个元素,使其MutationObserver能够监听自身的父元素。

class PopupTooltip extends HTMLElement {
  constructor() {
    super();
    this.observerCallback = this.observerCallback.bind(this);
    this.attachShadow({mode: 'open'});
    this.shadowRoot.appendChild(document.createElement('slot'));
    this.observer = new MutationObserver(this.observerCallback);
  }

  connectedCallback() {
    // HACK: WebGL library toggles style.visibility on it's own
    // generated DOM to hide and show tooltips.
    const libraryContainer = this.parentElement.parentElement.parentElement;
    const config = { attributes: true, subtree: false, children: false };
    this.observer.observe(libraryContainer, config);
  }

  observerCallback([{target}]) {
    const visible = target.style.visibility === 'visible';
    const type = 'popup-' + visible ? 'opened' : 'closed';
    const bubbles = true;
    const composed = true;
    const detail = this;
    this.dispatchEvent(new CustomEvent(type, { bubbles, composed, detail }));
  }
}

customElements.define('popup-tooltip', PopupTooltip);
Enter fullscreen mode Exit fullscreen mode

连接到 Vue 包装器

现在我们有了一个元素,当其容器的可见性被 WebGL 库切换时,<popup-tooltip>该元素会触发一个popup-opened事件。我们在包裹它的 Vue 组件的私有 DOM 中设置了监听器:popup-closed

<!-- WebGL lib instanciates on '#earth' -->
<div id="earth" @popup-opened="onPopupOpened" @popup-closed="onPopupClosed"></div>
Enter fullscreen mode Exit fullscreen mode

创建每个弹出窗口

然后,当我们实例化 WebGL 库并传递我们的数据时,我们设置标记以<popup-tooltip>在其工具提示内容中显示元素。

geoMarkers.forEach(marker => {
  const location = marker.latLng;
  const glMarker = new WebGLLib.popup({/*...*/});
  // NB: popupHTML is **just HTML**, there's no framework or library here.
  const popupHTML = `<popup-tooltip data-location="${location}">${marker.title}</popup-tooltip>`;
  // `bindPopup` is a method on our WebGL library's marker API.
  glMarker.bindPopup(popupHTML, config);
})
Enter fullscreen mode Exit fullscreen mode

利润!

最后我们需要做的就是追踪哪些弹出窗口被打开,哪些被关闭。

onPopupOpened({target: {dataset: {location}}}) {
  const [lat, lng] = location.split(',');
  console.log(`opened: lat: ${lat} lng: ${lng}`);
}
Enter fullscreen mode Exit fullscreen mode

你无需放弃现有框架即可使用 Web 组件。只要能使用 HTML 和 JavaScript,你就可以在任何需要的地方使用它们。这正是 Web 组件的优势所在:我们的 GL 库并不接受 Vue 组件作为输入,而是接受一串 HTML 代码。

几天后我们将继续进行关于 Polymer 库的第四部分,敬请期待。

您是否想就这里涵盖的任何主题进行一对一指导?请通过 Codementor 联系我

文章来源:https://dev.to/bennypowers/bonus-solving-problems-in-vue-with-web-components-2582