浏览器LocalStorage和SharedWorker跨标签页通信-连载2
目录
2.3、localStorage及其父代存储对象Storage:
2.5、localStorage的事件类型StorageEvent :
2.6.2、localStorage通信双方的相关脚本支持模块化导入
继续上篇:
边开发项目,就边深入学习、总结和运用:
一、回过头来说“浏览器”的“多标签页”
1.1、什么是“多标签页”
1.2、“多标签页”的本质
之前,一直没有提及“浏览器”的“多标签页”的概念和本质。形象的多,多标签页长这样:
◆ 浏览器App实例的pid---浏览器主进程pid
◆ 浏览器主进程pid下---可被其产生浏览器子进程pid
◆ 浏览器子进程pid---分为后台“非GUI服务子进程”、前台“GUI渲染子进程”
◆ 1个浏览器前台“GUI渲染子进程”---即:标签页,通常是图形用户界面的形式
◆ N个浏览器前台“GUI渲染子进程”的集合---即:多标签页
◆ “非GUI服务子进程”---那些个运行在后台,提供服务的子进程,比如各种worker等等、负责与操作系统通信的进程等等、一些必要的常驻内存的守护进程......
◆ 它是浏览器“多进程基础架构”的集中体现,提供了安全性和性能优势。
◆ 使用它,还可实现“读”、“写”存储的不同客户端操作,防止“共享冲突”
◆ 过去是单进程架构;Google最先发起“多进程基础架构”,微软、苹果等相继跟上。
◆ 关于多标签页的一些官方技术参考:
windows.WindowType - Mozilla | MDN
https://developer.chrome.com/docs/extensions/reference/windows/#type-WindowType
GitHub - GoogleChrome/chrome-extensions-samples: Chrome Extensions Samples
https://developer.chrome.com/docs/extensions/reference/tabs/#type-Tab
二、LocalStorage及其跨标签页通信
2.1、localStorage的概念:
◆ 顾名思义LocalStorage跨标签页通信,带存储,存储的分区为Storage下的Local Storage
◆ localStorage是原生DOM标准库lib.dom.d.ts中WindowLocalStorage的别名:
C:\Users\Administrator\.vscode\extensions\***\node_modules\typescript\lib\lib.dom.d.ts
interface WindowLocalStorage { readonly localStorage: Storage; }
◆ 它的宿主为:Window
◆ 它是全局变量、拥有全局作用域:
declare var localStorage: Storage;
◆ devTool中的存储分区在:
2.2、localStorage的上下文及其适用范围 :
// ● 来超 : // ◆ localStorage及其父代Storage(Storage存储对象) : // 隶属“浏览器的”全局Window上下文,因而对所有标签页均适用 : interface WindowEventHandlers { onafterprint: ((this: WindowEventHandlers, ev: Event) => any) | null; onbeforeprint: ((this: WindowEventHandlers, ev: Event) => any) | null; onbeforeunload: ((this: WindowEventHandlers, ev: BeforeUnloadEvent) => any) | null; ongamepadconnected: ((this: WindowEventHandlers, ev: GamepadEvent) => any) | null; ongamepaddisconnected: ((this: WindowEventHandlers, ev: GamepadEvent) => any) | null; onhashchange: ((this: WindowEventHandlers, ev: HashChangeEvent) => any) | null; onlanguagechange: ((this: WindowEventHandlers, ev: Event) => any) | null; onmessage: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null; onmessageerror: ((this: WindowEventHandlers, ev: MessageEvent) => any) | null; onoffline: ((this: WindowEventHandlers, ev: Event) => any) | null; ononline: ((this: WindowEventHandlers, ev: Event) => any) | null; onpagehide: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null; onpageshow: ((this: WindowEventHandlers, ev: PageTransitionEvent) => any) | null; onpopstate: ((this: WindowEventHandlers, ev: PopStateEvent) => any) | null; onrejectionhandled: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null; onunhandledrejection: ((this: WindowEventHandlers, ev: PromiseRejectionEvent) => any) | null; onunload: ((this: WindowEventHandlers, ev: Event) => any) | null; addEventListener<K extends keyof WindowEventHandlersEventMap>(type: K, listener: (this: WindowEventHandlers, ev: WindowEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener<K extends keyof WindowEventHandlersEventMap>(type: K, listener: (this: WindowEventHandlers, ev: WindowEventHandlersEventMap[K]) => any, options?: boolean | EventListenerOptions): void; removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; // ● storage事件(:StorageEvent类型) : onstorage: ((this: WindowEventHandlers, ev: StorageEvent) => any) | null; } interface WindowLocalStorage { readonly localStorage: Storage; } //
2.3、localStorage及其父代存储对象Storage:
// ◆ Storage存储对象 : ● 此Web存储API接口提供对特定"域/源"的会话(session)或本地存储(local storage)的访问。例如,它允许"添、删、改"存储的数据项 // :This Web Storage API interface provides access to a particular domain's session or local storage. It allows, for example, the addition, modification, or deletion of stored data items. interface Storage { // ◇ Returns the number of key/value pairs. readonly length: number; // // ◇ Removes all key/value pairs, if there are any. // // Dispatches a storage event on Window objects holding an equivalent Storage object. // clear(): void; // ◇ Returns the current value associated with the given key, or null if the given key does not exist. getItem(key: string): string | null; // ◇ Returns the name of the nth key, or null if n is greater than or equal to the number of key/value pairs. key(index: number): string | null; // // ◇ Removes the key/value pair with the given key, if a key/value pair with the given key exists. // // Dispatches a storage event on Window objects holding an equivalent Storage object. // removeItem(key: string): void; // // ◇ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously. // // Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.) // // Dispatches a storage event on Window objects holding an equivalent Storage object. // setItem(key: string, value: string): void; [name: string]: any; } declare var Storage: { prototype: Storage; new(): Storage; }; */
2.4、localStorage能和导航器的存储管理器StorageManager---navigator.storage通信 :
// ◆ 存储管理器StorageManager---navigator.storage : navigator.storage.estimate().then((storageEstimate) => console.log(storageEstimate)); navigator.storage.getDirectory().then((fileSystemDirectoryHandle) => { console.log(fileSystemDirectoryHandle); }); navigator.storage.persist().then((boolean) => console.log('可以持久化吗: ', boolean)); navigator.storage.persisted().then((boolean) => console.log('是否已持久化: ', boolean)); // ● 安全上下文中有效 : Available only in secure contexts. // ◇ 存储管理器StorageManager属于●导航器的存储: navigator.storage:StorageManager : // interface StorageManager { estimate(): Promise<StorageEstimate>; getDirectory(): Promise<FileSystemDirectoryHandle>; persist(): Promise<boolean>; persisted(): Promise<boolean>; } //
2.5、localStorage的事件类型StorageEvent :
// ◆ StorageEvent事件类型 : // ● 当StorageEvent事件"有权访问的存储区域(storageArea: Storage对象)"在另一个文档(document)的上下文中发生"增删改"(即:变化)时,会将其发送到窗口(即: 会触发该事件) : // : A StorageEvent is sent to a window when a storage area it has access to is changed within the context of another document. interface StorageEvent extends Event { // ◇ Returns the key of the storage item being changed. readonly key: string | null; // ◇ Returns the new value of the key of the storage item whose value is being changed. readonly newValue: string | null; // ◇ Returns the old value of the key of the storage item whose value is being changed. readonly oldValue: string | null; // ◇ Returns the Storage object that was affected. readonly storageArea: Storage | null; // ◇ Returns the URL of the document whose storage item changed. readonly url: string; // @deprecated // 弃用的 initStorageEvent(type: string, bubbles?: boolean, cancelable?: boolean, key?: string | null, oldValue?: string | null, newValue?: string | null, url?: string | URL, storageArea?: Storage | null): void; } declare var StorageEvent: { prototype: StorageEvent; new(type: string, eventInitDict?: StorageEventInit): StorageEvent; // :type: string---对应map映射常量WindowEventHandlersEventMap : }; interface WindowEventHandlersEventMap { // : map映射常量 : // StorageEvent事件类型映射常量字符串type === "storage" : "storage": StorageEvent; // "afterprint": Event; "beforeprint": Event; "beforeunload": BeforeUnloadEvent; "gamepadconnected": GamepadEvent; "gamepaddisconnected": GamepadEvent; "hashchange": HashChangeEvent; "languagechange": Event; "message": MessageEvent; "messageerror": MessageEvent; "offline": Event; "online": Event; "pagehide": PageTransitionEvent; "pageshow": PageTransitionEvent; "popstate": PopStateEvent; "rejectionhandled": PromiseRejectionEvent; "unhandledrejection": PromiseRejectionEvent; "unload": Event; }
2.6、localStorage跨标签页通信的编程
2.6.1、页面中的localStorage相关脚本:
如上,因为localStorage的宿主为全局Window,所以对所有“标签页”均适用,代码可以直接与DOM互动。
通信的发射方,写“存储”触发storage事件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>页面一</title> </head> <body> <script> localStorage.name = "谢杰"; localStorage.age = 20 + Math.ceil(Math.random() * 10); // :来超:否则一旦缓存之后会“http 304” console.log(`localStorage的键值对已经设置: ${localStorage.length}组`); </script> </body> </html>
通信的接收方,监听storage事件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>页面二</title> </head> <body> <script> // window.onstorage = function(e) { console.log("修改的键为:", e.key); console.log("之前的值为:", e.oldValue); console.log("修改后的值为:", e.newValue); console.log(e.storageArea); console.log(e.url); } </script> </body> </html>
2.6.2、localStorage通信双方的相关脚本支持模块化导入
三、SharedWorker及其跨标签页通信
3.1、SharedWorker已共享的线程
◆ 谁在“共享”(或它是谁的属性、或它的宿主是谁):浏览器的App(或称其主程进窗体)
◆ 共享什么(或随被共享了):消息的服务(即SharedWorker),或简单的理解为消息收发的服务代码的js模块文件
◆ 共享的消息的介质(对谁“读和写”操作):是内存,它没有磁盘存储
◆ 共享的目标对象:浏览器当前App进程下的各种“窗口”(或称其GUI子进程),现在主流的就是上面提到的“标签页”Tabs.tab
◆ 可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通“专用”worker的接口,具有不同的全局作用域,SharedWorkerGlobalScope
3.2、SharedWorker线程跨标签页通信
◆ SharedWorker线程作为注册它的通信双方的“中间件”
◆ 通信的双方,都必须注册SharedWorker线程,才能互通“消息”
◆ 它是基于“消息”message的通信,通过它共享的MessagePort使得注册双方建立connect连接事件后,由它来“代理”互通消息,(不像BroadCast Channel,是通过BroadcastChannel通道的通信,通过MessageChannel通道的两端,通过MessageChannel消息通道来监听MessageEvent消息事件,让双方来互通),都是基于MessageEvent的事件模型
◆ 客户端connect连接(一旦接入)即触发事件
◆ 可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通“专用”worker的接口,具有不同的全局作用域,SharedWorkerGlobalScope
◆ 浏览器支持不太好(特别在移动端):◇ 在浏览器的App主进程上“争用”内存; ◇ 又不支持严格的MIME type检测; ◇ 又不贡献“存储”; ◇ 还容易一起“读写的共享冲突” ; ◇ 权限又过大,可能会影响到其它脚本的正常作业;等等......
◆ 一般不建议使用
◆ 使用Worker的话:一般建议使用:
“专用Worker线程”(拥有DedicatedWorkerGlobalScope专用的全局作用域)或“服务serviceWorker线程”
关于“线程安全”和“内容安全策略CSP”详见:此链接
3.3、SharedWorker线程的编程(略),参见此链接
喜欢的,就收藏并点个赞,鼓励我继续技术的原创写作及经验分享:
浏览器Disk Cache磁盘缓存及其协商缓存、及原生App和浏览器实现缓存的差异_pulledup的博客-CSDN博客