Как много вы знаете о методах коммуникации JS? JS и многотерминальное взаимодействие приложений

JavaScript

общие методы связи

postMessage

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

  1. otherWindow
    • Ссылка на другое окно, напримерiframeизcontentWindowсвойства, исполнениеwindow.openвозвращаемый объект окна, именованный или пронумерованный в числовом видеwindow.frames.
  2. message
    • будут отправлены другимwindowДанные. Он будет сериализован алгоритмом структурированного клонирования. Это означает, что вы можете безопасно передавать объекты данных в целевое окно без каких-либо ограничений, не сериализуя их самостоятельно.
  3. targetOrigin
    • через окноoriginсвойство, чтобы указать, какие окна могут получать сообщения о событиях, значение которых может быть строкой "*" (на неограниченный срок) илиURI. При отправке сообщения, если какой-либо протокол целевого окна, адрес хоста или порт не совпадаютtargetOriginЕсли указано значение, сообщение не будет отправлено; только если все три точно совпадают, сообщение не будет отправлено. Этот механизм используется для управления тем, на какие сообщения Windows можно отправлять сообщения; например, при использованииpostMessageПри передаче пароля этот параметр особенно важен, и его значение должно гарантированно совпадать со значением предполагаемого получателя сообщения, содержащего пароль.originАтрибуты точно такие же, чтобы предотвратить перехват паролей злоумышленниками. Если вы точно знаете, в какое окно должно быть отправлено сообщение, всегда указывайтеtargetOrigin, вместо*. Отсутствие точной цели приведет к утечке данных на любой вредоносный сайт, заинтересованный в данных.
  4. transferнеобязательный
    • представляет собой строку из иmessageпередается одновременноTransferableОбъекты. Право собственности на эти объекты будет передано получателю сообщения, а отправитель больше не сохранит право собственности.
  • использовать
 window.parent.postMessage({
    close: true,
    type: 'shuaka',
    result:msg.result,
    errmeesage:msg.meesage,
    cardMap:msg.map
    }, 'http://192.168.60.104:3010/'
);
window.addEventListener('message', e => {
   console.log(e)
	var d = e.data;  //e.data  里面有自己所传的所有参数  可以根据参数做自己的判断
});
  • postMessageПередача сообщений перед межоконной и междоменной связью может быть разрешена.
  • Уведомление
    • Если вы не хотите получать с других сайтовmessage, пожалуйста, не надоmessageevent для добавления прослушивателей событий.
    • когда вы используетеpostMessageВсегда указывайте точную цель при отправке данных в другие окнаorigin, вместо*.
    • не могу проверитьoriginа такжеsourceАтрибут может привести к атакам межсайтового скриптинга.

Ajax

  • Ajax - Asynchronous JavaScript and XML(АсинхронныйJavaScriptа такжеXML), который можно использовать для связи с сервером для получения данных.
let xmlhttp;
if (window.XMLHttpRequest) {
	//  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
	xmlhttp=new XMLHttpRequest();
} else {
	// IE6, IE5 浏览器执行代码
	xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
	if (xmlhttp.readyState === 4 && xmlhttp.status === 200)	{
		document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
	}
}
xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);
xmlhttp.send();

WebSocket

  • Веб-сокет в js
  • WebSocketдаhtml5предоставляется в одномTCPПротокол двусторонней связи по соединению решает проблему связи в реальном времени между клиентом и сервером. Браузеру и серверу нужно только выполнить рукопожатие, а постоянныйTCPсоединение, после чего сервер и клиент проходят этотTCPсоединение для двусторонней связи в режиме реального времени.
  1. преимущество
    • Чтобы добиться передачи данных, многие веб-сайты используют такие технологии, какajaxопрос. Опрос — это запрос, инициированный браузером через определенный интервал времени, чтобы получить данные с сервера. Опрос требует непрерывной отправки запросов на сервер, что требует большой пропускной способности и ресурсов сервера.WebSocketУчреждатьTCPПосле подключения сервер может активно передавать данные клиенту, что может лучше сэкономить ресурсы сервера и пропускную способность и обеспечить передачу данных в реальном времени.
    function webSocket(){
        if("WebSocket" in window) {
            console.log("您的浏览器支持WebSocket");
            var ws = new WebSocket("ws://localhost:8080"); //创建WebSocket连接
            //...
        } else { 
            console.log("您的浏览器不支持WebSocket");
        }
    }
    
  2. использовать
    • Поддержка клиентовWebSocket, при созданииsocketПосле этого можно пройтиonopen,onmessage,oncloseа такжеonerrorчетыре пары событийsocketреагировать. Все операции WebSocket запускаются событиями, которые не блокируютсяUI,ДаUIЧем быстрее время отклика, тем лучше пользовательский опыт.
    • браузер черезJavaScriptВыпустить сборку на серверWebSocketПосле установления соединения клиент и сервер могут напрямую обмениваться данными через TCP-соединение. когда вы получаетеWebSocketПосле подключения вы сможетеsend()метод отправки данных на сервер, который может бытьonmessageСобытия получают данные, возвращаемые сервером.
    var ws = new WebSocket("ws://localhost:8080"); 
    //申请一个WebSocket对象,参数是服务端地址,同http协议使用http://开头一样,WebSocket协议的url使用ws://开头,另外安全的WebSocket协议使用wss://开头
    ws.onopen = function(){
        //当WebSocket创建成功时,触发onopen事件
        console.log("open");
        ws.send("hello"); //将消息发送到服务端
    }
    ws.onmessage = function(e){
        //当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据
        console.log(e.data);
    }
    ws.onclose = function(e){
        //当客户端收到服务端发送的关闭连接请求时,触发onclose事件
        console.log("close");
    }
    ws.onerror = function(e){
        //如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
        console.log(error);
    }
    

