Sekmeler veya pencereler arasındaki iletişim


176

İz bırakmadan bir tarayıcıda (aynı etki alanında, CORS değil) birden çok sekme veya pencere arasında iletişim kurmanın bir yolunu arıyordum. Birkaç çözüm vardı:

  1. pencere nesnesini kullanma
  2. postMessage
  3. kurabiye
  4. yerel depolama

Birincisi muhtemelen en kötü çözümdür - mevcut pencerenizden bir pencere açmanız gerekir ve daha sonra yalnızca pencereleri açık tuttuğunuz sürece iletişim kurabilirsiniz. Sayfayı pencerelerden herhangi birine yeniden yüklerseniz, büyük olasılıkla iletişimi kaybettiniz.

PostMessage kullanan ikinci yaklaşım, muhtemelen çapraz kökenli iletişimi mümkün kılar, ancak ilk yaklaşımla aynı sorunu yaşar. Bir pencere nesnesini korumanız gerekir.

Üçüncü olarak, çerezleri kullanarak, tarayıcıdaki bazı verileri saklayın, bu da aynı etki alanındaki tüm pencerelere bir mesaj göndermek gibi görünebilir, ancak sorun, tüm sekmelerin daha önce "mesajı" okuyup okumadığını asla bilemeyeceğinizdir. temizlemek. Çerezi periyodik olarak okumak için bir çeşit zaman aşımı uygulamanız gerekir. Ayrıca, 4KB olan maksimum çerez uzunluğu ile sınırlandırılırsınız.

LocalStorage kullanan dördüncü çözüm, çerezlerin sınırlamalarının üstesinden gelmiş gibi görünüyordu ve hatta olayları kullanarak dinlenebilir. Nasıl kullanılacağı kabul edilen cevapta açıklanmaktadır.

Edit 2018: Kabul edilen cevap hala çalışıyor, ancak modern tarayıcılar için BroadcastChannel'i kullanmak için daha yeni bir çözüm var. BroadcastChannel'i kullanarak mesajların sekmeler arasında nasıl kolayca iletileceğini açıklayan basit bir örnek için diğer cevaba bakınız.



İstemci tarafı veri depolamasını yönetmek için localStorage ve sessionStorage üzerinde bir kütüphane oluşturdum. StorageManager.savePermanentData ('data', 'key'); veya storageManager.saveSyncedSessionData ('veri', 'anahtar'); verilerinizin nasıl davranmasını istediğinize bağlı olarak. Süreci gerçekten basitleştirir. Makalenin tamamı burada: ebenmonney.com/blog/…
adentum


2
Birkaç yıl önce sysend.js kütüphanesini oluşturdum , en son sürümü BroadcastChannel kullanıyor. Bu sayfayı iki kez açarak kütüphaneyi test edebilirsiniz jcubic.pl/sysend.php , iframe proxy sağlarsanız farklı kaynaklarla da çalışır.
jcubic

Alt alan adını aynı başlangıç ​​noktası olarak mı görüyorum? Demek istediğim, üç alanın altında, yayın kanalı api yoluyla iletişim kuruyorlar mı? alpha.firstdomain.com, beta.firstdomain.com, gama.firstdomain.com
Tejas Patel

Yanıtlar:


142

Edit 2018: Bu amaçla BroadcastChannel'i daha iyi kullanabilirsiniz, aşağıdaki diğer cevaplara bakın. Yine de, sekmeler arasındaki iletişim için yerel depolama alanını kullanmayı tercih ediyorsanız, bunu şu şekilde yapın:

Bir sekme diğer sekmelere mesaj gönderdiğinde bildirim almak için 'depolama' etkinliğine bağlanmanız yeterlidir. Tüm sekmelerde şunu yapın:

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

Bu işlev message_receive, başka herhangi bir sekmede localStorage değerini her ayarladığınızda çağrılır. Olay dinleyicisi ayrıca localStorage'a yeni ayarlanmış verileri de içerir, bu nedenle localStorage nesnesinin kendisini ayrıştırmanıza bile gerek yoktur. Bu çok kullanışlıdır, çünkü herhangi bir izi etkili bir şekilde temizlemek için değeri ayarlandıktan hemen sonra sıfırlayabilirsiniz. Mesajlaşma işlevleri şunlardır:

// 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.
}

Şimdi sekmeleriniz ontorage olayına bağlandıktan sonra ve bu iki işlevi uyguladıktan sonra, yalnızca diğer sekmelere çağrı yapan bir mesaj yayınlayabilirsiniz, örneğin:

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

