最近在看微服務方面的東西,看到關於 多個微服務頁面間通訊和數據共享的解決方案,發現了一些比較陌生的 API
,說是陌生其實專門拿出來也能說出個因此然來,也知道是個什麼東西,但就是不熟練,憑空想的話就很難能想到,看了一下以爲有些門道,因而索性擴展開來整理了一下javascript
Broadcast
也是「廣播」的意思,將信號廣播出去,容許其餘人接聽。css
此 API
容許同一原始域和用戶代理下的全部窗口、iFrames
等進行交互,屬於 同源通訊。也就是說,若是用戶打開了同一個網站的的兩個標籤窗口,若是網站內容發生了變化,那麼兩個窗口會同時獲得更新通知。html
使用的場景,如,用戶同時依次打開某個網站的幾個頁面,而後在其中一個頁面 A
進行登陸操做,那麼其餘的頁面就能夠經過 BroadcastChannel
收到來自頁面 A
的登陸狀態,從而可以完成多個頁面自動同步登陸狀態的目的。前端
// A頁面向外廣播信號
// 建立句柄
const cast = new BroadcastChannel('mychannel')
// data 能夠是任何 JS數據類型
const data = 'I\'m from Page A'
// 廣播信號
cast.postMessage(data)
// 關閉鏈接
cast.close()
複製代碼
// B頁面監聽同源下全部頁面發送出的「廣播」
// BroadcastChannel的參數,即channel號必須與想要監聽的廣播源相同,這裏是 mychannel
const cast = new BroadcastChannel('mychannel')
// 接收信號
cast.onmessage = function (e) {
console.log(e.data) // => I'm from Page A
}
// 關閉鏈接
cast.close()
複製代碼
用起來很順手,也沒什麼複雜的道道,BroadcastChannel
的初始化參數 channel
,能夠看作是一個廣播頻道,只要同源下加入這個頻道的頁面,都可以互相收發信號進行通訊,可是瀏覽器支持度很不樂觀,並且一直也都沒什麼進展,總感受未來某天就要嗝屁了html5
otherWindow.postMessage(message, targetOrigin, [transfer]);java
相比於 BroadcastChannel
來講, postMessage
明顯幸福多了,postMessage
支持 跨域通訊,瀏覽器支持度也秒殺 BroadcastChannel
,達到了徹底可在生產環境使用的地步,說明瀏覽器廠商對這個仍是很熱衷的。資本推進技術,沒毛病node
A
頁面經過 window.open
得到 B
頁面的句柄,向 B
頁面發送信號,並監聽 B
頁面回傳回來的信號mysql
<!-- A頁面 -->
<div id="msg"></div>
<script> window.onload = () => { // 獲取句柄 var opener = window.open('http://127.0.0.1:9001/b.html') // setTimeout 是爲了等到真正獲取到 opener的句柄再發送數據 setTimeout(() => { // 只對 域名爲 http://127.0.0.1:9001的頁面發送數據信號 opener.postMessage('red', 'http://127.0.0.1:9001'); }, 0) // 監遵從句柄頁面發送回來的數據信號 window.addEventListener('message', event => { if(event.origin === 'http://127.0.0.1:9001'){ document.getElementById('msg').innerHTML = event.data } }) } </script>
複製代碼
B
頁面接收 A
頁面的信號,並經過事件句柄反向對 A
頁面發送數據信號程序員
<div id="box">color from a.html</div>
<script type="text/javascript"> window.addEventListener('message', event => { // 經過origin屬性判斷消息來源地址 // 只有當數據信號來源於 http://127.0.0.1:9001的服務器才接收 if(event.origin === 'http://127.0.0.1:9001'){ // 獲取信息員的數據信號 document.getElementById('box').style.color = event.data // 經過 event.source向信號源反向發送數據 event.source.postMessage('got your color!', event.origin) } }) </script>
複製代碼
postMessage
用起來也比較簡單,稍微須要注意一下的是,因爲此 API
能夠跨域通訊,能力越大責任也就越大,因此涉及到安全性問題,通常在發送信號和接收信號的時候,都須要指定信號源以規避安全問題web
相比於 BroadcastChannel
的一侷限點是,postMessage
信號的傳遞有點受限,必需要有 其餘窗口的一個引用
,而後經過這個引用才能繼續下面一系列的操做,這種 引用
的來源有 iframe
的 contentWindow
屬性、執行 window.open
返回的窗口對象、或者是命名過或數值索引的window.frames
,場景有限,明顯不如 BroadcastChannel
直接指定一個 channel
號來得靈活
Web worker
分爲兩種:專用線程 dedicated web worker
、共享線程 shared web worker
Dedicated web worker
隨當前頁面的關閉而結束;這意味着 Dedicated web worker
只能被建立它的頁面訪問;與之相對應的 Shared web worker
能夠被多個頁面訪問(包括多個標籤頁和 iframe
),不過這些頁面必須是同源的,即 Shared web worker
支持的是 同源通訊
下面是一個 SharedWorker
// worker.js
// 共享的數據
let shareData = 0
// 監聽主線程的鏈接
onconnect = function(e) {
const port = e.ports[0]
port.onmessage = function(e) {
if (e.data === 'get') {
// 向鏈接的主線程發送信號
port.postMessage(shareData)
} else {
// 將主線程發來的數據設置爲 worder內的 共享數據
shareData = e.data
}
}
}
複製代碼
A
頁面設置 SharedWorker
中的數據字段
<input type="text" id="textInput" />
<input type="button" value="設置共享數據" />
<script> const worker = new SharedWorker('worker.js') const inputEle = document.querySelector('#textInput') inputEle.onchange = () => { console.log('Message posted to worker') // 向 worker 發送數據信號 worker.port.postMessage(inputEle.value) } </script>
複製代碼
B
頁面獲取 SharedWorker
中的數據字段
<div id="result"></div>
<button id="btn">獲取 SharedWorker中的共享數據</button>
<script> const worker = new SharedWorker('worker.js') var result = document.querySelector('#result') // 發送獲取獲取 SharedWorder 中共享數據的請求 document.getElementById('btn').addEventListener('click' , () => { // 向 worker發送信號 worker.port.postMessage('get') }) // 接收從 SharedWorder發送來的共享的數據 worker.port.onmessage = e => { console.log('Message received from worker') // 在頁面上顯示獲取到的 worker共享數據 result.textContent = e.data } </script>
複製代碼
最終,在 A
頁面中設置的值,或被 B
頁面獲取到
worker.js
這個文件被 A
頁面和 B
頁面分別加載,但卻能夠共享數據,相似於 單例模式,雖然使用了 new
操做符,但最後兩個頁面獲取到的東西倒是同樣的
以前對於這個 SharedWorker
並不熟悉,只知道大概是幹什麼用的,但不知道具體細節,一直覺得這個東西能夠像 BroadcastChannel
或 postMessge
同樣,在一個頁面發送信號,另一個頁面就能夠即時自動接收,就像是兩我的打電話,一我的說話,另一我的什麼都須要作就能夠立馬聽到,可是如今弄完了才發現並非這樣
B
頁面確實能夠獲取到 A
頁面設置的數據,但這種獲取是須要主動的操做,不像是打電話,倒像是存儲,一個頁面在公共區域存了一個數據,另一個頁面想要了,須要主動去獲取,我是感受這個東西可能並非適合於頁面通訊,固然了,SharedWorker
原本就不是用於頁面通訊的,因此沒有預期的效果也是情有可原的
另外,在測試 SharedWorker
的時候,碰到了幾個坑,這裏提一下:
當頁面第一次加載完了 worker.js
後,後續再修改 worker.js
這個文件,而後刷新頁面,會發現 worker.js
其實並無變化,仍是修改以前的那一個,這是由於 worker.js
被瀏覽器緩存了,強制刷新瀏覽器也沒用
一個解決方案就是給 worker.js
文件加上 hash
,例如:
const worker = new SharedWorker('worker.js?hash=v1')
複製代碼
worker.js
全名稱要一致根據上面的方法,頁面就能更新 worker.js
了,但還須要注意的是,若是想要 A
頁面和 B
頁面(或者更多的頁面) new
出來的 worker
是同一個,也就是說能夠共享數據,那麼這些頁面加載的 worker.js
不只須要是同一個文件,並且全名稱也必需要徹底同樣,包括 hash
值
下面這種狀況,A
頁面和 B
頁面就沒法進行數據共享,由於它們加載的 worker.js
的 hash
值不一樣,單例模式沒法成立:
// A 頁面,hash值爲 v111
const worker = new SharedWorker('worker.js?hash=v111')
// ...
// B頁面,hash值爲 v222
const worker = new SharedWorker('worker.js?hash=v222')
複製代碼
相比於 dedicated web worker
來講,shared web worker
的瀏覽器支持度明顯弱了一截,多是由於現今 dedicated web worker
的應用場景要比 shared web worker
多上不少 另外,微軟系的 IE
瀏覽器以及 Edge
都徹底不支持此特性,緣由是微軟認爲此 API
存在安全隱患,估計之後也不太可能支持了
Local Storage
用於存儲數據,但因爲存在 storage
這個事件,因此也能夠對存儲狀態進行監聽,從而達到頁面間通訊的目標
// A頁面
window.onstorage = function(e) {
console.log(e.newValue); // previous value at e.oldValue
};
// B頁面
localStorage.setItem('key', 'value');
複製代碼
一開始,我一直覺得同一個頁面是能夠本身監聽本身的 storage
事件,誰知試了半天都沒用,MDN
文檔也翻了好幾遍也沒找出來緣由,大眼瞪小眼了半天,後來終於在網上找到緣由,原來 Chrome
、Edge
等瀏覽器下的這個 storage
事件必須由其餘同源頁面觸發,同一個頁面是沒法本身監聽本身的 storage
事件的(好像 FireFox
能夠本身監聽本身?沒測過不肯定),這種設計簡直就差點沒在本身身上寫個 支持頁面間通訊 的字符串了
WebSocket
是 HTML5
開始提供的一種在單個 TCP
鏈接上進行全雙工通信的協議,經常使用的場景是即時通信
想要使用此項技術,必須瀏覽器端和服務器端都支持,node.js
的 websocket
解決方案,比較知名的是 socket.io
服務器端
// index.js
const server = require('http').createServer()
const io = require('socket.io')(server)
io.on('connection', socket => {
socket.on('clientMsg', data => {
// broadcast 直接廣播出去,除了發送者外,其餘全部鏈接者均可以接收到
socket.broadcast.emit('serverMsg', data)
})
})
server.listen(3000)
複製代碼
上面是服務器端的所有代碼,須要安裝 socket.io
這個包,爲了方便演示,因此去除了其餘沒必要要的邏輯,主要功能就是開啓一個 socket
鏈接,能接收並廣播消息,相似於一個聊天室服務器
客戶端代碼:
<!-- client.html -->
<!-- 消息列表 -->
<ul id="ul"></ul>
<input type="text" id="textInput" />
<button onclick="btnClick()">發送</button>
<script> const socket = io('http://localhost:3000') // 接收服務器發過來的消息 socket.on('serverMsg', data => { addLi(`${data.id}: ${data.msg}`) }) const ul = document.getElementById('ul') const textInput = document.getElementById('textInput') const id = new Date().getTime() // 向服務器發送消息 function btnClick() { socket.emit('clientMsg', { msg: textInput.value, id }) textInput.value = '' } function addLi(text) { const li = document.createElement('li') li.innerText = text ul.appendChild(li) } </script>
複製代碼
上面是客戶端的主體代碼,爲了能與服務器端配合使用,須要在頁面上引入 socket.io.js
這個文件,從而開啓瀏覽器端的 websocket
:
<script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.js"></script>
複製代碼
socket.io
服務器啓動後,在本地打開客戶端頁面 client.html
,多打開幾個標籤頁,每一個 client.html
就是一個消息接收者,發送的消息,其餘頁面都能即時接收
websocket
技術已經很成熟了,徹底能夠用於生產環境,除了稍微有點學習成本外,使用起來也沒什麼難度,也沒什麼使用限制,應用場景普遍,不過若是僅僅是頁面間的通訊,用這個東西彷佛就有點殺雞用牛刀的感受,畢竟不管如何一個專門的 websocket
服務器是跑不了的
和 LocalStorage
同樣,indexDB
也用於數據存儲,不過更「專業」
IndexedDB
是一種低級 API
,用於客戶端存儲大量結構化數據(包括 文件、blobs
),該API使用索引來實現對該數據的高性能搜索,區別於 LocalStorage
只能存儲字符串,IndexedDB
能夠存儲 JS
全部的數據類型,包括 null
、undefined
等,是 HTML5
規範裏新出現的 API
IndexedDB
是一種使用瀏覽器存儲大量數據的方法.它創造的數據能夠被查詢,而且能夠離線使用。IndexedDB
對於那些須要存儲大量數據,或者是須要離線使用的程序是很是有效的解決方法
const request = indexedDB.open('dbBox')
request.onsuccess = function(e) {
console.log('成功打開 IndexDB')
const myDB = e.target.result
// 開啓一個讀寫的事物
const transaction = myDB.transaction('person', 'readwrite')
// 拿到 person表格的句柄
const store = transaction.objectStore('person')
// 向 person表格中添加兩條數據
store.add({name: 'jane', email:'jane@gmail.com'})
store.add({name: 'kangkang', email:'kangkang@gmail.com'})
// 全部的數據添加成功,觸發事務的 oncomplete事件
transaction.oncomplete = function(event) {
// 從新開啓一個查詢事務
const getResult = myDB.transaction('person', 'readwrite').objectStore('person').get('jane')
getResult.onsuccess= e => {
console.log('查詢結果:', e.target.resule)
// => {name: 'jane', email:'jane@gmail.com'}
}
}
}
// 在數據庫首次 open數據庫,或數據庫版本更新時會觸發此事件
request.onupgradeneeded = function(e) {
const db = e.target.result
// 若是不存在 person數據表
if (!db.objectStoreNames.contains('person')) {
// 新建數據表 person
const objectStore = db.createObjectStore('person', {
// 指定主鍵,相似於 primaryKey,後續查找數據庫,就是經過這個主鍵的值,進行查找的
keyPath: "name"
})
// 建立數據表字段 name
objectStore.createIndex("name", "name", {
//指定能夠被索引的字段,unique字段用於指定是否惟一
unique: true
})
// 建立數據表字段 phone
objectStore.createIndex("phone", "phone", {
unique: false
})
}
}
複製代碼
上述簡單示例包括了 鏈接數據庫、建立表、建立表字段結構、添加數據、查詢數據等操做,註釋得比較清楚,就很少加解釋了
作完上述操做後, F12
打開瀏覽器的控制檯,選中 Application
選項卡,選中 IndexeddDB
,展開,便可看到存儲的數據
IndexDB
做爲本地存儲 API
,沒有全局監聽事件,因此沒法用於頁面通訊,但可用於數據共享,此API
涉及到較多的專屬名詞和概念,可能對於純正的前端來講不太好理解,不過只要是計算機專業出身的,對於數據庫的基本概念仍是可以理解的,本質上也沒什麼可說的,就是一個簡化版的本地數據庫
對於 indexedDB
,瀏覽器桌面版的支持度仍是不錯的
一樣是瀏覽器數據庫的一種,IndexedDB
能夠看作是 NoSql
數據庫,操做指令(增刪改查等)的調用方式更偏向於 「前端化」,Web SQL
則更像是 關係型數據庫
,不管是諸多概念的定義,仍是操做指令都跟後端的一些關係型數據庫,例如 mysql
、sqlserver
等更像,相比於 IndexexDB
,Web SQL
更像是一個數據庫
另外,Web SQL
數據庫 API
並非 HTML5
規範的一部分,可是它是一個獨立的規範,引入了一組使用 SQL
操做客戶端數據庫的 APIs
,不過奇怪的是,這東西好像不是持久型存儲,頁面刷新後,以前存儲的數據,包括數據庫、數據表就徹底 drop
了
// 打開一個名爲 mydb 的數據庫,若是不存在則建立,並指定版本號爲 1.0,數據庫的描述文本爲 Test DB,大小限制在 2 * 1024 * 1024
var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024)
var msg
// 開啓事務
db.transaction(function (tx) {
// 建立一個名爲 LOGS的表,而且此表存在id 和 log兩個字段
tx.executeSql('CREATE TABLE IF NOT EXISTS LOGS (id unique, log)')
// 向數據表中插入兩條數據
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "菜鳥教程")')
tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "www.runoob.com")')
})
// 開始事務
db.transaction(function (tx) {
// 從 LOGS 數據表查詢出全部的數據
tx.executeSql('SELECT * FROM LOGS', [], function (tx, results) {
const len = results.rows.length
// 格式化查詢出的結果
const rst = Array(len).fill(1).map((v, index) => results.rows.item(index))
console.log('查詢到的數據列表爲:', rst)
}, null)
});
複製代碼
作完上述操做後, F12
打開瀏覽器的控制檯,選中 Application
選項卡,選中 Web SQL
,展開,便可看到存儲的數據
能夠看到,上述操做出現了不少 sql
,對於不熟悉數據庫的人來講至關於要多學一門 sql
語言,雖然若是隻是學習基本使用也沒什麼難度,但終歸對前端程序員不友好,另外,關係型數據庫出如今靈活到上天的 JavaScript
世界,彷佛有種不太和諧的感受,因而對於 Web Sql
的定論是:
IndexedDB
是WebSQL
數據庫的取代品,W3C
組織在2010
年11
月18
日廢棄了webSql
。IndexedDB
和WebSQL
的不一樣點在於WebSQL
是關係型數據庫(複雜)IndexedDB
是key-value
型數據庫(簡單好使).
呵呵,在沒看到這句話以前,我一直覺得 WebSql
更先進,該被替換掉的是 IndexedDB
,沒想到皁滑造化弄人,人生到處有驚喜
只是隨便從微服務方面知識中看到的一個點,擴展開來就是一篇文章,果真是學無止境啊。之前上大學的時候,天天時間多的是,因而天天都在歡快地學習,新知識出來一個學一個,不亦樂乎,如今工做了,天天業務代碼都寫不完,然而仍是要擠出時間來學習新知識,關注了一大堆的技術公衆號,天天推送的文章看都看不完,對技術瞭解得越多就越感受技術的一望無際,只想感慨一句,求求大家別再弄新東西出來了,老子學不下去了 活到老學到老。