탭 또는 창 사이의 통신


176

추적을 남기지 않고 브라우저 (COS가 아닌 동일한 도메인의)에서 여러 탭 또는 창간에 통신하는 방법을 찾고있었습니다. 몇 가지 해결책이있었습니다.

  1. 창 개체 사용
  2. postMessage
  3. 쿠키
  4. localStorage

첫 번째는 아마도 최악의 해결책 일 것입니다-현재 창에서 창을 열어야하며 창을 열어 둔 상태에서만 통신 할 수 있습니다. 임의의 창에서 페이지를 다시로드하면 통신이 끊어졌을 가능성이 큽니다.

postMessage를 사용하는 두 번째 접근법은 아마도 원본 간 통신을 가능하게하지만 첫 번째 접근법과 동일한 문제를 겪습니다. 창 개체를 유지해야합니다.

세 번째 방법은 쿠키를 사용하여 브라우저에 일부 데이터를 저장하는 것입니다. 동일한 도메인의 모든 창에 메시지를 보내는 것처럼 보일 수 있지만 문제는 모든 탭에서 이미 "메시지"를 읽었는지 여부를 알 수 없다는 것입니다 청소. 쿠키를 주기적으로 읽으려면 일종의 시간 초과를 구현해야합니다. 또한 최대 쿠키 길이 (4KB)로 제한됩니다.

localStorage를 사용하는 네 번째 솔루션은 쿠키의 한계를 극복하는 것처럼 보였으며 이벤트를 사용하여들을 수도 있습니다. 사용 방법은 허용 된 답변에 설명되어 있습니다.

2018 편집 : 허용 된 답변은 여전히 ​​작동하지만 최신 브라우저에는 BroadcastChannel을 사용하는 최신 솔루션이 있습니다. BroadcastChannel을 사용하여 탭간에 메시지를 쉽게 전송하는 방법을 설명하는 간단한 예는 다른 답변을 참조하십시오.


4
같은 질문이 몇 년 동안 열려있을 때 왜이 질문이 "너무 광범위"로 닫혔습니까? JavaScript를 사용하여 열려있는 모든 창 / 탭에 메시지 보내기 , stackoverflow.com/questions/2236828/… , 두 개의 브라우저 탭 / 창간어떻게 통신합니까? 그리고 몇 가지 더.
Dan Dascalescu

클라이언트 측 데이터 스토리지를 관리하기 위해 localStorage 및 sessionStorage를 통해 라이브러리를 작성했습니다. storageManager.savePermanentData ( 'data', 'key')와 같은 작업을 수행 할 수 있습니다. 또는 storageManager.saveSyncedSessionData ( 'data', 'key'); 데이터의 작동 방식에 따라 실제로 프로세스를 단순화합니다. 전체 기사는 여기 : ebenmonney.com/blog/…
adentum


2
몇 년 전에 라이브러리 sysend.js를 만들었 으며 최신 버전에서는 BroadcastChannel을 사용합니다. jcubic.pl/sysend.php 이 페이지를 두 번 열어 라이브러리를 테스트 할 수 있습니다 . iframe 프록시를 제공하면 다른 출처에서도 작동합니다.
jcubic

하위 도메인을 동일한 출처로 간주합니까? 내 도메인이 3 개 미만인데 브로드 캐스트 채널 API를 통해 통신합니까? alpha.firstdomain.com, beta.firstdomain.com, gama.firstdomain.com
Tejas Patel

답변:


142

2018 편집 :이 목적으로 BroadcastChannel을 더 잘 사용할 수 있습니다. 아래 다른 답변을 참조하십시오. 그러나 탭 간 통신에 여전히 로컬 저장소를 사용하려면 다음과 같이하십시오.

탭이 다른 탭으로 메시지를 보낼 때 알림을 받으려면 'storage'이벤트를 바인딩하면됩니다. 모든 탭에서 다음을 수행하십시오.