Сцены

  • Поскольку социальный продукт внутри компании нуждается в разработке новой функции (блокнот), общая функция заключается в том, что пользователи могут создавать некоторые заметки в своих учетных записях, а формат записи заметокmarkdownграмматика. Над областью редактирования есть еще одинtoolbar, аналогичный приведенному выше редактору форматированного текста. Дело в том, что продукт имеет несколько концов, включаяiOS,AndroidИ клиент, экспорт и загрузка редактора блокнота и т. д. должны вызывать метод соответствующего конца, поэтому необходимо поддерживать единую связь с каждым концом.

решение

Вариант 1: режим публикации-подписки

  • Каждый конец монтирует измененный объект к глобальному объекту приложения, реализуя промежуточный уровень публикации и подписки, а редактор и клиент взаимодействуют с помощью методов публикации и подписки.
  • ТС реализуется следующим образом:
// communite.ts
class Event {
  public handlers: any = {}; // 事件容器,用来装事件数组(因为订阅者可以是多个)
  public note: object = { // 写死的note属性
    id: 1234,
    language: "en-US",
    themeColor: "red",
  };

  // 事件添加方法,参数有事件名和事件方法
  public addEvent(type: string, handler: (...param: any[]) => void) {
    // 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器
    if (!(type in this.handlers)) {
      this.handlers[type] = [];
    }
    // 将事件存入/替换
    this.handlers[type][0] = handler;
    // this.handlers[type].push(handler); // 同一事件名多个回调函数
  }

  // 触发事件两个参数(事件名,参数)
  public dispatchEvent(type: string, params?: any[], callback?: (...param: any) => void) {
    // 若没有注册该事件则抛出错误
    if (!(type in this.handlers)) {
      return new Error("未注册该事件");
    }
    // tslint:disable-next-line:no-console
    console.log("dispatchEvent params", type, params);
    // tslint:disable-next-line:whitespace
    switch(type) {
      case "touchCharSize" :
        callback ? callback() : this.handlers[type][0]();
        break;
      case "charSize":
        // tslint:disable-next-line:no-console
        console.log(params);
        // callback ? callback(content) : this.handlers[type][0](content);
        break;
      case "upload":
        const fileObject = {
          filename: "test",
          id: "123456",
          size: 1024,
          type: "pdf",
          url: "/sdad/dsads/test.pdf",
        };
        callback ? callback(fileObject) : this.handlers[type][0](fileObject);
        break;
      default:
        break;
    }
    // 触发
    // this.handlers[type].forEach((handler: (...params: any[]) => void) => {
    //   handler(...params)
    // })
  }

  // 事件移除参数(事件名,删除的事件,同一事件多个回调函数若无第二个参数则删除该事件的订阅和发布)
  public removeEventListener(type: string, handler?: () => void) {
      // 无效事件抛出
      if (!(type in this.handlers)) {
        return new Error("无效事件");
      }
      if (!handler) {
        // 直接移除事件
        delete this.handlers[type];
      } else {
        const idx = this.handlers[type].findIndex((ele: () => void) => ele === handler);
        // 抛出异常事件
        if (idx === -1) {
          return new Error("无该绑定事件");
        }
        // 移除事件
        this.handlers[type].splice(idx, 1);
        if (this.handlers[type].length === 0) {
          delete this.handlers[type];
        }
      }
    }
}

