多页签websocket 共享

网上搜索类似的问题方法讲的都很不错,以下是一个简答说明

问题来源

主要是看到atmosphere 的js client 看到支持多页签websocket 共享,比较好奇,顺带看了下实现机制,发现居然是基于了
localStorage的storage event 实现上还是比较巧妙,功能还是很强大的

参考处理

 
function _execute() {
                // Shared across multiple tabs/windows.
                if (_request.shared) {
                       // windows 以及tabs 共享
                    _localStorageService = _local(_request);
                    if (_localStorageService != null) {
                        if (_canLog('debug')) {
                            atmosphere.util.debug("Storage service available. All communication will be local");
                        }
 
                        if (_localStorageService.open(_request)) {
                            // Local connection.
                            return;
                        }
                    }
 
                    if (_canLog('debug')) {
                        atmosphere.util.debug("No Storage service available.");
                    }
                    // Wasn't local or an error occurred
                    _localStorageService = null;
                }
 
 

_local 的处理

function _local(request) {
                var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
                    // 基于localstorage 的处理
                    storage: function () {
                        function onstorage(event) {
                            if (event.key === name && event.newValue) {
                                // 处理消息
                                listener(event.newValue);
                            }
                        }
 
                        if (!atmosphere.util.storage) {
                            return;
                        }
 
                        var storage = window.localStorage,
                            get = function (key) {
                                var item = storage.getItem(name + "-" + key);
                                return item === null ? [] : JSON.parse(item);
                            },
                            set = function (key, value) {
                                storage.setItem(name + "-" + key, JSON.stringify(value));
                            };
 
                        return {
                            init: function () {
                                set("children", get("children").concat([guid]));
                                atmosphere.util.on(window, "storage", onstorage);
                                return get("opened");
                            },
                            signal: function (type, data) {
                                storage.setItem(name, JSON.stringify({
                                    target: "p",
                                    type: type,
                                    data: data
                                }));
                            },
                            close: function () {
                                var children = get("children");
 
                                atmosphere.util.off(window, "storage", onstorage);
                                if (children) {
                                    if (removeFromArray(children, request.id)) {
                                        set("children", children);
                                    }
                                }
                            }
                        };
                    },
                    windowref: function () {
                        var win = window.open("", name.replace(/\W/g, ""));
 
                        if (!win || win.closed || !win.callbacks) {
                            return;
                        }
 
                        return {
                            init: function () {
                                win.callbacks.push(listener);
                                win.children.push(guid);
                                return win.opened;
                            },
                            signal: function (type, data) {
                                if (!win.closed && win.fire) {
                                    win.fire(JSON.stringify({
                                        target: "p",
                                        type: type,
                                        data: data
                                    }));
                                }
                            },
                            close: function () {
                                // Removes traces only if the parent is alive
                                if (!orphan) {
                                    removeFromArray(win.callbacks, listener);
                                    removeFromArray(win.children, guid);
                                }
                            }
 
                        };
                    }
                };
 
                function removeFromArray(array, val) {
                    var i, length = array.length;
 
                    for (i = 0; i < length; i++) {
                        if (array[i] === val) {
                            array.splice(i, 1);
                        }
                    }
 
                    return length !== array.length;
                }
 
                // Receives open, close and message command from the parent
                function listener(string) {
                    var command = JSON.parse(string), data = command.data;
                    // 基于协议处理消息
                    if (command.target === "c") {
                        switch (command.type) {
                            case "open":
                                _open("opening", 'local', _request);
                                break;
                            case "close":
                                if (!orphan) {
                                    orphan = true;
                                    if (data.reason === "aborted") {
                                        _close();
                                    } else {
                                        // Gives the heir some time to reconnect
                                        if (data.heir === guid) {
                                            _execute();
                                        } else {
                                            setTimeout(function () {
                                                _execute();
                                            }, 100);
                                        }
                                    }
                                }
                                break;
                            // 消息处理
                            case "message":
                               // 消息处理
                                _prepareCallback(data, "messageReceived", 200, request.transport);
                                break;
                            // 本地消息处理
                            case "localMessage":
                                _localMessage(data);
                                break;
                        }
                    }
                }
 
                function findTrace() {
                    var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
                    if (matcher) {
                        return JSON.parse(decodeURIComponent(matcher[2]));
                    }
                }
 
                // Finds and validates the parent socket's trace from the cookie
                trace = findTrace();
                if (!trace || atmosphere.util.now() - trace.ts > 1000) {
                    return;
                }
 
                // Chooses a connector
                connector = connectors.storage() || connectors.windowref();
                if (!connector) {
                    return;
                }
 
                return {
                    open: function () {
                        var parentOpened;
 
                        // Checks the shared one is alive
                        _traceTimer = setInterval(function () {
                            var oldTrace = trace;
                            trace = findTrace();
                            if (!trace || oldTrace.ts === trace.ts) {
                                // Simulates a close signal
                                listener(JSON.stringify({
                                    target: "c",
                                    type: "close",
                                    data: {
                                        reason: "error",
                                        heir: oldTrace.heir
                                    }
                                }));
                            }
                        }, 1000);
 
                        parentOpened = connector.init();
                        if (parentOpened) {
                            // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
                            setTimeout(function () {
                                _open("opening", 'local', request);
                            }, 50);
                        }
                        return parentOpened;
                    },
                    send: function (event) {
                        connector.signal("send", event);
                    },
                    localSend: function (event) {
                        connector.signal("localSend", JSON.stringify({
                            id: guid,
                            event: event
                        }));
                    },
                    close: function () {
                        // Do not signal the parent if this method is executed by the unload event handler
                        if (!_abortingConnection) {
                            clearInterval(_traceTimer);
                            connector.signal("close");
                            connector.close();
                        }
                    }
                };
            }

说明:以上代码处理还是比较复杂的,核心上还是利用了localStorage的storage event,因为消息框架的复杂性,以上代码做了不少消息协议的处理
而且同时也包含了处理多windows的共享,很值得仔细研究下

参考资料

https://juejin.cn/post/6844904163533389837
https://juejin.cn/post/6844903811232825357
http://www.w3.org/TR/webstorage/#event-storage
https://github.com/Atmosphere/atmosphere-javascript
https://developer.mozilla.org/en-US/docs/web/api/window/open

posted on 2022-04-24 21:53  荣锋亮  阅读(408)  评论(0编辑  收藏  举报

导航