Подтвердить что ты не робот

PostMessage с несколькими функциями или пользовательскими обратными вызовами

До сих пор я видел только учебники для postmessage, где одно окно отправляет один вид сообщения, а другое окно интерпретирует сообщение только одним способом.

Что делать, если я хочу иметь много разных видов взаимодействия между окнами, может ли postmessage обрабатывать это?

Это противоречит тому, что должно делать postmessage?

Например, что, если я хочу иметь возможность отправлять пользовательские обратные вызовы взад и вперед и т.д.?

4b9b3361

Ответ 1

Существует несколько способов передать многочастное сообщение обработчику postMessage. Первый (и менее "чистый" способ) заключается в использовании символа разделителя, а затем передачи данных через строку.

Скажем, мы хотели передать идентификатор пользователя, действие и имя пользователя. Строка будет выглядеть так:

54|do_logout|chris

В обработчике postMessage переданные данные могут быть split (docs) на символе |, тогда каждый сегмент сообщения может быть используется по мере необходимости.

Другой способ вместо ручного создания/разделения строки - использовать JSON (docs) для преобразования объекта в строку с одной стороны и использовать JSON для преобразования обратно в объект в обработчике.

var pass_data = {
    'name':'Chris',
    'id':54,
    'action':'do_logout'
};
target_window.postMessage(JSON.stringify(pass_data), "http://www.example.net");

... затем в обработчике:

function (event) {
    var pass_data = JSON.parse(event.data);
}

Обязательно проверьте, однако, что объект JSON не предоставляется для всех агентов пользователя, особенно для старых. Существует множество (многих, многих) сторонних библиотек для поддержки поддержки JSON, поэтому не позволяйте отсутствию полного принятия напугать вас - JSON определенно является безопасным стандартом "продвижения вперед".

Не было бы лучше, если бы мы могли просто передать этот объект сразу? Ну, глядя в Firefox 6 (источник), данные, которые вы передаете обработчику postmessage, могут быть объектом. Объект будет сериализован, поэтому на этом фронте есть некоторые проблемы, но:

var pass_data = {
    'name':'Chris',
    'id':54,
    'action':'do_logout'
};
target_window.postMessage(pass_data, "http://www.example.net");

Немного лучше, а? К сожалению, текущие версии IE будут иметь дело только со строками. Мне не удалось найти обсуждение будущих планов относительно postMessage для IE 10. Кроме того, в IE 8/9 есть известная ошибка, которая разбивает postMessage на что-либо иное, кроме фреймов. (источник).

Переход к конкретному аспекту вашего вопроса - обратные вызовы. Если вы не можете передать обратный вызов по имени функции, нет способа передать функцию; никаких анонимных функций для вас. Это связано с тем, как данные фактически передаются обработчику. На практике "нет" поддержки объектов как данных, за кулисами браузер превращает ваш прошедший объект в строку (сериализация).

Итак, вы должны понимать, что передача объекта точно такая же, как использование JSON для stringify объекта перед передачей, только в первом случае браузер выполняет свою собственную сериализацию (и последующую нессеризацию) тогда как с последним маршрутом вам придется сериализовать /unserialize.

Точки вылета здесь:

  • postMessage все еще имеет ограниченную поддержку кросс-браузера.
  • Тенденция для более новых версий совместимых со стандартами браузеров заключается в том, чтобы разрешить прохождение объектов в дополнение к строкам
  • Прошедший объект будет сериализован, поэтому ссылки на функции не будут разрешены
  • Самая широкая поддержка "в дикой природе" предназначена для данных только для строк, что означает, что вам придется придерживаться строк и "упаковывать" ваши данные, как показано выше, если вы хотите поддерживать самые разные пользовательские агенты
  • Internet Explorer погубит все ваши планы (в том числе семейные праздники).

Документация и ссылки

Ответ 2

Обратные вызовы с postMessage: очень возможно и очень полезно

Есть хороший плагин, который я нашел на npm под названием "серебристая пуля" . Он выполняет postMessage с обратными вызовами и использует eventEmitter для получения определенных событий. Это очень приятно.

Но для реализации этого я бы сделал что-то вроде...

phostMessage(iframe, someObj, callback);

Вы должны сделать это:

  • Вам требуется общий идентификатор обратного вызова, передаваемый между передаваемыми фреймами.
  • отправитель создает уникальный идентификатор обратного вызова для каждого сообщения и сохраняет его в обратном вызове, чтобы найти обратный вызов после отправки.
  • приемник сообщения обеспечивает только идентификатор обратного вызова.
  • Для обмена всеми кадрами для этого используется одна и та же библиотека JS.

Вот очень простая демонстрация этого:

var callbacks = {};

// when receiving messages
window.addEventListener('message', function(ev) {
  // todo: add origin check
  if (!ev.data)
    return;

  var message;
  try {
    message = JSON.parse(ev.data);
  } catch (ex) {
    console.error(ex);
  }

  // ignore messages not having a callback ID
  if (!message || !message.callbackId)
    return;

  // we are the sender getting the callback
  if (callbacks[message.callbackId]) {
    callbacks[message.callbackId](message);
    delete callbacks[message.callbackId];
    return;
  }

  // we are the receiver so we respond with the callback ID
  // todo: restrict who can receive message (last param)
  iframe.contentWindow.postMessage(JSON.stringify(message), '*');
});

// when sending messages
function phostMessage(iframe, obj, callback) {
  obj.eventId = Math.random();
  callbacks[obj.eventId] = callback;
  // todo: restrict who can receive message (last param)
  iframe.contentWindow.postMessage(JSON.stringify(obj), '*');
}

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

Таким образом, последняя строка кода для обработки событий сообщения:

if (messageHandler[message.handler])
  messageHandler[message.handler](message, function() {
    iframe.contentWindow.postMessage(JSON.stringify(message), '*');
  });
else
  iframe.contentWindow.postMessage(JSON.stringify(message), '*');

который позволяет асинхронным вещам.

Ответ 3

Один довольно простой способ вызвать обратные вызовы без передачи какого-либо реального кода:

Target

var callbacks = {
  myCallback: function() { doSomething(); }
};
window.addEventListener('message', function (ev) {
  // origin checking etc
  callbacks[ev.data]();
}, false);

Источник

target.postMessage('myCallback', 'http://www.example.com');