postMessage очень полезен

внешний интерфейс

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

Определение сообщения

postMessage – это API, представленный html5. Метод postMessage() позволяет сценариям из разных источников эффективно взаимодействовать асинхронно, обеспечивая кросс-текстовый документ, многооконный и междоменный обмен сообщениями. Он в основном используется для обмена данными между windows, что также позволяет стать эффективным решением для междоменной связи.

Совместимость postMessage

На следующем рисунке показан скриншот совместимости с postMessage, найденный на caniuse.За исключением низкой поддержки браузера IE, поддержка других браузеров хорошая.

Введение в API postMessage

отправить данные:

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow

Ссылка на окно, такое как свойство contentWindow элемента iframe, объект окна, возвращаемый при выполнении команды window.open, или именованный или числовой индекс window.frames.

message

Данные, которые будут отправлены в другие окна, будут обработаны [Алгоритмом структурированного клонирования](developer.Mozilla.org/en-US/docs/…) сериализация Это означает, что вы можете безопасно передавать объект данных в целевое окно без каких-либо ограничений, не сериализуя его самостоятельно.

targetOrigin

Атрибут происхождения окна используется для указания того, какие окна могут получать событие сообщения. После указания только окно под соответствующим источником может получать сообщение. Установка подстановочного знака "*" означает, что оно может быть отправлено в любое окно, но обычно это не рекомендуется из соображений безопасности. Сделайте это. Если вы хотите отправить в окно того же источника, что и текущее окно, установите его в "/"

передача | необязательный атрибут

Является ли строка объектов **Transferable** переданной одновременно с сообщением, право собственности на эти объекты будет передано получателю сообщения, а отправитель больше не сохранит право собственности.

Получение данных: прослушивание появления событий сообщения

window.addEventListener("message", receiveMessage, false) ;
function receiveMessage(event) {
     var origin= event.origin;
     console.log(event);
}

Скриншот результата печати объекта события выглядит следующим образом:

Здесь мы сосредоточимся на четырех свойствах объекта события.

  • data :Относится к объекту сообщения, отправленному из других окон;

  • type:относится к типу отправленного сообщения;

  • source:Ссылается на объект окна, который отправляет сообщение;

  • origin:Ссылается на источник окна, отправившего сообщение

Сценарии использования postMessage

Сценарий 1 Междоменная связь (включая запрос GET и запрос POST)

Все мы знаем, что JSONP может решить междоменную проблему GET-запросов, но не может решить междоменную проблему POST-запросов. А postMessage может. Вот только пример, только для справки, как писать конкретный код зависит по конкретному сценарию.

Родительская форма создает междоменный iframe и отправляет информацию

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>跨域POST消息发送</title>
        <script type="text/JavaScript">    
            // sendPost 通过postMessage实现跨域通信将表单信息发送到 moweide.gitcafe.io上,
            // 并取得返回的数据    
            function sendPost() {        
                // 获取id为otherPage的iframe窗口对象        
                var iframeWin = document.getElementById("otherPage").contentWindow;        
                // 向该窗口发送消息        
                iframeWin.postMessage(document.getElementById("message").value, 
                    'http://moweide.gitcafe.io');    
            }    
            // 监听跨域请求的返回    
            window.addEventListener("message", function(event) {        
                console.log(event, event.data);    
            }, false);
        </script>
    </head>
    <body> 
        <textarea id="message"></textarea> 
        <input type="button" value="发送" onclick="sendPost()"> 
        <iframe
            src="http://moweide.gitcafe.io/other-domain.html" id="otherPage"
            style="display:none"></iframe>
    </body>

</html>

Подформа получает информацию и обрабатывает ее.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>POST Handler</title>
        <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
        <script type="text/JavaScript">
            window.addEventListener("message", function( event ) {
                // 监听父窗口发送过来的数据向服务器发送post请求
                var data = event.data;
                $.ajax({
                    // 注意这里的url只是一个示例.实际练习的时候你需要自己想办法提供一个后台接口
                    type: 'POST', 
                    url: 'http://moweide.gitcafe.io/getData',
                    data: "info=" + data,
                    dataType: "json"
                }).done(function(res){        
                    //将请求成功返回的数据通过postMessage发送给父窗口        
                    window.parent.postMessage(res, "*");    
                }).fail(function(res){        
                    //将请求失败返回的数据通过postMessage发送给父窗口        
                    window.parent.postMessage(res, "*");    
                });
            }, false);
        </script>
    </head>

    <body></body>
