Различные варианты отладки интерфейсных и интерфейсных интерфейсов, узнайте о

JavaScript

Обычно интерфейс отлаживается совместно с серверной частью или на стороне клиента со встроенным веб-просмотром, что, черт возьми, совместная отладка интерфейса и интерфейса? На самом деле он тоже существует.Например, другой фронтенд написал огромный модуль (типа игр, онлайн-IDE, страниц визуального редактирования и т.д., которые требуют среды песочницы), и в это время необходимо использовать iframe, чтобы использовать его. В большом спросе, согласно модульному разделению труда, очевидно, что один человек отвечает за функции в iframe, а другой человек отвечает за главную страницу. Вещи, за которые отвечают разные люди, отображаются и взаимодействуют на странице одновременно, поэтому в процессе двух фронтенд-разработок должен быть совместный процесс отладки.

Предыстория: iframe находится на родительской странице index.html, а src iframe является дочерней страницей (ссылка на другой html).Следующее основано на этой ситуации.

Традиционный способ - обмен почтовыми сообщениями iframe

// 父页面的js
    document.querySelector("iframe").onload = () => {
      window.frames[0].postMessage("data from parent", "*");
    };

// 子页面的js
    window.addEventListener(
      "message",
      e => {
        console.log(e); // e是事件对象,e.data即是父页面发送的message
      },
      false
    );

Это более традиционный метод. Пожалуйста, будьте в курсе,addEventListenerПри получении сообщения необходимо сначала использовать свойства происхождения и исходных свойств объекта события для проверки идентификации отправителя сообщения, если появятся ошибки, могут привести к скриптам скрепления на месте.И необходимость использовать пост-сообщение триггера загрузки iframe.

Сообщение об изменении хэша для iframe

Средство для снижения порога, вы можете пересекать домен

родительская страница

    const iframe = document.querySelector("iframe");
    const { src } = iframe;
// 把数据转字符串,再通过哈希传递到子页面
    function postMessageToIframe(data) {
      iframe.src = `${src}#${encodeURIComponent(JSON.stringify(data))}`;
    }

дочерняя страница iframe

    window.onhashchange = e => {
// 监听到哈希变化,序列化json
      const data = JSON.parse(decodeURIComponent(location.hash.slice(1)));
      console.log(data, "data >>>>");
    };

Откройте родительскую страницу и выполнитеpostMessageToIframe({ txt: 'i am lhyt' }), вы можете видеть, что консоль имеет обратную связь на подстранице:

В свою очередь, дочерняя страница взаимодействует с родительской страницей, используя родительскую:

// 子页面
parent.postMessageToIframe({ name: "from child" })

// 父页面, 代码是和子页面一样的
    window.onhashchange = () => {
      const data = JSON.parse(decodeURIComponent(location.hash.slice(1)));
      console.log(data, "data from child >>>>");
    };

Уведомление:

  • Для передачи хэша от родителя к дочернему порогу нет, он может быть междоменным, и вы можете напрямую открыть html двойным щелчком.
  • Когда дочерняя страница использует родительскую, об ошибке будет сообщено во всех доменах.Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.

событие хранения

Связь между родительско-дочерними страницами iframe

localstorageЭто пространство для хранения, совместно используемое браузером с тегом домена. html5 поддерживает одинonstorage

требует внимания 此事件是非当前页面对localStorage进行修改时才会触发,当前页面修改localStorage不会触发监听函数!!!

// 父页面
    setTimeout(() => {
      localStorage.setItem("a", localStorage.getItem("a") + 1);
    }, 2000);
    window.addEventListener("storage", e => console.log(e, "parent"));

// 子页面
window.addEventListener("storage", e => console.log(e, "child"));

Больше насажденного операции, общайтесь с собой

Есть две страницы, и нам нужно написать два html.Есть ли способ не писать два html, а только один html? На самом деле это возможно!

добавить в URLqueryПараметры или хэш, указывающий, что страница является подстраницей. Если это родительская страница, создайте iframe, SRC — это ссылка на эту страницу плюсqueryпараметр.HTML-код родительской страницы не нуждается ни в каких других тегах, требуется только скрипт

    const isIframe = location.search;
    if (isIframe) {
// 子页面
      window.addEventListener("storage", e => console.log(e, "child"));
    } else {
// 父页面,创建一个iframe
      const iframe = document.createElement("iframe");
      iframe.src = location.href + "?a=1";
      document.body.appendChild(iframe);
      setTimeout(() => {
        localStorage.setItem("a", localStorage.getItem("a") + 1);
      }, 2000);
      window.addEventListener("storage", e => console.log(e, "parent"));
    }

MessageChannel

MessageChannelСоздает новый канал сообщений и отправляет данные через два его свойства MessagePort и доступен в Web Worker.MessageChannelЭкземпляр имеет два свойства:portl1а такжеport2. Отправьте сообщение на порт1, и порт2 его получит.

// 父页面
    const channel = new MessageChannel();
// 给子页面的window注入port2
    iframe.contentWindow.port2 = channel.port2;
    iframeonload = () => {
      // 父页面使用port1发消息,port2会收到
      channel.port1.postMessage({ a: 1 });
    };

// 子页面,使用父页面注入的port2
    window.port2.onmessage = e => {
      console.error(e);
    };

