2011-12-12 54 views
16

Cho đến nay tôi chỉ thấy các hướng dẫn để đăng tải khi một cửa sổ gửi một loại tin nhắn, và cửa sổ kia diễn giải thông điệp chỉ bằng một cách duy nhất.PostMessage với nhiều chức năng hoặc gọi lại tùy chỉnh

Điều gì xảy ra nếu tôi muốn có nhiều loại tương tác khác nhau giữa các cửa sổ, bạn có thể xử lý bưu thiếp đó không?

Điều đó có chống lại hạt của việc đăng tải phải làm gì không?

Ví dụ: nếu tôi muốn có thể gửi các cuộc gọi lại tùy chỉnh qua lại, v.v ...?

+0

Không chắc chắn ý bạn là gì; bạn tự do kiểm tra một thông điệp nhận được và có điều kiện gọi mã khác nhau, vv ... – Pointy

+0

Đó là jsut cho đến nay, tôi đã không thấy bất kỳ ví dụ nào về điều đó, và từ những gì tôi đã đọc, postmessage không thể gửi đồ vật hoặc bất cứ thứ gì, có nghĩa là tôi phải mổ xẻ sợi dây ... có vẻ như không sạch lắm. – johnnietheblack

Trả lời

49

Có một số cách để truyền thông điệp nhiều phần tới trình xử lý postMessage. Cách đầu tiên (và ít hơn "sạch") là sử dụng ký tự phân cách, sau đó chuyển dữ liệu của bạn qua một chuỗi.

Giả sử chúng tôi muốn chuyển ID người dùng, hành động và tên người dùng. Chuỗi sẽ trông như thế này:

54|do_logout|chris

Trong handler postMessage, dữ liệu thông qua có thể split (docs) trên nhân vật |, sau đó mỗi phân khúc của thông điệp có thể được sử dụng khi cần thiết.

Một tuyến đường khác, thay vì tạo/tách chuỗi thủ công, là sử dụng JSON (docs) để chuyển đổi đối tượng thành chuỗi ở một bên và sử dụng JSON để chuyển đổi lại đối tượng trong trình xử lý.

var pass_data = { 
    'name':'Chris', 
    'id':54, 
    'action':'do_logout' 
}; 
target_window.postMessage(JSON.stringify(pass_data), "http://www.example.net"); 

... sau đó trong xử lý:

function (event) { 
    var pass_data = JSON.parse(event.data); 
} 

Hãy chắc chắn để kiểm tra, tuy nhiên, như đối tượng JSON không được cung cấp trên tất cả các đại lý người dùng, đặc biệt là cũ. Có rất nhiều (nhiều, nhiều) thư viện của bên thứ ba ngoài đó để hỗ trợ JSON shim, do đó, đừng để việc thiếu sự chấp nhận hoàn toàn làm bạn sợ hãi - JSON chắc chắn là một tiêu chuẩn "di chuyển" an toàn.

Sẽ không đẹp hơn nếu chúng ta có thể vượt qua đối tượng đó ngay lập tức? Vâng, nhìn chằm chằm vào Firefox 6 (source), dữ liệu bạn chuyển đến một trình xử lý bưu chính có thể là một đối tượng. Đối tượng sẽ được tuần tự, vì vậy có một số mối quan tâm về phía trước đó, nhưng:

var pass_data = { 
    'name':'Chris', 
    'id':54, 
    'action':'do_logout' 
}; 
target_window.postMessage(pass_data, "http://www.example.net"); 

Một chút đẹp hơn, eh? Thật không may, các phiên bản hiện tại của IE sẽ chỉ xử lý các chuỗi. Tôi không thể tìm thấy bất kỳ cuộc thảo luận nào về các kế hoạch tương lai liên quan đến postMessage đối với IE 10. Hơn nữa, có một lỗi đã biết trong IE 8/9 mà vi phạm postMessage cho bất kỳ thứ gì ngoài khung. (source).

