1 BrodcastChanner
概念
BroadcastChannel
接口表示給定源的任何瀏覽上下文都可以訂閱的命名頻道。它允許同源的不同瀏覽器窗口、標簽頁、frame 或者 iframe 下的不同文檔之間相互通信。消息通過message事件進行廣播,該事件在偵聽該頻道的所有BroadcastChannel
對象上觸發,發送消息的對象除外。
何為源?
- 由用于訪問它的URL的方案(協議)、主機名(域名)和端口定義。只有當協議、主機和端口都匹配時,兩個對象才具有相同的源。
何為瀏覽上下文?
- 瀏覽器(browser)展示文檔的環境。在現代瀏覽器中,通常是一個標簽頁(tab),也可能是一個窗體(window)或只是頁面的一部分。
構成
- 構造函數:BroadcastChannel
- 實例屬性:name,頻道名稱
- 實例方法:
- 發送消息:postMessage
- 關閉頻道對象:close
- 事件:
- 收到消息時觸發:message,也可以使用onmessage屬性訪問。
使用
1.創建一個鏈接到命名頻道的對象
const broad = new BroadcastChannel('moment')
2.綁定事件,以便接收消息
broad.onmessage = function (e) {const { value } = e.dataconsole.log('value::',value)
}
3.按需進行消息發送?
broad.postMessage({value: 'Hi,my baby'
})
代碼落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const index = e.target.dataset.indexconst imgUrl = avatarList[index]useBroadSendMsg(imgUrl)img.src = imgUrl// console.log(imgUrl)})const broad = new BroadcastChannel('moment')broad.onmessage = function (e) {const { value } = e.dataimg.src = value}const useBroadSendMsg = (data) => {setTimeout(() => {broad.postMessage({value: data})}, 500)}</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const index = e.target.dataset.indexconst imgUrl = avatarList[index]useBroadSendMsg(imgUrl)img.src = imgUrl// console.log(imgUrl)})const useBroadSendMsg = (data) => {setTimeout(() => {broad.postMessage({value: data})}, 500)}const broad = new BroadcastChannel('moment')broad.onmessage = function (e) {const { value } = e.dataimg.src = value}</script>
</html>
/* common.css */
* {padding: 0;margin: 0;box-sizing: border-box;
}ul {list-style: none;
}.avatar-list {margin: 20px auto;width: 300px;display: flex;justify-content: space-around;
}
.user-info {text-align: center;
}
.avatar {width: 100px;height: 100px;border-radius: 10px;
}.avatar-item {width: 80px;height: 80px;
}
.avatar-item:hover {cursor: pointer;
}
.avatar-item:nth-child(1) {background-image: url('../images/1.jpg');background-size: 100% 100%;
}.avatar-item:nth-child(2) {background-image: url('../images/2.jpg');background-size: 100% 100%;
}.avatar-item:nth-child(3) {background-image: url('../images/3.png');background-size: 100% 100%;
}
2 ServiceWorker
概念
ServiceWorker
接口提供了對 service worker 的引用。各個瀏覽上下文(例如頁面、worker 等)可以與相同的 service worker 相關聯,每個瀏覽上下文都可以通過唯一的ServiceWorker
對象訪問。
Service worker 運行在 worker 上下文:因此它無法訪問 DOM,相對于驅動應用的主 JavaScript 線程,它運行在其他線程中,所以不會造成阻塞。
何為worker上下文?
- 在瀏覽器環境中,Web Worker 是一種可以在瀏覽器后臺運行腳本的機制,它允許在主線程之外創建獨立的線程來執行 JavaScript 代碼。Service Worker 就是基于 Web Worker 技術實現的,它運行在一個特殊的 worker 上下文環境中。
何為無法訪問DOM?
- 由于 Service Worker 運行在獨立的 worker 上下文中,與主頁面的 DOM 環境是隔離的。
- 這意味著 Service Worker 不能直接對頁面上的元素進行修改,比如改變文本內容、修改樣式等。如果需要更新頁面內容,Service Worker 可以通過向主頁面發送消息,由主頁面的 JavaScript 代碼來完成 DOM 操作。
運行在其他線程中?
- 在瀏覽器中,主 JavaScript 線程負責處理用戶交互、頁面渲染和執行主頁面的 JavaScript 代碼。如果主線程執行了耗時的操作,如大量的計算或長時間的網絡請求,會導致頁面卡頓,用戶體驗變差。
- 而 Service Worker 運行在獨立于主 JavaScript 線程的其他線程中,它有自己的執行棧和事件循環。這意味著 Service Worker 中的代碼執行不會阻塞主頁面的渲染和交互。
既然是獨立出來的,那如何操作呢?
self
- 在 Service Worker 中,
self
是一個全局對象,它類似于主頁面中的window
對象。self
代表當前的 Service Worker 實例本身,通過它可以訪問 Service Worker 的各種方法和屬性,也可以監聽 Service Worker 生命周期中的各種事件。
self.clients
self.clients
是一個Clients
對象,它代表了當前與 Service Worker 關聯的所有客戶端(通常是瀏覽器的標簽頁或窗口)。通過self.clients
可以對這些客戶端進行管理和操作,比如獲取客戶端列表、向客戶端發送消息等。- 可以通過
client.postMessage()
方法向指定的客戶端發送消息。
代碼落地
// service-worker.js
// 監聽消息事件
self.addEventListener('message', function (event) {// 獲取發送消息的客戶端 IDconst senderId = event.source.id// 獲取所有受控的客戶端self.clients.matchAll().then(function (clients) {clients.forEach(function (client) {// 避免將消息發送回發送者if (client.id !== senderId) {// 向其他客戶端發送消息client.postMessage(event.data)}})})
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')if ('serviceWorker' in navigator) {window.addEventListener('load', function () {navigator.serviceWorker.register('./service-worker.js').then(function (registration) {console.log('Service Worker registered successfully:', registration)// 監聽來自Service Worker 的消息navigator.serviceWorker.addEventListener('message', function (event) {const { type, value } = event.dataif (type === 'change-avatar') {img.src = value}})// DOM操作ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 發送消息navigator.serviceWorker.controller.postMessage({type: 'change-avatar',value: imgUrl})img.src = imgUrl}})}).catch(function (error) {console.error('Service Worker registration failed:', error)})})}</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')if ('serviceWorker' in navigator) {window.addEventListener('load', function () {navigator.serviceWorker.register('./service-worker.js').then(function (registration) {console.log('Service Worker registered successfully:', registration)// 監聽來自 Service Worker 的消息navigator.serviceWorker.addEventListener('message', function (event) {const { type, value } = event.dataif (type === 'change-avatar') {img.src = value}})// 發送消息的按鈕點擊事件// DOM操作ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 發送消息navigator.serviceWorker.controller.postMessage({type: 'change-avatar',value: imgUrl})img.src = imgUrl}})}).catch(function (error) {console.error('Service Worker registration failed:', error)})})}</script>
</html>
?
3 postMessage
概念
**window.postMessage()**方法可以安全地實現跨源通信。通常,對于兩個不同頁面的腳本,只有當執行它們的頁面位于具有相同的協議(通常為 https),端口號(443 為 https 的默認值),以及主機 (兩個頁面的模數Document.domain設置為相同的值) 時,這兩個腳本才能相互通信。
從廣義上講,一個窗口可以獲得對另一個窗口的引用(比如
targetWindow = window.opener
),然后在窗口上調用targetWindow.postMessage()
方法分發一個MessageEvent消息。接收消息的窗口可以根據需要自由處理此事件。
怎么拿到另一個窗口的引用?
其他窗口的一個引用,比如 iframe 的 contentWindow 屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。
代碼落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /><link rel="stylesheet" href="./c-post.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul><div class="edit-box"><button class="btn1">打開other頁面</button></div></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')let targetWindow = nullconst btn1 = document.querySelector('.btn1')const btn2 = document.querySelector('.btn2')btn1.onclick = () => {targetWindow = window.open('./other.html')}ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]targetWindow.postMessage({ value: imgUrl }, 'http://127.0.0.1:5500')img.src = imgUrl}})</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /><link rel="stylesheet" href="./c-post.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')const btn = document.querySelector('button')window.addEventListener('message', function (event) {if (event.origin == 'http://127.0.0.1:5500') {const { value } = event.dataimg.src = value}})</script>
</html>
?
4 Storage
當存儲區域(localStorage 或 sessionStorage)被修改時,將觸發 storage 事件。
對于全局localstorage,知道的是:
- 鍵值對形式
- 存儲只能存儲字符串內容,復雜類型數據需要使用JSON做序列化處理;
- 不會隨頁面刷新而丟失
使用
借助storage事件——在當前修改的這個頁面不會觸發,也就是行為發生的頁面的storage事件不會觸發。
三個常用字段:
- key:"鍵值對"
- newValue:"新值"
- oldValue:"舊值"
代碼落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 存儲數據localStorage.setItem('avatar', imgUrl)img.src = imgUrl}})window.addEventListener('storage', (e) => {const newUrl = e.newValueimg.src = newUrl})</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">個簽:積善者必有余慶</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 存儲數據localStorage.setItem('avatar', imgUrl)img.src = imgUrl}})window.addEventListener('storage', (e) => {const newUrl = e.newValueimg.src = newUrl})</script>
</html>