Aynı iletinin iki kez gönderilmesinin yalnızca bir kez yayılacağını unutmayın, bu nedenle iletileri tekrarlamanız gerekirse, onlara benzersiz bir tanımlayıcı ekleyin.

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

Ayrıca, iletiyi yayınlayan geçerli sekmenin gerçekten almadığını, yalnızca aynı etki alanındaki diğer sekmeleri veya pencereleri de unutmayın.

Kullanıcı, farklı bir web sayfası yüklerse veya sekmesini removeItem () öğesinden önce setItem () çağrısından hemen sonra kapatırsa ne olacağını sorabilirsiniz. Kendi testimden itibaren, tarayıcı tüm işlev bitene kadar boşaltma işlemini beklemeye alır message_broadcast(). () Döngüsü için çok uzun süre test ettim ve kapatmadan önce döngünün bitmesini bekledim. Kullanıcı sekmeyi sadece aralarında öldürürse, tarayıcı mesajı diske kaydetmek için yeterli zamana sahip olmaz, bu nedenle bu yaklaşım bana iz bırakmadan mesaj göndermenin güvenli bir yolu gibi görünüyor. Yorumlarınızı bekliyoruz.


1
JSON.parse () öğesini çağırmadan önce remove olayını yok sayabilir misiniz?
dandavis

1
önceden mevcut localStorage verileri de dahil olmak üzere olay verileri sınırını unutmayın. depolama olaylarını yalnızca gönderim yerine mesajlaşma için kullanmak daha iyi / daha güvenli olabilir. Sanki postaneden bir paket almanızı söyleyen bir kartpostal aldığınızda ... ayrıca, yerel depolama sabit sürücüye gider, bu nedenle yanlışlıkla önbellekleri bırakabilir ve günlükleri etkileyebilir; gerçek veriler.
dandavis

1
bir süre geri ilgili bir şey yaptım: danml.com/js/localstorageevents.js , bir olay yayıcı tabanı ve "yerel yankı" vardır, böylece her yerde her şey için EE kullanabilirsiniz.
dandavis

7
Safari BroadcastChannel'i desteklemiyor - caniuse.com/#feat=broadcastchannel
Srikanth

1
Sadece bir kafa kadar, paylaşılan çalışanları da kullanabilirsiniz: developer.mozilla.org/en-US/docs/Web/API/SharedWorker (tarayıcılarda daha iyi desteği olan)
Seblor

116

Bu amaca yönelik modern bir API var - Yayın Kanalı

Bu kadar kolay:

var bc = new BroadcastChannel('test_channel');

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

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

İletinin yalnızca bir DOMString olmasına gerek yoktur, her türlü nesne gönderilebilir.

Muhtemelen, API temizliğinden ayrı olarak, bu API'nın ana yararıdır - nesne dizilemesi yoktur.

Şu anda yalnızca Chrome ve Firefox'ta desteklenmektedir , ancak localStorage kullanan bir çoklu dolgu bulabilirsiniz.


3
Bekle, mesajın nereden geldiğini nereden biliyorsun? Aynı sekmeden gelen iletileri yok sayar mı?
AturSams

2
@zehelvion: Örneğin bu güzel genel bakışa göre gönderen bunu almayacak . Ayrıca, ne istersen mesajın içine koyabilirsiniz. gerekirse gönderenin bir kimliği.
Sz.

7
Bu özelliği burada bir çapraz tarayıcı kütüphanesinde tamamlayan
james2doyle

Safari'den, bu API için desteğin o tarayıcıya inip inmeyeceği konusunda herhangi bir genel sinyal var mı?
Casey

@AturSams MessageEvent.origin, MessageEvent.source veya MessageEvent.ports öğesinin istediğiniz gibi olup olmadığını denetlersiniz. Her zaman olduğu gibi, belgeler başlamak için en iyi yerdir: developer.mozilla.org/en-US/docs/Web/API/MessageEvent
Stefan Mihai Stanescu

40

JQuery tabanlı olmayan bir çözüm arayanlar için, bu Thomas M tarafından sağlanan çözümün düz bir JavaScript sürümüdür:

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 çağrısını neden atladınız?
Tomas M

2
Sadece jQuery ve JavaScript arasındaki farklara odaklanıyordum.
Nacho Coloma

Ben çok dolgu ve desteklenmeyen özellik olasılığı nedeniyle her zaman bir lib kullanın!
Amin Rahimi

20

