Greasy Fork is available in English.
跨域本地存储
此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.org/scripts/473442/1384389/cross-origin-storage.js
// @name cross-origin-storage // @name:zh 跨域本地存储 // @namespace https://github.com/pansong291/ // @version 1.0.4 // @author paso // @license Apache-2.0 ;(function() { 'use strict' const __msgType = 'cross-origin-storage' /** * 生成随机ID * @returns {string} */ function uuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8 return v.toString(16) }) } /** * @param {WindowProxy} win * @param {*} msg */ function sendMsgTo(win, msg) { win.postMessage(msg, '*') } /** * @param {(e: MessageEvent) => void} handler */ function onReceive(handler) { window.addEventListener('message', handler) } /** * @param {string} serverUrl */ function createStorageClient(serverUrl) { const serverIframe = document.createElement('iframe') serverIframe.src = serverUrl serverIframe.setAttribute('style', 'display: none !important;') window.document.body.appendChild(serverIframe) return startStorageClient(serverIframe.contentWindow, 10_000) } /** * @param {WindowProxy} serverWindow * @param {number} [timeout] */ function startStorageClient(serverWindow, timeout) { // 所有请求消息数据映射 const _requests = {} const _cache = { // 开始建立连接的时间 startTime: 0, // 与 Server 的连接是否已建立完成 connected: false, // 连接是否超时 timeout: false, // 缓存的请求队列 queue: [] } // 监听 Server 发来的消息 onReceive((e) => { if (e?.data?.__msgType !== __msgType) return if (e.data.connected) { // 连接已建立完成, 发送队列中的全部请求 _cache.connected = true while (_cache.queue.length) { sendMsgTo(serverWindow, _cache.queue.shift()) } return } const { id, response } = e.data // 找到消息对应的回调函数,调用并传递数据 _requests[id]?.resolve(response) delete _requests[id] }) // 请求与 Server 建立连接 const loopId = setInterval(() => { if (_cache.connected) { clearInterval(loopId) return } if (!_cache.startTime) { _cache.startTime = Date.now() } else if (timeout && timeout > 0) { if (Date.now() - _cache.startTime > timeout) { _cache.timeout = true clearInterval(loopId) while (_cache.queue.length) { const reqId = _cache.queue.shift().id _requests[reqId]?.reject('connection timeout') delete _requests[reqId] } return } } sendMsgTo(serverWindow, { connect: 1, __msgType }) }, 500) /** * 发起请求函数 * @param method 请求方式 * @param key * @param value */ function _requestFn(method, key, value) { return new Promise((resolve, reject) => { const req = { id: uuid(), method, key, value, __msgType } // 请求唯一标识 id 和回调函数的映射 _requests[req.id] = { resolve, reject } if (_cache.connected) { // 连接建立完成时直接发请求 sendMsgTo(serverWindow, req) } else if (_cache.timeout) { // 连接超时拒绝请求 reject('connection timeout') } else { // 连接未建立则把请求放入队列 _cache.queue.push(req) } }) } return { /** * 获取存储数据 * @param {Iterable | Object | string} key */ getItem(key) { return _requestFn('get', key) }, /** * 更新存储数据 * @param {Object | string} key * @param {Object | string} [value = undefined] */ setItem(key, value = void 0) { return _requestFn('set', key, value) }, /** * 删除数据 * @param {Iterable | Object | string} key */ delItem(key) { return _requestFn('delete', key) }, /** * 清除数据 */ clear() { return _requestFn('clear') } } } function startStorageServer() { const functionMap = { /** * 设置数据 * @param {Object | string} key * @param {?Object | ?string} value */ setStore(key, value = void 0) { if (!key) return if (typeof key === 'string') { return localStorage.setItem(key, typeof value === 'object' ? JSON.stringify(value) : value) } Object.keys(key).forEach((dataKey) => { let dataValue = typeof key[dataKey] === 'object' ? JSON.stringify(key[dataKey]) : key[dataKey] localStorage.setItem(dataKey, dataValue) }) }, /** * 获取数据 * @param {Iterable | Object | string} key */ getStore(key) { if (!key) return if (typeof key === 'string') return localStorage.getItem(key) let dataRes = {} const keys = key[Symbol.iterator] ? key : Object.keys(key) for (const dataKey of keys) { dataRes[dataKey] = localStorage.getItem(dataKey) || null } return dataRes }, /** * 删除数据 * @param {Iterable | Object | string} key */ deleteStore(key) { if (!key) return if (typeof key === 'string') return localStorage.removeItem(key) const keys = key[Symbol.iterator] ? key : Object.keys(key) for (const dataKey of keys) { localStorage.removeItem(dataKey) } }, /** * 清空 */ clearStore() { localStorage.clear() } } const clients = new Set() // 监听 Client 消息 onReceive((e) => { if (e?.data?.__msgType !== __msgType) return if (e.data.connect) { clients.add(e.source) // 通知 Client, 连接建立完成 sendMsgTo(e.source, { connected: true, __msgType }) return } const { method, key, value, id = 'default' } = e.data // 获取方法 const func = functionMap[`${method}Store`] // 取出本地的数据 const response = { data: func?.(key, value) } if (!func) response.errorMsg = 'Request method error!' // 发送给 Client const resultMsg = { id, request: e.data, response, __msgType } clients.forEach((c) => sendMsgTo(c, resultMsg)) }) } if (!window.paso || !(window.paso instanceof Object)) window.paso = {} window.paso.crossOriginStorage = { startStorageServer, startStorageClient, createStorageClient } })()