$(window).on('storage', message_receive);

message_receive다른 탭에서 localStorage 값을 설정할 때마다이 함수 가 호출됩니다. 이벤트 리스너에는 localStorage로 새로 설정된 데이터도 포함되므로 localStorage 객체 자체를 구문 분석 할 필요조차 없습니다. 트레이스를 효과적으로 정리하기 위해 설정 직후에 값을 재설정 할 수 있기 때문에 매우 편리합니다. 메시징 기능은 다음과 같습니다.

// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
    localStorage.setItem('message',JSON.stringify(message));
    localStorage.removeItem('message');
}


// receive message
//
function message_receive(ev)
{
    if (ev.originalEvent.key!='message') return; // ignore other keys
    var message=JSON.parse(ev.originalEvent.newValue);
    if (!message) return; // ignore empty msg or msg reset

    // here you act on messages.
    // you can send objects like { 'command': 'doit', 'data': 'abcd' }
    if (message.command == 'doit') alert(message.data);

    // etc.
}

이제 탭이 onstorage 이벤트에서 바인드되고이 두 기능이 구현되면 다음과 같이 다른 탭 호출로 메시지를 브로드 캐스트 할 수 있습니다.

message_broadcast({'command':'reset'})

정확히 동일한 메시지를 두 번 보내는 것은 한 번만 전파되므로 메시지를 반복해야하는 경우 다음과 같은 고유 식별자를 추가하십시오.

message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})

또한 메시지를 브로드 캐스트하는 현재 탭은 실제로 메시지를 수신하지 않고 동일한 도메인의 다른 탭이나 창만 수신한다는 점을 기억하십시오.

removeItem () 전에 setItem () 호출 직후에 사용자가 다른 웹 페이지를로드하거나 탭을 닫으면 어떻게되는지 묻습니다. 글쎄, 내 자신의 테스트에서 브라우저는 전체 기능 message_broadcast()이 완료 될 때까지 언로드를 보류합니다 . 나는 매우 긴 for () 사이클을 넣는 것을 테스트했지만 사이클이 끝나기 전에 기다렸다가 닫습니다. 사용자가 그 사이에서 탭을 죽이면 브라우저에 메시지를 디스크에 저장할 시간이 충분하지 않으므로이 방법은 흔적없이 메시지를 보내는 안전한 방법과 같습니다. 의견 환영합니다.


1
JSON.parse ()를 호출하기 전에 remove 이벤트를 무시할 수 있습니까?
dandavis

1
기존 localStorage 데이터를 포함하여 이벤트 데이터 제한을 명심하십시오. 배송 대신 메시징을 위해 스토리지 이벤트를 사용하는 것이 좋습니다. 우체국에서 패키지를 수령하라는 엽서를받을 때와 같이 로컬 스토리지는 하드 드라이브로 이동하므로 실수로 캐시를 남기고 로그에 영향을 줄 수 있습니다. 실제 데이터.
dandavis

1
나는 danml.com/js/localstorageevents.js 와 관련 이 있습니다. 이벤트 에미 터 기반과 "로컬 에코"가있어 모든 곳에서 EE를 사용할 수 있습니다.
dandavis

7
Safari는 BroadcastChannel를 지원하지 않습니다 - caniuse.com/#feat=broadcastchannel
스리 칸스

1
브라우저를 통해 더 나은 지원을 제공하는 developer.mozilla.org/en-US/docs/Web/API/SharedWorker와 같은 공유 작업자를 사용할 수도 있습니다.
Seblor

116

이 목적을 위해 현대적인 API가 있습니다- 방송 채널

다음과 같이 쉽습니다.

var bc = new BroadcastChannel('test_channel');

bc.postMessage('This is a test message.'); /* send */

bc.onmessage = function (ev) { console.log(ev); } /* receive */

메시지가 단지 DOMString 일 필요는 없으며, 모든 종류의 객체가 전송 될 수 있습니다.