export default new Event();
  • Когда клиенту нужно получить некоторые данные, клиент запускает (dispatchEvent) Соответствующее имя события достигает среднего уровня, средний уровень выполняет функцию обратного вызова соответствующего события в редакторе, запускает новое событие в функции обратного вызова, отправляет данные клиенту, а клиент прослушивает соответствующее событие в средний слой, чтобы иметь дело с результатом. Ниже приведен пример получения текущего редактируемого содержимого записной книжки:
// conmmunite.ts 中间层已实现对应的touchCharSize和charSize事件处理
// 客户端触发事件:touchCharSize
// 笔记本编辑器添加:touchCharSize,并执行回调触发:charSize

import Event from './communite.ts'
// 获取笔记字数
Event.addEventListener('touchCharSize', () => {
  Event.dispatchEvent('charSize', {
    total: 46000, // 总字数
    current: 12233 // 当前字数
  });
})
Event.dispatchEvent('touchCharSize'); // 客户端触发获取字数事件

// 获取笔记字数
Event.addEventListener('charSize', () => {
  console.log('charSize');
})
  • Поскольку сбор количества слов активно инициируется клиентом, для завершения всего процесса связи нам нужны два события.Клиент получает данные в событиях, инициированных редактором блокнота.

Вариант 2: Реализуется индивидуально

  • В режиме публикации-подписки нам нужен промежуточный уровень для хранения наших событий, а связь между двумя концами более сложная, поэтому существует вторая схема реализации.
  • Поскольку приложение может выполнять в приложенииJavaScriptметод, поэтому, когда клиент получает некоторую информацию в редакторе записной книжки, он может выполнитьJavaScriptМетоды.
// 类似安卓中的实现
WebView webView;
Button buttonLeft;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main4);
    webView=findViewById(R.id.webview);
    buttonLeft = findViewById(R.id.btnLeft);

    WebSettings webSettings=webView.getSettings();
    webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);//缓存
    webSettings.setJavaScriptEnabled(true);
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//允许弹窗
    webView.getSettings().setDomStorageEnabled(true);
    webView.loadUrl("file:///android_asset/evaluate.html");
    buttonLeft.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            webView.post(new Runnable() {
                @RequiresApi(api = Build.VERSION_CODES.KITKAT)
                @Override
                public void run() {
                   webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                   @Override
                   public void onReceiveValue(String s) {
                       buttonLeft.setText(s);
                    }
                  });
                  // 执行JavaScript函数
                  webView.loadUrl("javascript:detanx()");
                }
            });
        }
    });
    webView.setWebChromeClient(new WebChromeClient(){
        @Override
        public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
            AlertDialog.Builder builder=new AlertDialog.Builder(Main4Activity.this);
            builder.setTitle("alert1");
            builder.setMessage(message);
            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            });
            builder.setCancelable(false);
            builder.create().show();
            return true;
        }
    });
}
  • существуетindex.htmlДобавьте соответствующий метод в .
// index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
  <title></title>
</head>
<body>
  <div id="detanx"></div>
  <script>
    function detanx() {
      document.getElementById('detanx').innerHTML = 'detanx';
    }
  </script>
</body>
</html>
  • вызов приложенияJavaScriptОн реализуется, а затем приложение монтирует объект глобально (Event), объект содержит некоторые методы, мы передаемEventЭтот объект, вJavaScriptДля выполнения метода соответствующих событий мы передаемJavaScriptЧтобы реализовать унифицированную функцию обратного вызова, после запуска соответствующего события в приложении приложение возвращает соответствующий результат, выполняя унифицированную функцию обратного вызова. В функции обратного вызова мы используем уникальный идентификатор для представления каждого инициированного события. функция после обратного вызова соответствующего события выполняется позже. Вот пример:
// 缓存
const list = {}
// 触发方法并缓存
function dispatchEvent() {
  // 生成唯一标识
  const markid = new Date().getTime().toString();
  list.markid = setContent;
  Event.getContent(noteid, markid);
}
// 设置内容执行函数
function setContent(result) {
  const { content } = result
  console.log(result);
}
// 应用处理完成统一回调函数
function commonCallbak (markid, result) {
  if(list.markid) {
    list.markid(result);
    // 移除缓存
    delete list.markid;
  } else {
    throw new Error('markid is not exist.')
  }
}
  • Эта реализация может сохранить реализацию среднего уровня, но предпосылка состоит в том, что несколько терминалов могут поддерживать этот тип вызова.JavaScriptПуть.

Суммировать

  • Существует множество способов общения, таких какwebsocket,Ajax,postMessage, режим публикации-подписки и т. д. Технические решения, которые мы используем, безусловно, различаются в зависимости от разных конкретных бизнес-сценариев. Нет лучшего решения, есть только самое подходящее. У нас достаточно преимуществ и недостатков каждого решения. Только поняв, вы знать, какое решение в настоящее время является лучшим.