Checkout AcrossTabs - Çapraz kökenli tarayıcı sekmeleri arasında kolay iletişim. İletişimi daha kolay ve güvenilir hale getirmek için postMessage ve sessionStorage API'sının bir kombinasyonunu kullanır .


Farklı yaklaşımlar vardır ve her birinin kendi avantajları ve dezavantajları vardır. Her birini tartışalım:

  1. Yerel depolama

    Artıları :

    1. Web depolama, çerezlerde bir iyileştirme olarak basit bir şekilde görüntülenebilir ve çok daha fazla depolama kapasitesi sağlar. Mozilla kaynak koduna bakarsanız, 5120KB'nin ( Chrome'da 2,5 Milyon karaktere eşit 5 MB ) tüm alan için varsayılan depolama boyutu olduğunu görebiliriz. Bu, çalışmak için tipik bir 4KB çerezinden çok daha fazla alan sağlar.
    2. Veriler, her HTTP isteği (HTML, resimler, JavaScript, CSS, vb.) İçin sunucuya geri gönderilmez - istemci ve sunucu arasındaki trafik miktarını azaltır.
    3. LocalStorage'da depolanan veriler, açıkça silinene kadar devam eder. Yapılan değişiklikler kaydedilir ve siteye yapılan mevcut ve gelecekteki tüm ziyaretler için kullanılabilir.

    Eksileri :

    1. Aynı köken politikası üzerinde çalışır . Bu nedenle, depolanan veriler yalnızca aynı kaynaktan elde edilebilir.
  2. Kurabiye

    Artıları:

    1. Diğerlerine kıyasla AFAIK hiçbir şey yok.

    Eksileri:

    1. 4K sınırı, ad, değer, son kullanma tarihi vb. Dahil tüm çerez içindir. Çoğu tarayıcıyı desteklemek için adı 4000 baytın altında ve genel çerez boyutu 4093 bayt'ın altında tutun.
    2. Veriler, her HTTP isteği (HTML, resimler, JavaScript, CSS, vb.) İçin sunucuya geri gönderilir; bu da istemci ve sunucu arasındaki trafik miktarını artırır.

      Genellikle aşağıdakilere izin verilir:

      • Toplam 300 çerez
      • Çerez başına 4096 bayt
      • Alan adı başına 20 çerez
      • Alan adı başına 81920 bayt (Maksimum 4096 = 81920 bayt boyutunda 20 çerez verildi.)
  3. sessionStorage

    Artıları:

    1. Buna benzer localStorage.
    2. Değişiklikler yalnızca pencere başına (veya Chrome ve Firefox gibi tarayıcılarda sekme) kullanılabilir. Yapılan değişiklikler kaydedilir ve geçerli sayfa ile aynı pencerede gelecekteki siteye ziyaretler için kullanılabilir. Pencere kapatıldıktan sonra depolama alanı silinir

    Eksileri:

    1. Veriler yalnızca ayarlandığı pencerenin / sekmenin içinde bulunur.
    2. Veriler kalıcı değildir, yani pencere / sekme kapatıldıktan sonra kaybolacaktır.
    3. Gibi localStorage, tt çalışır aynı kaynak politikası . Bu nedenle, depolanan veriler yalnızca aynı kaynaktan elde edilebilir.
  4. PostMessage

    Artıları:

    1. Kaynaklar arası iletişimi güvenle sağlar .
    2. Bir veri noktası olarak, (Safari ve Chrome tarafından kullanılan) WebKit uygulaması şu anda herhangi bir sınırlama uygulamamaktadır (hafızanın tükenmesi ile yüklenenler dışında).

    Eksileri:

    1. Geçerli pencereden bir pencere açmanız gerekir ve daha sonra yalnızca pencereleri açık tuttuğunuz sürece iletişim kurabilir.
    2. Güvenlik endişeleri - postMessage yoluyla dizeler göndermek, diğer JavaScript eklentileri tarafından yayınlanan diğer postMessage olaylarını alacağınızdır, bu nedenletargetOriginmesaj dinleyicisine iletilen veriler içinave akıl sağlığı kontrolüuyguladığınızdan emin olun.
  5. PostMessage + SessionStorage'ın bir kombinasyonu

    Birden çok sekme arasında iletişim kurmak için postMessage kullanma ve aynı zamanda iletilen veriyi devam ettirmek için yeni açılan tüm sekmelerde / pencerelerde sessionStorage kullanarak. Sekmeler / pencereler açık kaldığı sürece veriler kalıcı olacaktır. Bu nedenle, açıcı sekmesi / penceresi kapansa bile, açılan sekmeler / pencereler yenilendikten sonra bile tüm verilere sahip olacaktır.

