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

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

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

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

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

3 ответов


есть несколько способов передать многосоставное сообщение на 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 объект перед сдачей, только в первом случае браузер делает свой собственный сериализации (и последующей десериализации), а во втором маршруте это до вас, чтобы сериализовать/восстановить.

пункты выноса здесь:

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

документация и литература


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

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

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

phostMessage(iframe, someObj, callback);

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

  1. вам понадобится общий идентификатор обратного вызова прошло между кадрами сообщающийся.
  2. на sender создает уникальный идентификатор обратного вызова на каждом сообщении и сохраняет его в хэше поиска обратного вызова, чтобы найти обратный вызов после отправки.
  3. на приемник сообщения обеспечивает только идентификатор обратного вызова отправляется обратно.
  4. все фреймы, сообщающиеся, используют одну и ту же библиотеку 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), '*');
}

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

поэтому последняя строка кода для обработки события сообщение:

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

что позволяет асинхронную вещи случаться.


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

цель

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

источник

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