API 정리와는 별도로이 API의 주요 이점은 개체 문자열 화가 아니라는 것입니다.

현재 Chrome 및 Firefox에서만 지원 되지만 localStorage를 사용하는 폴리 필을 찾을 수 있습니다.


3
잠깐, 메시지의 출처를 어떻게 알 수 있습니까? 동일한 탭에서 오는 메시지를 무시합니까?
AturSams

2
@zehelvion : 예를 들어이 멋진 개요 에 따르면 발신자가 수신하지 못합니다 . 또한 원하는 것을 포함하여 메시지에 메시지를 넣을 수 있습니다. 필요한 경우 발신자의 일부 ID
Sz.

7
크로스 브라우저 라이브러리에서이 기능을 마무리하는 멋진 프로젝트가 있습니다 : github.com/pubkey/broadcast-channel
james2doyle

이 API 지원이 해당 브라우저에 포함 될지 여부에 대한 Safari의 공개 신호가 있습니까?
Casey

@AturSams MessageEvent.origin, MessageEvent.source 또는 MessageEvent.ports가 원하는지 확인하십시오. 항상 문서가 시작하기에 가장 좋은 곳입니다. developer.mozilla.org/en-US/docs/Web/API/MessageEvent
Stefan Mihai Stanescu

40

jQuery를 기반으로하지 않는 솔루션을 검색하려는 경우 Thomas M에서 제공하는 솔루션의 일반 JavaScript 버전입니다.

window.addEventListener("storage", message_receive);

function message_broadcast(message) {
    localStorage.setItem('message',JSON.stringify(message));
}

function message_receive(ev) {
    if (ev.key == 'message') {
        var message=JSON.parse(ev.newValue);
    }
}

1
removeItem 호출을 생략 한 이유는 무엇입니까?
Tomas M

2
jQuery와 JavaScript의 차이점에 중점을 두었습니다.
Nacho Coloma

나는 항상 polyfill과 지원되지 않는 기능 때문에 lib를 사용합니다!
아민 라 히미

20

체크 아웃 AcrossTabs - 출처 간 브라우저 탭 사이의 쉬운 통신. postMessagesessionStorage API 의 조합을 사용하여 통신을 훨씬 쉽고 안정적으로 만듭니다.


