• postMessage 跨域


    基于 postMessage 和 localStorage 的跨域本地存储方案

    安·记 2014-09-07 2099 阅读

    HTML5 的 postMessage 为解决跨域页面通信提供了一套可控的机制, 而 localStorage 则提供了易用简洁的本地存储方案? 这两者结合起来,能否实现跨域本地存储呢 ?

    答案是可以的。假设有 a.com 和 b.com 两个页面。我们想通过 a 页面去修改 b 页面的本地数据。 我们需要做如下步奏:

    • 在 a 页面创建一个 iframe ,嵌入 b 页面
    • a 页面通过 postMessage 传递指定格式的消息给 b 页面
    • b 页面解析 a 页面传递过来的消息内容,调用localStorage API 操作本地数据
    • b 页面包装 localStorage 的操作结果,并通过 postMessage 传递给 a 页面
    • a 页面解析 b 页面传递回来的消息内容,得到 localStorage 的操作结果

    整个过程如下图:

    OK,清楚了整个过程,我们就可以封装出相应的组件:CSClient.js 和 csHub.js,来完成这个过程,下面我们来简单实现一下。

    先来看CSClient.js,作用于 a 页面,用于创建跨域存储实例,它提供了 get、set、del 这三个方法来操作跨域的数据:

    function CSClient(url) {
            this.id = this._getId();
            this._init(url);
            this._origin = this._getOrigin(url);
            this._callbacks = {
                _get: {},
                _set: {},
                _del: {}
            }
            this._bindEvent();
        }
        
        CSClient.prototype._getId = function () {
            id = 0;
            return function () {
                return ++id;
            }
        }();
        
        CSClient.prototype._init = function (url) {
            var frame = document.createElement('iframe');
            frame.style.display = 'none';
            frame.src = url;
            document.body.appendChild(frame);
            this._hub = frame.contentWindow;
        }
        
        CSClient.prototype._getOrigin = function(url) {
            var uri, origin;
            uri = document.createElement('a');
            uri.href = url;
            origin = uri.protocol + '//' + uri.host;
            return origin;
        };
        
        CSClient.prototype._parseMessage = function (method, key, value) {
            return JSON.stringify({
                method: method,
                key: key,
                value: value
            });
        }
        
        
        CSClient.prototype._bindEvent = function () {
            var _this = this;
            window.addEventListener('message', function (event) {
                var data = JSON.parse(event.data);
                var error = data.error;
                var result = data.result  && JSON.parse(data.result) || null;
                try {
                    _this._callbacks['_' + data.method][data.key](error, result);
                }
                catch (e){
                    console.log(e);
                }
            }, false);
        }
        
        CSClient.prototype.get = function (key, callback) {
            this._hub.postMessage(this._parseMessage('get', key), this._origin);
            this._callbacks._get[key] = callback;
        }
        
        CSClient.prototype.set = function (key, value, callback) {
            this._hub.postMessage(this._parseMessage('set', key, value), this._origin);
            this._callbacks._set[key] = callback;
        }
        
        CSClient.prototype.del = function (key, callback) {
            this._hub.postMessage(this._parseMessage('del', key),this._origin);this._callbacks._del[key]= callback;}

    使用方法如下:

    var key = document.querySelector('#key');
        var value = document.querySelector('#value');
        var btn = document.querySelectorAll('button');
        var storage = new CSClient('http://b.com');
    
        btn[0].onclick = function (e) {
            storage.get(key.value, function (err, result) {
                if (err) {
                    console.log(err);
                    return;
                }
                console.log(result);
            })
        }
    
        btn[1].onclick = function (e) {
            storage.set(key.value, value.value, function (err) {
                if (err) {
                    console.log(err);
                    return;
                }
                console.log('set ok');
            })
        }
    
        btn[2].onclick = function (e) {
            storage.del(key.value, function (err) {
                if (err) {
                    console.log(err);
                    return;
                }
                console.log('del ok');
            })
        }

    接着看 csHub.js,作用于 b 页面,用于设置跨域存储的权限、接受 client 的消息、操作本地数据。只有拥有权限的 origin ,才能操作本页面的本地数据。

    var csHub = window.csHub = {
            init: function (origin) {
                this.originRule = origin;
            },
        
            get: function (key) {
                return JSON.stringify(window.localStorage.getItem(key));
            },
        
            set: function (key, value) {
                window.localStorage.setItem(key, JSON.stringify(value));
            },
        
            del: function (key) {
                window.localStorage.removeItem(key);
            }
        };
        
        window.addEventListener('message', function (event) {
            var message = JSON.parse(event.data), result, err = null;
            if (csHub.originRule.test(event.origin)) {
                try {
                    result = csHub[message.method](message.key, message.value);
                }
                catch (e) {
                    err = {
                        message: e.message,
                        stack: e.stack
                    };
                }
                window.parent.postMessage(JSON.stringify({
                    error: err,
                    method: message.method,
                    key: message.key,
                    result: result
                }), event.origin);
            }
        }, false);

    使用方法如下:

    csHub.init(/a.com$/);

    以上代码都比较简单, 主要为了演示如何实现跨域存储。如果你想在项目中使用此方案。推荐使用https://github.com/zendesk/cross-storage这个库。 这个库实现了更灵活的权限机制,使用 ES6 的 promises 规范简化了操作接口, 并对浏览器的差异化做了处理,兼容到了 ie8+ 与其他现代浏览器。

  • 相关阅读:
    linux就该这么学.pdf
    linux中shell编辑小技巧
    相关功能分享
    现代操作系统第三版高清.pdf中文版免费下载
    linux高性能服务器编程pdf免费下载
    git每次更新都需要输入账号密码,如何解决?
    Python 面向对象
    模块和包
    Python常用模块(collections、 time、 random、 os 、sys、序列化模块)
    内置函数和匿名函数(lambda)
  • 原文地址:https://www.cnblogs.com/zhengyan/p/8205218.html
Copyright © 2020-2023  润新知