Bunun için çapraz kökenli sekmeler / pencereler ve sessionStorage yaşadıkları sürece açılan sekmeler / windows kimliğini sürdürmek için postMessage API kullanan AcrossTabs adlı bir JavaScript kitaplığı yazdım .


Kullanarak AcrossTabs, başka bir sekmede farklı bir web sitesi açmak ve bu siteden verileri üst sekmeye almak mümkün müdür? Başka bir web sitesi için kimlik doğrulama ayrıntılarına sahip olacağım.
Madhur Bhaiya

1
Evet, @MadhurBhaiya
softvar

Çerezin en büyük avantajı, "a.target.com", "b.target.com" gibi bir dizi kökeniniz olduğunda yaygın olarak kullanılan çapraz kökenli aynı alana izin vermesidir
StarPinkER

7

İnsanların kullanmayı düşünmesi gereken bir diğer yöntem de Paylaşılan İşçilerdir. Bunun son teknoloji bir konsept olduğunu biliyorum, ancak Paylaşılan İşçide yerel depolamadan çok daha hızlı olan ve aynı kökende olduğunuz sürece üst / alt pencere arasında bir ilişki gerektirmeyen bir geçiş oluşturabilirsiniz.

Bununla ilgili yaptığım bazı tartışmalar için cevabımı burada görebilirsiniz .


7

Aynı kökene ait sekmeler / pencereler arasında senkronizasyon / iletişim kurmak için küçük bir açık kaynaklı bileşen var (yasal uyarı - katkıda bulunanlardan biriyim!) 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

PS: "Lock / mutex / sync" bileşenlerinin çoğu, neredeyse aynı anda gerçekleşen websocket bağlantılarında başarısız olduğu için burada önerme özgürlüğünü aldım


6

Bir kitaplık sysend.js oluşturdum , çok küçük, kaynak kodunu kontrol edebilirsiniz. Kütüphanenin herhangi bir dış bağımlılığı yoktur.

Aynı tarayıcı ve etki alanındaki sekmeler / pencereler arasındaki iletişim için kullanabilirsiniz. Kitaplık destekleniyorsa BroadcastChannel veya localStorage'daki depolama olayını kullanır.

API çok basit:

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

tarayıcınız BroadcastChannel'i desteklediğinde değişmez nesne gönderdi (ancak aslında tarayıcı tarafından otomatik olarak serileştirildi) ve değilse önce JSON'a serileştirildi ve diğer tarafta serileştirildi.

Son sürüm ayrıca Etki Alanları Arası iletişim için proxy oluşturmak için yardımcı API'ye sahiptir. (hedef etki alanında tek bir html dosyası gerektirir).

İşte demo .

DÜZENLE :

Hedef etki alanına özel dosya ve kaynak etki alanından çağrı işlevi eklerseniz, yeni sürüm Alanlar Arası iletişimi de destekler :proxy.htmlproxy

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

(proxy.html çok basit bir html dosyasıdır, kütüphaneyle birlikte sadece bir komut dosyası etiketi vardır).

İki yönlü iletişim istiyorsanız, aynı şeyi target.cometki alanında yapmanız gerekir .

NOT : localStorage kullanarak aynı işlevselliği uygulayacaksanız, IE'de sorun vardır. Depolama olayı, etkinliği tetikleyen aynı pencereye gönderilir ve diğer tarayıcılar için yalnızca diğer sekmeler / pencereler için çağrılır.


2
Bunun için sana biraz kudo vermek istedim. Basit ve oturum kapatma uyarı yazılımının insanları tekmelemesini önlemek için sekmelerim arasında iletişim kurmamı sağlayan güzel tatlı küçük ek. İyi iş. Basit bir mesajlaşma çözümü istiyorsanız bunu tavsiye ederim.
BrownPony


1

Blogumda bunun hakkında bir makale yazdım: http://www.ebenmonney.com/blog/how-to-implement-remember-me-functionality-using-token-based-authentication-and-localstorage-in-a- web uygulaması .

Oluşturduğum bir kütüphaneyi kullanarak storageManagerbunu şu şekilde yapabilirsiniz:

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

Diğer senaryoları da ele almak için başka uygun yöntemler de vardır


0

Bu, Tomas M'nin Chrome için yanıtının bir geliştirme storageparçasıdır . Dinleyici eklemeliyiz

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

Depolama alanına öğeyi yükle / kaydet bu olayı zorlamıyor - manuel olarak

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

ve şimdi, tüm açık sekmeler etkinlik alacak

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.