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+ 与其他现代浏览器。

原文地址:https://www.cnblogs.com/zhengyan/p/8205218.html