PostMessage с несколькими функциями или пользовательскими обратными вызовами - PullRequest
18 голосов
/ 12 декабря 2011

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

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

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

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

Ответы [ 4 ]

54 голосов
/ 13 декабря 2011

Существует несколько способов передачи сообщения, состоящего из нескольких частей, в обработчик 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 ( source ), данные, передаваемые обработчику пост-сообщений, могут быть объектами.Объект будет сериализован, поэтому есть некоторые проблемы на этом фронте, но:

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 для чего-либо, кроме кадров.( source ).

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

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

Точки забора здесь:

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

Документация и справочные материалы

3 голосов
/ 25 июня 2015

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

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

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

phostMessage(iframe, someObj, callback);

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

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

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

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

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

что позволяет асинхронным вещам происходить.

0 голосов
/ 19 марта 2019

Я недавно столкнулся с той же проблемой. После нескольких часов поисков я наткнулся на post-robot . Он разработан paypal и решил большинство моих проблем, включая обратный вызов для postMessage.

Он также поддерживает функции передачи внутри полезной нагрузки.

Ознакомиться с введением можно здесь Представляем пост-робота

0 голосов
/ 04 августа 2012

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

Target

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

Source

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