</html>

Сценарий 2. Веб-воркер

Язык JavaScript использует однопоточную модель. Вообще говоря, все задачи выполняются в одном потоке, и одновременно может выполняться только одна задача. Последующие задачи могут выполняться только после выполнения предыдущих задач. Если метод сталкивается со сложными и трудоемкими вычислениями, это вызовет блокировку и серьезно затруднит нормальную работу приложения.Веб-воркер предоставляет простой способ для веб-контента запускать скрипты в фоновом потоке, а поток может выполнять задачи без беспокоит пользователя. Интерфейс. После создания работник может отправлять сообщения коду JavaScript, который его создал, через сообщения, отправленные в обработчики событий, указанные в коде.

Пробуждающий элемент использует конструктор для создания объекта, запускает именованный файл JavaScript — этот файл будет содержать код для запуска в рабочем потоке, пробуждающий механизм запускается в другом глобальном контексте, в отличие от текущего окна, к которому нельзя получить доступ с помощью глобальных свойств окна. .

некоторые ограничения

  • Можно загружать только файлы сценариев того же происхождения, а узлами DOM нельзя напрямую манипулировать.

  • Рабочий поток не может выполнитьсяalert()Методы иconfirm()метод, но вы можете использовать объект XMLHttpRequest для выполнения запросов AJAX.

  • Не удается прочитать локальные файлы, можно загружать только сетевые файлы

  • Вы также не можете использовать методы и свойства по умолчанию объекта окна, однако вы можете использовать ряд вещей в объекте окна, включая механизмы хранения данных, такие как webSocket, indexedDB и специфичный для FireFoxOS API DataStore.Functions and classes available to workersПолучить подробности.

Передача данных между воркерами и основным потоком осуществляется через механизм сообщений — обе стороны отправляют свои собственные сообщения, используя метод postMessage(), и используют обработчик события onmessage для ответа на сообщения (сообщения содержатся в[Message](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/Message "/zh-CN/docs/Web/Reference/Events/Message")в атрибуте данных события). В этом процессе данные не распределяются, а копируются, воркеры делятся на выделенных воркеров и общих воркеров. то же время.

Пример использования выделенного работника:

// main.js
if(window.Worker) {
    var myWorker = new Worker('http://xxx.com/worker.js');
    // 发送消息
    first.onchange = function() {
        myWorker.postMessage([first.value, second.value]);
        console.log("Message posted to worker");
    }
    second.onchange = function() {
      myWorker.postMessage([first.value,second.value]);
      console.log('Message posted to worker');
    }
    // 主线程 监听onmessage以响应worker回传的消息
    myWorker.onmessage = function (e) {
      var textContent = e.data;
      console.log("message received from worker");  
    }
}

// worker.js

// 内置selfduixiang,,代表子线程本身, worker内部要加载其他脚本,可通过importScripts()方法
onmessage = function(e) {
    console.log("message received from main script");
    var workerResult = "Result: " + (e.data[0] * e.data[1]);
    console.log("posting message\back to main script");
    postMessage(workerResult);
}

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

Использование сбора данных скрытых точек: вы можете собирать данные в main.js, отправлять собранную информацию в worker.js через postMessage, выполнять связанные операции и сортировку в waker.js и отправлять их на сервер; конечно же, без использования Web. Woker, создав iframe в index.html в одностраничном приложении, также можно переключаться между страницами и собирать такие данные, как время пребывания на странице.Я не буду вдаваться в подробности конкретной реализации.Заинтересованные студенты могут выйти в интернет Ищите решения, если у вас есть какие-либо вопросы, пожалуйста, пришлите мне личное сообщение~~~

Сценарий 3 Сервисный работник

Вы можете увидеть наличие Service Worker в приложении консоли браузера

Service Worker — оптимальное решение для автономного хранения веб-приложений.Сходство между Service Worker и Web Worker в том, что в дополнение к обычному потоку js engine открывается новый поток js для обработки некоторых сервисов, которые не подходят для обработки на основном thread., отличия в основном заключаются в следующих моментах:

  • Службы в стиле Web Worker для конкретной страницы, а Service Worker можно использовать на нескольких страницах после регистрации и установки.

  • Service Worker находится в браузере и не будет уничтожен при закрытии страницы.По сути, это фоновый поток, который завершится только в том случае, если вы активно завершите его или браузер перезапустит его.

  • Жизненный цикл вызываемого API тоже отличается