Tham gia vào khía cạnh cụ thể của câu hỏi - gọi lại. Trừ khi bạn có thể chuyển cuộc gọi lại bằng tên hàm, không có cách nào để chuyển một hàm; không có chức năng ẩn danh nào cho bạn. Điều này liên quan đến cách dữ liệu thực sự được truyền cho trình xử lý. Trong thực tế, có "không phải là" hỗ trợ cho các đối tượng như dữ liệu, đằng sau hậu trường trình duyệt đang chuyển đối tượng được truyền của bạn thành một chuỗi (serialization).Tất cả những gì đã nói, sau đó, bạn nên hiểu rằng việc truyền một đối tượng hoàn toàn giống với việc sử dụng JSON đến stringify một đối tượng trước khi vượt qua, chỉ trong trường hợp trước đây trình duyệt đang thực hiện tuần tự hóa riêng của nó (và sau đó không được thực hiện), trong khi với các tuyến đường sau đó là vào bạn để serialize/unserialize.

Những điểm take-away ở đây:

  • postMessage vẫn bị hạn chế qua trình duyệt hỗ trợ
  • Xu hướng cho các phiên bản mới hơn của trình duyệt tiêu chuẩn phù hợp là cho phép đoạn của các đối tượng, thêm vào chuỗi
  • Đối tượng được truyền sẽ được sắp xếp theo thứ tự, vì vậy không có tham chiếu hàm nào được phép
  • Hỗ trợ rộng nhất "trong tự nhiên" chỉ dành cho dữ liệu chuỗi, có nghĩa là bạn sẽ phải gắn chuỗi và "đóng gói" dữ liệu của mình như được minh họa ở trên nếu bạn muốn hỗ trợ nhiều ide của đại lý người dùng
  • Internet Explorer sẽ làm hỏng mọi kế hoạch bạn đã bao giờ thực hiện (kể cả ngày lễ gia đình)

Tài liệu và tài liệu tham khảo

+5

Câu trả lời hay! Chúc tôi sẽ +10 cho các ngày nghỉ gia đình bình luận – johnnietheblack

+1

Bạn luôn có thể thực hiện mã hóa và giải mã JSON ở hai bên của thư. – Pointy

+1

Ngay cả với tuần tự hóa JSON, bạn không thể chuyển các tham chiếu hàm (mà tôi tin rằng OP là sau) vì chúng không thể được tuần tự hóa. Đây là trường hợp cả hai nếu bạn mã hóa JSON đối tượng (đối với IE) hoặc chỉ truyền đối tượng (cho Firefox> 6). Nếu bạn cố gắng vượt qua một chức năng trong Firefox, bạn sẽ nhận được lỗi 'Lỗi: Đối tượng không thể được nhân bản.' :(Quá xấu, nhưng có lẽ điều này an toàn hơn. –

1

Một khá dễ dàng cách để kích hoạt callbacks mà không đi bất kỳ mã thực tế sẽ là:

Target

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

Nguồn

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

Callback với postMessage: rất tốt và rất hữu ích

Có một plugin tốt đẹp tôi đã tìm thấy trên npm called "silver-bullet". Nó postMessage với callbacks và sử dụng eventEmitter để nhận các sự kiện cụ thể. Nó rất đẹp.

Tuy nhiên, để thực hiện điều này tôi sẽ làm một cái gì đó giống như ...

phostMessage(iframe, someObj, callback); 

Bạn phải làm điều này:

  1. Bạn cần một callback chung ID qua giữa khung giao tiếp.
  2. Người gửi tạo một ID gọi lại duy nhất trên mỗi thư và lưu trữ nó trong hàm băm tìm kiếm lại để tìm lại cuộc gọi lại sau khi gửi.
  3. Máy thu của thư chỉ đảm bảo số gọi lại được gửi lại.
  4. Tất cả các khung giao tiếp sử dụng cùng thư viện JS cho việc này.

Dưới đây là một minh chứng rất cơ bản về điều đó:

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), '*'); 
} 

Tôi lấy khái niệm này một chút nữa và sử dụng một tra cứu xử lý thông điệp nơi thư có tên hàm xử lý mong muốn gợi lên và vượt qua một thông điệp tới . Trình xử lý tin nhắn cũng nhận một cuộc gọi lại khi hoàn thành việc gọi lại cuộc gọi lại. Cuộc gọi lại chỉ có logic đơn giản gọi lại tin nhắn gốc gửi lại id gọi lại đã nhận của nó.

Vì vậy, dòng cuối cùng của mã cho việc xử lý sự kiện thông điệp sẽ là:

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

cho phép những thứ không đồng bộ xảy ra.

Các vấn đề liên quan