Nuxt.js 从 iframe 内的页面发出的全局事件对父页面不可用

Nuxt.js global events emitted from page inside iframe are not available to parent page

我正在尝试创建一个模式库应用程序来显示 iframe 元素内的组件,以及它们的 HTML。每当 iframe 的内容发生变化时,我希望包含 iframe 的页面通过重新获取 iframe 的 HTML 并将其打印到页面来响应。不幸的是,该页面无法知道其 iframe 中的组件何时发生变化。这是如何设置的简化示例:

我有一个 "accordion" 组件在更新时发出全局事件:

components/Accordion.vue

<template>
  <div class="accordion"></div>
</template>

<script>
  export default {
    updated() {
      console.log("accordion-updated event emitted");
      this.$root.$emit("accordion-updated");
    }
  }
</script>

然后我将该组件拉入页面:

pages/components/accordion.vue

<template>
  <accordion/>
</template>

<script>
  import Accordion from "~/components/Accordion.vue";

  export default {
    components: { Accordion }
  }
</script>

然后我在另一页的 iframe 中显示该页:

pages/documentation/accordion.vue

<template>
  <div>
    <p>Here's a live demo of the Accordion component:</p>
    <iframe src="/components/accordion"></iframe>
  </div>
</template>

<script>
  export default {
    created() {
      this.$root.$on("accordion-updated", () => {
        console.log("accordion-updated callback executed");
      });
    },
    beforeDestroy() {
      this.$root.$off("accordion-updated");
    }
  }
</script>

当我编辑 "accordion" 组件时,"event emitted" 日志出现在我的浏览器控制台中,因此似乎正在发出 accordion-updated 事件。不幸的是,我从未在 documentation/accordion 页面的事件处理程序中看到 "callback executed" 控制台日志。我试过同时使用 this.$root.$emit/this.$root.$onthis.$nuxt.$emit/this.$nuxt.$on,但似乎都不起作用。

是否可能每个页面都包含一个单独的 Vue 实例,因此 iframe 页面的 this.$root 对象与 documentation/accordion 页面的 this.$root 对象不同?如果是这样,那我该如何解决这个问题?

听起来我是对的,在我的 iframe 页面及其父页面中确实有两个单独的 Vue 实例:https://forum.vuejs.org/t/eventbus-from-iframe-to-parent/31299

所以我最终将 MutationObserver 附加到 iframe,如下所示:

<template>
  <iframe ref="iframe" :src="src" @load="onIframeLoaded"></iframe>
</template>

<script>
  export default {
    data() {
      return { iframeObserver: null }
    },
    props: {
      src: { type: String, required: true }
    },
    methods: {
      onIframeLoaded() {
        this.getIframeContent();

        this.iframeObserver = new MutationObserver(() => {
          window.setTimeout(() => {
            this.getIframeContent();
          }, 100);
        });
        this.iframeObserver.observe(this.$refs.iframe.contentDocument, {
          attributes: true, childList: true, subtree: true
        });
      },
      getIframeContent() {
        const iframe = this.$refs.iframe;
        const html = iframe.contentDocument.querySelector("#__layout").innerHTML;
        // Print HTML to page
      }
    },
    beforeDestroy() {
      if (this.iframeObserver) {
        this.iframeObserver.disconnect();
      }
    }
  }
</script>

将观察者直接附加到 contentDocument 意味着除了 <body> 之外,当文档 <head> 中的元素发生变化时,我的事件处理程序也会触发。这允许我在 Vue 将新的 CSS 或 JavaScript 块注入 <head> 时做出反应(通过热模块替换)。