Мы можем использовать Service Worker для кэширования, использовать js для перехвата HTTP-запросов браузера и устанавливать кэшированные файлы для создания автономных веб-приложений.Введение в концепцию Service Worker находится здесь~~, если вам интересно, вы можете найти соответствующую статью. , если у вас есть какие-либо вопросы, добро пожаловать в личное сообщение, чтобы обсудить со мной~, здесь мы в основном представляем использование метода postMessage для связи между Service Worker и страницами.

Отправить информацию со страницы в Service Worker

Следует отметить, что если эту страницу кинуть прямо в браузер (по файловому протоколу), то будет выдано сообщение об ошибке.Вам нужно использовать nginx для сопоставления портов, либо использовать node для сборки сервера (по протоколу http) для получить доступ к странице (в настоящее время причина, которую я предполагаю, заключается в том, что браузер накладывает некоторые служебные ограничения на файлы, открываемые файловым протоколом. Если кто-то знает конкретную причину, сообщите мне). Конфигурация моего nginx для сопоставления портов также будет прилагается ниже.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Service Worker跨窗口通信</title>
</head>
<body>
    <textarea id="showArea"></textarea>
    <script src="sw1.js"></script> 
    <script src="sw2.js"></script> 
    <script type="text/JavaScript">
        if('serviceWorker' in window.navigator) {
            // 对于多个不同scope的多个Service Worker,我们也可以给指定的Service Worker发送消息
            navigator.serviceWorker.register('./sw1.js', { scope:'./sw1'})
                .then(function(reg) {
                    console.log('success', reg);
                    return new Promise((resolve, reject) => {
                        const interval = setInterval(function() {
                            if(reg.active) {
                                clearInterval(interval);
                                resolve(reg.active);
                            }    
                        }, 1000);
                    }).then(sw => {
                        sw.postMessage("this message is from page to sw1");
                    })
                    
                })
            navigator.serviceWorker.register('./sw2.js', { scope:'./sw2'})
                .then(function(reg) {
                    console.log('success', reg);
                    return new Promise((resolve, reject) => {
                        const interval = setInterval(function() {
                            if(reg.active) {
                                clearInterval(interval);
                                resolve(reg.active);
                            }    
                        }, 1000);
                    }).then(sw => {
                        sw.postMessage("this message is from page to sw2");

                    })
                    
                });
                navigator.serviceWorker.addEventListener('message', function (event) {
                    console.log(event.data);
                    // 接受数据,并填充在 DOM 中
                    document.getElementById('showArea').value = event.data ;
                });
        }
    
    </script>
</body>
</html>

// sw1.js
self.addEventListener("message", function(event) {
    console.log("sw1.js " + event.data);
    event.source.postMessage('this message is from sw1.js, to page');
});

// sw2.js

self.addEventListener("message", function(event) {    
    console.log("sw2.js " + event.data); 
     // event.source是消息来源页面对象的引用   
    event.source.postMessage('this message is from sw2.js, to page');
});

Связанная конфигурация для nginx для сопоставления портов:

// nginx.conf
// 因为有多个项目会用到nginx服务做端口映射
// 所以我在nginx的目录下新建了conf.d的文件来存放每个项目的配置.
// 然后在主配置文件里通过include引入

 http {    
    # 这里省略了一些你本机电脑上的nginx服务的配置    
    include conf.d/*.conf; 
}


// testHtml.conf
server {    
    listen 9090;    
    server_name       localhost;
    location / {        
        root  C:/Users/hzljie/Desktop/test/testb;  
        # 这是我的测试页面的存放路径,读者用的时候记得根据自己的来更改奥        
        index  test.html test.htm;    
    }
}

Скриншот эффекта в действии:

Таким образом реализуется связь между Service Worker и другими страницами.Заинтересованные партнеры могут попробовать это вместе.Если вы получите ошибку, внимательно проверьте свой код~

Вывод

Что ж, для окончательного подведения итогов потребовалось много труда, но статья может быть недостаточно подробной.Ведь у технологии будет много расширений и ответвлений, и внедрять их по одному сложно.Мой уровень ведения блога еще нужен быть улучшенным, и вы можете изучить это.Или партнеры, которые заинтересованы или нашли неправильное место в статье, свяжитесь со мной и добьйтесь прогресса вместе~~~.Мой почтовый ящик2510909248@qq.com.

Прошлые статьи

 Использование SockJS для реализации связи через webSocket в vue

Научите, как сделать приостановку заголовка таблицы (фиксированный заголовок таблицы)

Expires, Last-Modified, механизм кэширования Etag