这是我参与更文挑战的第22天,活动详情查看: 更文挑战 !
👽 概论
众所周知,受限于浏览器的安全策略,同一浏览器的不同标签页之间是没法直接进行通信的。间接实现通信的最简单可靠的桥梁便是LocalStroage。
LocalStroage是浏览器中实现本地存储的一种方案,它与SessionStroage最大的区别便在于其不受标签页隔离限制,只要是同源状态下便可共享存储内容。
👽 基本思路
实现的基本思路也很简单:在全局添加监听localStroage的监听器,当目标页面对localStoage内的值发生改变时做出相应。
原理很简单,具体的实现我们来分析下借由此原理实现的lsbridge库的源码,看看还有没有更多值得学习的地方。
👽 lsbridge源码解读
//lsbridge https://github.com/krasimir/lsbridge
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.lsbridge = factory();
}
})(this, function () {
var api = {};
/**
* --测试lsbridge兼容性--
* 但此步必要性不是很强,
* 目前不兼容lsbridge的浏览器几乎没有
*/
api.isLSAvailable = (function () {
var mod = '_';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch (e) {
return false;
}
})();
if (api.isLSAvailable) {
var interval = 100, //获取订阅消息的间隔(每100ms获取一次)事实上该库就是通过不断的访问localstroge来进行监听的。
intervalForRemoval = 200, //清除间隔
ls = localStorage,
listeners = {}, //监听器(所有订阅的消息都会在这个对象上)
isLoopStarted = false,
buffer = {}; //缓存器(起数据中转作用)
/**
*
* @description 此方法就是定期监听localStroge的关键方法。
* 该方法会从监听器上取出各个命名空间对应的值,将其置入缓存器,
* 然后对数据执行相应的自定义方法,最后再清空缓存器
*/
var loop = function () {
console.log('buffer: ', buffer);
for (var namespace in listeners) {
var data = ls.getItem(namespace);
if (data && buffer[namespace] && buffer[namespace].indexOf(data) === -1) {
buffer[namespace].push(data);
try {
var parsed = JSON.parse(data);
if (parsed) data = parsed;
} catch (e) {}
for (var i = 0; i < listeners[namespace].length; i++) {
listeners[namespace][i](data);
}
if (!ls.getItem(namespace + '-removeit')) {
ls.setItem(namespace + '-removeit', '1');
(function (n) {
setTimeout(function () {
ls.removeItem(n);
ls.removeItem(n + '-removeit');
buffer[namespace] = [];
}, intervalForRemoval);
})(namespace);
}
} else if (!data) {
buffer[namespace] = [];
}
}
console.log('buffer: ', buffer);
setTimeout(loop, interval);
return true;
};
/**
* @description 调用此方法向目标命名空间发布消息
* @param {*} namespace 目标命名空间(可以看做localstorage中键值对的key)
* @param {*} data 消息内容(可以看做localstorage中键值对的value)
* @supplement 该方法有对函数或者对象形式的消息内容做特殊处理,然后将其存入localstroge的
* 相应命名空间中
*/
api.send = function (namespace, data) {
var raw = '';
if (typeof data === 'function') {
data = data();
}
if (typeof data === 'object') {
raw = JSON.stringify(data);
} else {
raw = data;
}
ls.setItem(namespace, raw);
};
/**
* @description 调用此方法订阅目标命名空间
* @param {*} namespace 目标命名空间
* @param {*} cb 对应待执行的自定义方法
* @supplement //目标命名空间存在后(不存在时会在监听器和缓存器上同时新建相应命名空间)。
* 会将对应的执行方法存入监听器中,同时开启定时器
*/
api.subscribe = function (namespace, cb) {
if (!listeners[namespace]) {
listeners[namespace] = [];
buffer[namespace] = [];
}
listeners[namespace].push(cb);
if (!isLoopStarted) {
isLoopStarted = loop();
}
};
/**
* @description 调用此方法取消订阅
* @param {*} namespace 目标命名空间
* @supplement 取消订阅时释放监听器与缓存器
*/
api.unsubscribe = function (namespace) {
if (listeners[namespace]) {
listeners[namespace] = [];
}
if (buffer[namespace]) {
buffer[namespace] = [];
}
};
api.getBuffer = function () {
return buffer;
};
} else {
api.send = api.subscribe = function () {
throw new Error('localStorage not supported.');
};
}
return api;
});