Преимущества MessageChannel: объекты можно передавать без ручной сериализации и десериализации, а другой порт получает глубокую копию объекта.

SharedWorker

Это своего рода воркер, который может использоваться несколькими страницами одновременно, и к нему можно получить доступ из нескольких контекстов просмотра, таких как несколько окон, фреймов, воркеров. У него другая глобальная область видимости - только некоторые из методов под обычным виндоу. Разрешить нескольким страницам совместно использовать работника и использовать работника в качестве средства для достижения связи.

код работника

// 存放所有的连接端口
const everyPorts = [];
onconnect = function({ ports }) {
// onconnect一触发,就存放到数组里面
  everyPorts.push(...ports);
// 每次连接所有的端口都加上监听message事件
  [...ports].forEach(port => {
    port.onmessage = function(event) {
// 每次收到message,对所有的连接的端口广播,除了发消息的那个端口
      everyPorts.forEach(singlePort => {
        if (port !== singlePort) {
          singlePort.postMessage(event.data.data);
        }
      });
    };
  });
};

Родительский код JS код

    const worker = new SharedWorker("./worker.js");
    window.worker = worker;
    worker.port.addEventListener(
      "message",
      e => {
        console.log("parent:", e);
      },
      false
    );
    worker.port.start();
    setTimeout(() => {
      worker.port.postMessage({
        from: "parent",
        data: {
          a: 111,
          b: 26
        }
      });
    }, 2000);

Код JS из IFrame Child Page:

    const worker = new SharedWorker("./worker.js");
    worker.port.onmessage = function(e) {
      console.log("child", e);
    };
    worker.port.start();
    setTimeout(() => {
      worker.port.postMessage({ data: [1, 2, 3] });
    }, 1000);

Объекты и метод прямого впрыска

Во многих приведенных выше примерах используется contentWindow.Поскольку contentWindow является собственным окном iframe, мы можем внедрить любое содержимое по желанию для вызова iframe. Одним из часто используемых методов совместной отладки на стороне клиента и на стороне клиента является внедрение функций. Дочерняя страница вызывает метод родительской страницы.Поскольку существует глобальный атрибут parent, также можно получить окно родительской страницы.

// 父页面
    document.querySelector("iframe").contentWindow.componentDidMount = () => {
      console.log("iframe did mount");
    };

// 子页面
    window.onload = () => {
      // 假设这里有react一系列流程运行...
      setTimeout(() => {
        // 假设现在是react组件didmount的时候
        window.componentDidMount && window.componentDidMount();
      }, 1000);
    };

Далее, на основе метода внедрения окна в iframe, разрабатывается простой коммуникационный модуль.

  • Родительская страница активно вызывает дочернюю страницу, а дочерняя страница вызывается родительской страницей.
  • Родительская страница вызывается дочерней страницей, а дочерняя страница вызывается родительской страницей.

Под родительской страницей к окну присоединен объект parentPageApis, представляющий собой набор вызывающих методов для дочерних страниц. И введите метод callParentApi на дочернюю страницу, чтобы вызвать метод родительской страницы.

   const iframe = document.querySelector("iframe");

    window.parentPageApis = window.parentPageApis || {};
    // 父页面自己给自己注入子页面调用的方法
    Object.assign(window.parentPageApis, {
      childComponentDidMount() {
        console.log("子页面did mount");
      },
      changeTitle(title) {
        document.title = title;
      },
      showDialog() {
        // 弹窗
      }
    });

// 给子页面注入一个callParentApi的方法来调父页面
    iframe.contentWindow.callParentApi = function(name, ...args) {
      window.parentPageApis[name] && window.parentPageApis[name].apply(null, args);
    };

    iframe.contentWindow.childPageApis =
      iframe.contentWindow.childPageApis || {};
    Object.assign(iframe.contentWindow.childPageApis, {
      // 父页面也可以给子页面注入方法
    });
    setTimeout(() => {
// 调用子页面的方法
      callChildApi("log", "父页面调子页面的log方法打印");
    }, 2000);

Дочерняя страница также внедряет метод callChildApi в родительскую страницу и записывает некоторые из своих собственных наборов внешних методов в childPageApis.

    window.childPageApis = window.childPageApis || {};
    Object.assign(window.childPageApis, {
      // 子页面自己给自己注入方法
      log(...args) {
        console.log(...args);
      }
    });
    window.parent.window.callChildApi = function(name, ...args) {
      window.childPageApis[name] && window.childPageApis[name].apply(null, args);
    };
    window.onload = () => {
      // 假设这里有react一系列流程运行...
      setTimeout(() => {
        // 假设现在是react组件didmount的时候,告诉父页面
        window.callParentApi("childComponentDidMount");
      }, 1000);
    };

наконец

Вышеупомянутые решения для хранения и SharedWorker также применимы к проблеме «общения между разными вкладками». В целом, SharedWorker более безопасен, он более гибок для внедрения глобальных методов, а связь с преобразованием хэшей проще. Почтовые сообщения, изменения хэшей и события хранения основаны на строках, а MessageChannel и SharedWorker могут передавать любое «копируемое значение». Глобальная инъекция может делать все что угодно, но она же и самая опасная, и нужно соблюдать меры предосторожности

Никакая общественная озабоченность «не тот же интерфейс», чтобы узнать другую точку зрения на интерфейс, быстрорастущий, играющий с новейшими технологиями, исследующий черный и