cross-origin-storage

跨域本地存储

目前为 2024-05-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/473442/1384340/cross-origin-storage.js

  1. // @name cross-origin-storage
  2. // @name:zh 跨域本地存储
  3. // @namespace https://github.com/pansong291/
  4. // @version 1.0.2
  5. // @author paso
  6. // @license Apache-2.0
  7.  
  8. ;(function() {
  9. 'use strict'
  10.  
  11. const __msgType = 'cross-origin-storage'
  12.  
  13. /**
  14. * 生成随机ID
  15. * @returns {string}
  16. */
  17. function uuid() {
  18. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  19. const r = (Math.random() * 16) | 0,
  20. v = c === 'x' ? r : (r & 0x3) | 0x8
  21. return v.toString(16)
  22. })
  23. }
  24.  
  25. /**
  26. * @param {WindowProxy} win
  27. * @param {*} msg
  28. */
  29. function sendMsgTo(win, msg) {
  30. win.postMessage(msg, '*')
  31. }
  32.  
  33. /**
  34. * @param {WindowProxy} win
  35. * @param {(e: MessageEvent) => void} handler
  36. */
  37. function onReceive(win, handler) {
  38. win.addEventListener('message', handler)
  39. }
  40.  
  41. /**
  42. * @param {string} middlewareUrl
  43. */
  44. function createStorage(middlewareUrl) {
  45. const iframe = document.createElement('iframe')
  46. iframe.src = middlewareUrl
  47. iframe.setAttribute('style', 'display: none !important;')
  48. window.document.body.appendChild(iframe)
  49. return startStorage(iframe.contentWindow, window)
  50. }
  51.  
  52. /**
  53. * @param {WindowProxy} serverWindow
  54. * @param {WindowProxy} clientWindow
  55. */
  56. function startStorage(serverWindow, clientWindow) {
  57. startStorageServer(serverWindow, clientWindow)
  58. return startStorageClient(serverWindow, clientWindow)
  59. }
  60.  
  61. /**
  62. * @param {WindowProxy} serverWindow
  63. * @param {WindowProxy} clientWindow
  64. */
  65. function startStorageClient(serverWindow, clientWindow) {
  66. const _requests = {} // 所有请求消息数据映射
  67. // Server 是否已准备完成以及缓存的请求队列
  68. const _cache = {
  69. ready: false,
  70. queue: []
  71. }
  72. // 监听 Server 发来的消息
  73. onReceive(clientWindow, (e) => {
  74. if (e?.data?.__msgType !== __msgType) return
  75. if (e.data.ready) {
  76. // Server 已准备完成, 发送队列中的全部请求
  77. _cache.ready = true
  78. while (_cache.queue.length) {
  79. sendMsgTo(serverWindow, _cache.queue.shift())
  80. }
  81. return
  82. }
  83. let { id, response } = e.data
  84.  
  85. // 找到消息对应的回调函数
  86. let currentCallback = _requests[id]
  87. if (!currentCallback) return
  88. // 调用并返回数据
  89. currentCallback(response, e.data)
  90. delete _requests[id]
  91. })
  92.  
  93. /**
  94. * 发起请求函数
  95. * @param method 请求方式
  96. * @param key
  97. * @param value
  98. */
  99. function _requestFn(method, key, value) {
  100. return new Promise((resolve) => {
  101. const req = {
  102. id: uuid(),
  103. method,
  104. key,
  105. value,
  106. __msgType
  107. }
  108.  
  109. // 请求唯一标识 id 和回调函数的映射
  110. _requests[req.id] = resolve
  111.  
  112. if (_cache.ready) {
  113. // Server 已准备完成时直接发请求
  114. sendMsgTo(serverWindow, req)
  115. } else {
  116. // Server 未准备完成则把请求放入队列
  117. _cache.queue.push(req)
  118. }
  119. })
  120. }
  121.  
  122. return {
  123. /**
  124. * 获取存储数据
  125. * @param {Iterable | Object | string} key
  126. */
  127. getItem(key) {
  128. return _requestFn('get', key)
  129. },
  130. /**
  131. * 更新存储数据
  132. * @param {Object | string} key
  133. * @param {Object | string} [value = undefined]
  134. */
  135. setItem(key, value = void 0) {
  136. return _requestFn('set', key, value)
  137. },
  138. /**
  139. * 删除数据
  140. * @param {Iterable | Object | string} key
  141. */
  142. delItem(key) {
  143. return _requestFn('delete', key)
  144. },
  145. /**
  146. * 清除数据
  147. */
  148. clear() {
  149. return _requestFn('clear')
  150. }
  151. }
  152. }
  153.  
  154. /**
  155. * @param {WindowProxy} serverWindow
  156. * @param {WindowProxy} clientWindow
  157. */
  158. function startStorageServer(serverWindow, clientWindow) {
  159. const functionMap = {
  160. /**
  161. * 设置数据
  162. * @param {Object | string} key
  163. * @param {?Object | ?string} value
  164. */
  165. setStore(key, value = void 0) {
  166. if (!key) return
  167. if (typeof key === 'string') {
  168. return localStorage.setItem(key, typeof value === 'object' ? JSON.stringify(value) : value)
  169. }
  170. Object.keys(key).forEach((dataKey) => {
  171. let dataValue = typeof key[dataKey] === 'object' ? JSON.stringify(key[dataKey]) : key[dataKey]
  172. localStorage.setItem(dataKey, dataValue)
  173. })
  174. },
  175.  
  176. /**
  177. * 获取数据
  178. * @param {Iterable | Object | string} key
  179. */
  180. getStore(key) {
  181. if (!key) return
  182. if (typeof key === 'string') return localStorage.getItem(key)
  183. let dataRes = {}
  184. const keys = key[Symbol.iterator] ? key : Object.keys(key)
  185. for (const dataKey of keys) {
  186. dataRes[dataKey] = localStorage.getItem(dataKey) || null
  187. }
  188. return dataRes
  189. },
  190.  
  191. /**
  192. * 删除数据
  193. * @param {Iterable | Object | string} key
  194. */
  195. deleteStore(key) {
  196. if (!key) return
  197. if (typeof key === 'string') return localStorage.removeItem(key)
  198. const keys = key[Symbol.iterator] ? key : Object.keys(key)
  199. for (const dataKey of keys) {
  200. localStorage.removeItem(dataKey)
  201. }
  202. },
  203.  
  204. /**
  205. * 清空
  206. */
  207. clearStore() {
  208. localStorage.clear()
  209. }
  210. }
  211.  
  212. // 监听 Client 消息
  213. onReceive(serverWindow, (e) => {
  214. if (e?.data?.__msgType !== __msgType) return
  215. const { method, key, value, id = 'default' } = e.data
  216.  
  217. // 获取方法
  218. const func = functionMap[`${method}Store`]
  219.  
  220. // 取出本地的数据
  221. const response = {
  222. data: func?.(key, value)
  223. }
  224. if (!func) response.errorMsg = 'Request method error!'
  225.  
  226. // 发送给 Client
  227. sendMsgTo(clientWindow, { id, request: e.data, response, __msgType })
  228. })
  229.  
  230. // 通知 Client, Server 已经准备完成
  231. sendMsgTo(clientWindow, { ready: true, __msgType })
  232. }
  233.  
  234. if (!window.paso || !(window.paso instanceof Object)) window.paso = {}
  235. window.paso.crossOriginStorage = {
  236. startStorage,
  237. createStorage
  238. }
  239. })()