서로 다른 접근 방식이 있으며 각각 고유 한 장단점이 있습니다. 각각에 대해 토론 해 봅시다.

  1. 로컬 스토리지

    찬성 :

    1. 웹 스토리지는 쿠키의 개선으로 단순하게 볼 수있어 훨씬 더 큰 스토리지 용량을 제공합니다. Mozilla 소스 코드를 보면 5120KB ( Chrome의 250 만 자 에 해당 하는 5MB )가 전체 도메인의 기본 저장소 크기 임을 알 수 있습니다 . 따라서 일반적인 4KB 쿠키보다 훨씬 더 많은 공간을 사용할 수 있습니다.
    2. 모든 HTTP 요청 (HTML, 이미지, JavaScript, CSS 등)마다 데이터가 서버로 다시 전송되지 않으므로 클라이언트와 서버 간의 트래픽 양이 줄어 듭니다.
    3. localStorage에 저장된 데이터는 명시 적으로 삭제 될 때까지 지속됩니다. 변경 사항은 저장되어 현재 및 향후 사이트 방문시 사용할 수 있습니다.

    단점 :

    1. 동일 출처 정책 에서 작동합니다 . 따라서 저장된 데이터는 동일한 출처에서만 사용할 수 있습니다.
  2. 쿠키

    장점 :

    1. 다른 것에 비해 AFAIK는 없습니다.

    단점 :

    1. 4K 제한은 이름, 값, 만료 날짜 등을 포함한 전체 쿠키에 적용됩니다. 대부분의 브라우저를 지원하려면 이름을 4000 바이트 이하로 유지하고 전체 쿠키 크기를 4093 바이트 이하로 유지하십시오.
    2. 모든 HTTP 요청 (HTML, 이미지, JavaScript, CSS 등)에 대해 데이터가 서버로 다시 전송되어 클라이언트와 서버 간의 트래픽 양이 증가합니다.

      일반적으로 다음이 허용됩니다.

      • 300 개의 쿠키
      • 쿠키 당 4096 바이트
      • 도메인 당 쿠키 20
      • 도메인 당 81920 바이트 (최대 크기 4096 = 81920 바이트의 쿠키 20 개 제공)
  3. sessionStorage

    장점 :

    1. 와 비슷합니다 localStorage.
    2. 창 또는 Chrome 및 Firefox와 같은 브라우저의 탭에서만 변경 사항을 사용할 수 있습니다. 변경 사항은 현재 페이지와 동일한 창에서 나중에 사이트를 방문 할 때 저장 및 사용 가능합니다. 창이 닫히면 스토리지가 삭제됩니다

    단점 :

    1. 데이터는 설정된 창 / 탭에서만 사용할 수 있습니다.
    2. 데이터는 영구적이지 않습니다. 즉, 창 / 탭을 닫으면 손실됩니다.
    3. 마찬가지로 localStoragett는 동일한 출처 정책 에서 작동합니다 . 따라서 저장된 데이터는 동일한 출처에서만 사용할 수 있습니다.
  4. PostMessage

    장점 :

    1. 교차 출처 통신을 안전하게 활성화 합니다.
    2. 데이터 포인트로서, WebKit 구현 (Safari 및 Chrome에서 사용)은 현재 메모리 부족으로 인한 제한 이외의 제한을 적용하지 않습니다.

    단점 :

    1. 현재 창에서 창을 열어야하며 창을 열어 둔 상태에서만 통신 할 수 있습니다.
    2. 보안 관련 문제 -postMessage를 통해 문자열을 보내는 것은 다른 JavaScript 플러그인에서 게시 한 다른 postMessage 이벤트를 선택하므로targetOrigin메시지 리스너에 전달되는 데이터에 대한 무결성 검사를 구현해야합니다.
  5. PostMessage + SessionStorage 의 조합

    postMessage를 사용하여 여러 탭간에 통신하고 동시에 새로 열린 모든 탭 / 창에서 sessionStorage를 사용하여 전달되는 데이터를 유지합니다. 탭 / 창이 열려있는 한 데이터가 유지됩니다. 따라서 오프너 탭 / 창이 닫히더라도 열린 탭 / 창은 새로 고친 후에도 전체 데이터를 갖습니다.

나는 postMessage API를 사용하여 교차 출처 탭 / 창과 sessionStorage간에 통신하여 열린 탭 / 창 ID가 지속되는 한 지속성유지 하는 AcrossTabs 라는 JavaScript 라이브러리를 작성했습니다 .


를 사용하면 AcrossTabs다른 탭에서 다른 웹 사이트를 열고 해당 데이터를 부모 탭으로 가져올 수 있습니까? 다른 웹 사이트에 대한 인증 정보가 있습니다.
Madhur Bhaiya

1
예, @MadhurBhaiya
softvar

쿠키의 가장 큰 장점은 교차 도메인 동일한 도메인을 허용한다는 것입니다. 이는 "a.target.com", "b.target.com"등과 같은 원본을 가질 때 일반적으로 유용합니다.
StarPinkER

7

사람들이 고려해야 할 또 다른 방법은 공유 작업자입니다. 나는 그것이 최첨단 개념이라는 것을 알고 있지만, 로컬 스토리지보다 훨씬 빠르며 같은 출처에있는 한 부모 / 자식 창 사이의 관계가 필요하지 않은 공유 작업자에 릴레이를 만들 수 있습니다.

이에 대한 토론은 여기 에서 내 대답을 참조하십시오 .


7

을 기반으로 동일한 출처 (면책 조항-기고자 중 하나입니다!)의 탭 / 창간 동기화 / 통신을위한 작은 오픈 소스 구성 요소가 있습니다 localStorage.

TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);

TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
    DoSomething();
});

TabUtils.CallOnce("lockname", function () {
    alert("I run only once across multiple tabs");
});

https://github.com/jitbit/TabUtils

추신 : 이벤트가 거의 동시에 일어날 때 대부분의 "잠금 / 뮤텍스 / 동기화"구성 요소가 웹 소켓 연결에서 실패하기 때문에 여기에서 권장하는 자유를 얻었습니다.


6

sysend.js 라이브러리를 만들었습니다. 매우 작습니다. 소스 코드를 확인할 수 있습니다. 라이브러리에는 외부 종속성이 없습니다.

동일한 브라우저와 도메인의 탭 / 창 간 통신에 사용할 수 있습니다. 라이브러리는 지원되는 경우 BroadcastChannel 또는 localStorage의 스토리지 이벤트를 사용합니다.

API는 매우 간단합니다.

sysend.on('foo', function(message) {
    console.log(message);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo'); // empty notification

브라우저가 BroadcastChannel을 지원할 때 리터럴 객체를 보냈지 만 (실제로는 브라우저에 의해 자동 직렬화 됨) 그렇지 않은 경우 JSON으로 직렬화되고 다른 쪽에서는 직렬화 해제됩니다.

최신 버전에는 도메인 간 통신을위한 프록시를 만드는 도우미 API도 있습니다. (대상 도메인에 단일 html 파일이 필요합니다).

여기 데모가 있습니다.

편집 :

대상 도메인에 특수 파일을 포함 하고 소스 도메인에서 함수를 호출하는 경우 새 버전은 도메인 간 통신 도 지원 합니다.proxy.htmlproxy

sysend.proxy('https://target.com');

(proxy.html 매우 간단한 html 파일이며 라이브러리에 하나의 스크립트 태그 만 있습니다).

양방향 통신을 원하는 경우 target.com도메인 에서 동일한 작업을 수행해야 합니다.

참고 : localStorage를 사용하여 동일한 기능을 구현하면 IE에 문제가 있습니다. 스토리지 이벤트가 동일한 창으로 전송되어 이벤트가 트리거되었으며 다른 브라우저의 경우 다른 탭 / 창에 대해서만 호출됩니다.


2
이것에 대한 몇 가지 kudo를주고 싶었습니다. 간단하고 멋진 탭을 추가하여 로그 아웃 경고 소프트웨어가 사람들을 쫓아 가지 않도록 막을 수 있습니다. 좋은 작업. 간단한 메시징 솔루션을 원한다면 이것을 강력히 추천합니다.
BrownPony

4

공식 방송 채널 과 동일하게 작동 하지만 로컬 스토리지, indexeddb 및 유닉스 소켓을 기반으로 폴 백이 있는 모듈을 만들었습니다 . 이를 통해 Webworkers 또는 NodeJS에서도 항상 작동합니다. pubkey : BroadcastChannel 참조


1

나는 내 블로그에 이것에 관한 기사를 썼습니다 : http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- 웹 애플리케이션 .

내가 만든 라이브러리를 사용하면 storageManager다음과 같이 이것을 달성 할 수 있습니다.

storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data

다른 시나리오도 처리 할 수있는 다른 편리한 방법이 있습니다


0

이것은 Chrome 용 Tomas M answer 의 개발 storage부분입니다 . 리스너를 추가해야합니다

window.addEventListener("storage", (e)=> { console.log(e) } );

저장소에 항목을로드 / 저장하면이 이벤트가 발생하지 않습니다. 수동으로 트리거해야합니다.

window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME

이제 열려있는 모든 탭에서 이벤트가 수신됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.