瀏覽器跨 Tab 視窗通信原理及應用實踐

来源:https://www.cnblogs.com/coco1s/archive/2023/11/28/17861360.html
-Advertisement-
Play Games

最近,相信大家一定被這麼個動效給刷屏了: 以至於,基於這個效果的二次創作層出不窮,眼花繚亂。 基於跨視窗通信的彈彈球: 基於跨視窗通信的 Flippy Bird: 我也嘗試製作了一個跨 Tab 視窗的 CSS 動畫聯動,效果如下: 代碼不多,核心代碼 200 行,感興趣的可以戳這裡:Github - ...


最近,相信大家一定被這麼個動效給刷屏了:

以至於,基於這個效果的二次創作層出不窮,眼花繚亂。

基於跨視窗通信的彈彈球:

基於跨視窗通信的 Flippy Bird:

我也嘗試製作了一個跨 Tab 視窗的 CSS 動畫聯動,效果如下:

代碼不多,核心代碼 200 行,感興趣的可以戳這裡:Github - broadcastAnimation

當然,本文的核心不是去一一剖析上面的效果具體的實現方式,而是講講其中比較關鍵的一個技術點:

而是應用如何在多視窗下進行互相通信

所謂多視窗下進行互相通信,是指在瀏覽器中,不同視窗(包括不同標簽頁、不同瀏覽器視窗甚至不同瀏覽器實例)之間進行數據傳輸和通信的能力。

當然,本文我們探討的是純前端的跨 Tab 頁面通信,在非純前端的方式下,我們可以藉助諸如 Web Socket 等方式,藉由後端這個中間載體,進行跨頁面通信。

因此,本文我們更多的重心將放在,如何基於純前端技術,實現多視窗下進行互相通信。

為了實現跨視窗通信,它應該需要具備以下能力:

  1. 數據傳輸能力:能夠將數據從一個視窗發送到另一個視窗,以及接收來自其他視窗的數據。
  2. 實時性:能夠實現實時或近實時的數據傳輸,以便及時更新不同視窗的內容。
  3. 安全性:確保通信過程中的數據安全,防止惡意竊取或篡改通信數據。當然,這個不是本文討論的重點,但是是實際應用中不應該忽視的一個重點。

方式一:Broadcast Channel()

Broadcast Channel 是一個較新的 Web API,用於在不同的瀏覽器視窗、標簽頁或框架之間實現跨視窗通信。它基於發佈-訂閱模式,允許一個視窗發送消息,並由其他視窗接收。

其核心步驟如下:

  1. 創建一個 BroadcastChannel 對象:在發送和接收消息之前,首先需要在每個視窗中創建一個 BroadcastChannel 對象,使用相同的頻道名稱進行初始化。
  2. 發送消息:通過 BroadcastChannel 對象的 postMessage() 方法,可以向頻道中的所有視窗發送消息。
  3. 接收消息:通過監聽 BroadcastChannel 對象的 message 事件,可以在視窗中接收到來自其他視窗發送的消息。

同時,Broadcast Channel 遵循瀏覽器的同源策略。這意味著只有在同一個協議、主機和埠下的視窗才能正常進行通信。如果視窗不滿足同源策略,將無法互相發送和接收消息。

因為有同源限制,我們需要起一個服務,這裡我基於 Vite 快速起了一個 Vue 項目,簡單的基於 .vue 文件下進行一個演示。

其核心代碼非常簡單:

<template>
  <div class="g-container" id="j-main">
    // ...  
  </div>
</template>

<script>
import { onMounted } from 'vue';

export default {
  setup() {
    function createBroadcastChannel() {
      broadcastChannel = new BroadcastChannel('broadcast');
      broadcastChannel.onmessage = handleMessage;
    }

    function sendMessage(data) {
      broadcastChannel.postMessage(data);
    }

    function handleMessage(event) {
        console.log('接收到 event', event);
        // TODO: 處理接收到信息後的邏輯
    }

    function resizeEventBind() {
      window.addEventListener('resize', () => {
         const pos = getCurPos();
         sendMessage(pos);
       });
    }

    // 計算當前元素距離顯示器視窗右上角的距離
    function getCurPos() {
      const barHeight = window.outerHeight - window.innerHeight;
      const element = document.getElementById('j-main');
      const rect = element.getBoundingClientRect();

      // 獲取元素相對於屏幕左上角的 X 和 Y 坐標
      const x = rect.left + window.screenX; // 元素左邊緣相對於屏幕左邊緣的距離
      const y = rect.top + window.screenY + barHeight;// 元素頂部邊緣相對於屏幕頂部邊緣的距離

      return [x, y];
    }
    
    onMounted(() => {
      createBroadcastChannel();
      resizeEventBind();
    });

    return {};
  }
};
</script>

<style lang="scss"></style>

這裡,我們的核心邏輯在於:

  • createBroadcastChannel() 函數用於創建一個 BroadcastChannel 對象,並設置消息處理函數。
  • sendMessage(data) 函數用於向 BroadcastChannel 發送消息。
  • handleMessage(event) 函數用於處理接收到的消息。
  • resizeEventBind() 函數用於監聽視窗大小變化事件,併在事件發生時獲取當前元素的位置信息,並通過 sendMessage() 函數發送位置信息到 BroadcastChannel。
  • getCurPos() 函數用於計算當前元素相對於顯示器視窗右上角的距離。

onMounted() 生命周期鉤子中,調用了 createBroadcastChannel()resizeEventBind() 函數,用於在組件掛載後執行相關的初始化操作。

這樣,當我們同時打開兩個視窗,移動其中一個視窗,就可以向另外一個視窗發生當前視窗希望傳遞過去的信息,在本例子中就是 #j-main 元素距離顯示器右上角的距離。

假設 #j-main 只是一個在瀏覽器正中心矩形,我們同時打開兩邊的控制台,看看會發生什麼:

可以看到,如果我們同時打開兩個一個的頁面,當觸發右邊頁面的 Resize,左邊的頁面會收到基於 broadcastChannel.onmessage = handleMessage 接收到的信息,反之同理。

而一個完整的 Event 信息如下:

譬如,傳遞過來的信息放在 data 屬性內、同時也可以獲取當前的的 Broadcast Name 等。

基於 BroadcastChannel,就可以實現每個 Tab 內的核心信息互傳, 可以得知當前線上設備數,再基於這些信息去完成我們想要的動畫、交互等效果。

這裡的核心點,還是:

  1. 數據向其他 Tab 頁面傳遞的能力
  2. Tab 頁面接受其他頁面傳遞過來的數據的能力

其本質就是一個數據共用池子。

方式二:SharedWorker API

好,介紹完 Broadcast Channel(),我們再來看看 SharedWorker API。

SharedWorker API 是 HTML5 中提供的一種多線程解決方案,它可以在多個瀏覽器 TAB 頁面之間共用一個後臺線程,從而實現跨頁面通信。

與其他 Worker 不同的是,SharedWorker 可以被多個瀏覽器 TAB 頁面共用,且可以在同一功能變數名稱下的不同頁面之間建立連接。這意味著,多個頁面可以通過 SharedWorker 實例之間的消息傳遞,實現跨 TAB 頁面的通信。

它的實現與上面的 Broadcast Channel 非常類似,我們來看一看實際的代碼:

<template>
  <div class="g-container" id="j-main">
    // ...  
  </div>
</template>

<script>
import { onMounted } from 'vue';

export default {
  setup() {
    // 創建一個 SharedWorker 對象
    let worker;
    
    function initWorker() {
      // 創建一個 SharedWorker 對象
      worker = new SharedWorker('/shared-worker.js', 'tabWorker');

      // 監聽消息事件
      worker.port.onmessage = function (event) {
        console.log('接收到 event', event);
        handleMessage(event);
      };
    }
    
    function handleMessage(data) {
      // TODO: 處理接收到信息後的邏輯
    }

    function sendMessage(data) {
      // 發送消息
      worker.port.postMessage(data);
    }

    function resizeEventBind() {
      window.addEventListener('resize', () => {
        const pos = getCurPos();
        sendMessage(pos);
      });
    }

    function getCurPos() {
      const barHeight = window.outerHeight - window.innerHeight;
      const element = document.getElementById('j-main');
      const rect = element.getBoundingClientRect();

      // 獲取元素相對於屏幕左上角的 X 和 Y 坐標
      const x = rect.left + window.screenX; // 元素左邊緣相對於屏幕左邊緣的距離
      const y = rect.top + window.screenY + barHeight;// 元素頂部邊緣相對於屏幕頂部邊緣的距離

      return [x, y];
    }
    
    onMounted(() => {
      initWorker();
      resizeEventBind();
    });

    return {};
  }
};
</script>

<style lang="scss"></style>

簡單描述一下,上面也說了,跨 Tab 頁通信的核心在於數據向外的發送與接收的能力:

  1. initWorker() 方法中,使用 worker = new SharedWorker('/shared-worker.js', 'tabWorker') 創建了一個 SharedWorker 後面每一個被打開的同域瀏覽器 TAB 頁面,都是共用這個 Worker 線程,從而實現跨頁面通信
  2. 基於 worker.port.postMessage(data)實現數據的傳輸
  3. 基於 worker.port.onmessage = function() {} 實現傳輸數據的監聽

當然,上面有引入一個 /shared-worker.js,這個是需要額外定義的,一個極簡版本的代碼如下:

//shared-worker.js
const connections = [];

onconnect = function (event) {
  var port = event.ports[0];
  connections.push(port);

  port.onmessage = function (event) {
    // 接收到消息時,向所有連接發送該消息
    connections.forEach(function (conn) {
      if (conn !== port) {
        conn.postMessage(event.data);
      }
    });
  };

  port.start();
};

簡單解析一下,下麵對其進行解析:

  1. 上面的代碼中,定義了一個數組 connections,用於存儲與 SharedWorker 建立連接的各個頁面的埠對象;
  2. onconnect 是事件處理程式,當有新的連接建立時會觸發該事件;
  3. 在 onconnect 函數中,通過 event.ports[0] 獲取到與 SharedWorker 建立的連接的第一個埠對象,並將其添加到 connections 數組中,表示該頁面與共用 Worker 建立了連接。
  4. 在連接建立後,為每個埠對象設置了 onmessage 事件處理程式。當埠對象接收到消息時,會觸發該事件處理程式。
  5. 在 onmessage 事件處理程式中,通過遍歷 connections 數組,將消息發送給除當前連接埠對象之外的所有連接。這樣,消息就可以在不同的瀏覽器 TAB 頁面之間傳遞。
  6. 最後,通過調用 port.start() 啟動埠對象,使其開始接收消息。

總而言之,shared-worker.js 腳本創建了一個共用 Worker 實例,它可以接收來自不同頁面的連接請求,並將接收到的消息發送給其他連接的頁面。通過使用 SharedWorker API,實現跨 TAB 頁面之間的通信和數據共用

同理,我們來看看基於 Worker 的數據傳輸效果,同樣是簡化 DEMO,當 Resize 視窗時,向另外一個視窗發送當前視窗下 #j-main 元素的坐標:

可以看到,如果我們同時打開兩個一個的頁面,當觸發右邊頁面的 Resize,左邊的頁面會利用 worker.port.onmessage = function() {} 收到基於 worker.port.postMessage(data) 發送的信息,反之同理。

而一個完整的 Event 信息如下:

可以看到,在 SharedWorker 方式中,傳輸數據與 Broadcast Channel 是一樣的,都是利用 Message Event。簡單對比一下:

  1. SharedWorker 通過在多個Tab頁面之間共用相同的 Worker 實例,方便地共用數據和狀態,SharedWorker 需要多定義一個 shared-worker.js;
  2. Broadcast Channel 通過向所有訂閱同一頻道的 Tab 頁面廣播消息,實現廣播式的通信。

相容性方面,到今天(2023-11-26),broadcast Channel 看著是相容性更好的方式:

另外,需要註意的是,兩個方法都使用了 postMessage 方法。window.postMessage() 方法可以安全地實現跨源通信。並且,本質上而言,單獨使用 postMessage 就可以實現跨 Tab 通信。

但是,單獨使用 postMessage 適合簡單的點對點通信。在更複雜的場景中,Broadcast Channel 和 SharedWorker 提供更強大的機制,可簡化通信邏輯,有更廣泛的通信範圍和生命周期管理。Broadcast Channel 的通信範圍是所有訂閱該頻道的視窗,而 SharedWorker 可在多個視窗之間共用狀態和通信。

方式三:localStorage/sessionStorage

OK,最後一種跨 Tab 視窗通信的方式是利用 localStoragesessionStorage 本地化存儲 API 以及的 storage 事件。

與上面 Broadcast Channel、SharedWorker 稍微不同的地方在於:

  1. localStorage 方式,利用了本地瀏覽器存儲,實現了同域下的數據共用;
  2. localStorage 方式,基於 window.addEventListener('storage', function(event) {})事件實現了 localStore 變化時候的數據監聽;

簡單看看代碼:

<template>
  <div class="g-container" id="j-main">
    // ...  
  </div>
</template>

<script>
import { ref, reactive, computed, onMounted } from 'vue';

export default {
  setup() {
    function initLocalStorage() {
      let tabArray = JSON.parse(localStorage.getItem('tab_array'));
      if (!tabArray) {
        const tabIndex = 1;
        id = tabIndex;
        localStorage.setItem('tab_array', JSON.stringify([tabIndex]));
      } else {
        const tabIndex = tabArray[tabArray.length - 1] + 1;
        id = tabIndex;
        const newTabArray = [...tabArray, tabIndex];
        localStorage.setItem('tab_array', JSON.stringify(newTabArray));
      }
    }

    function setLocalStorage(data) {
      localStorage.setItem(`tab_index_${id}`, JSON.stringify(data));
    }

    function handleMessage(data) {
      const rArray = JSON.parse(data);
      remoteX.value = rArray[0];
      remoteY.value = rArray[1];
    }

    function resizeEventBind() {
      window.addEventListener('resize', () => {
         const pos = getCurPos();
         setLocalStorage(pos);
      });
 
      window.addEventListener('storage', (event) => {
        console.log('localStorage 變化了!', event);
        console.log('鍵名:', event.key);
        console.log('變化前的值:', event.oldValue);
        console.log('變化後的值:', event.newValue);
        handleMessage(event.newValue);
      });
    }

    function getCurPos() {
      const barHeight = window.outerHeight - window.innerHeight;
      const element = document.getElementById('j-main');
      const rect = element.getBoundingClientRect();

      // 獲取元素相對於屏幕左上角的 X 和 Y 坐標
      const x = rect.left + window.screenX; // 元素左邊緣相對於屏幕左邊緣的距離
      const y = rect.top + window.screenY + barHeight;// 元素頂部邊緣相對於屏幕頂部邊緣的距離

      return [x, y];
    }
    
    onMounted(() => {
      initLocalStorage();
      resizeEventBind();
    });

    return {};
  }
};
</script>

<style lang="scss"></style>

同樣的簡單解析一下:

  1. 每次頁面初始化時,都會首先有一個 initLocalStorage 過程,用於給當前頁面一個唯一 ID 標識,並且存入 localStorage 中
  2. 每次頁面 resize,將當前頁面元素 #j-main 的坐標值,通過 ID 標識當 Key,存入 localStorage 中
  3. 其他頁面,通過 window.addEventListener('storage', (event)=> {}) 監聽 localStorage 的變化

交互傳輸結果,與上述兩個動圖是一致的,就不額外貼圖了,但是基於 storage 事件傳輸的值有點不一樣,我們展開看看:

我們通過 window.addEventListener('storage', (event)=>{}) ,可以拿到此次變化的 localStorage key 是什麼,前值 oldValuenewValue 等等。

當然,由於 localStorage 存儲過程只能是字元串,在讀取的時候需要利用 JSON.stringifyJSON.parse 額外處理一層,調試的時候需要註意。

雖然看起來這種方式最不優雅,但是結合相容性一起看, localstorage 反而是相容性最好的方式。在數據量較小的時候,性能相差不會太大,反而可能是更好的選擇。

我基於上面三種方式:Broadcast Channel、SharedWorker 與 localStorage,都實現了一遍下麵這個跨 Tab 頁的 CSS 聯動動畫:

三種方式的代碼都不多,感興趣的可以戳這裡:Github - broadcastAnimation

實際應用思考

當然,上面的實現其實有很大一個瑕疵。

那就是我們只顧著實現通信,沒有考慮實際應用中的一些實際問題:

  1. 如何確定何時開始通信?
  2. Tab 頁頻繁的開關,如何知道當前還有多少頁面處於打開狀態?

基於實際應用,我們需要基於上述 3 種方式,進一步細化方案。

上面,為了方便演示,每次傳輸數據時,只傳輸動畫需要的數據。而實際應用,我們可以需要細化整個傳輸數據,設定合理的協議。譬如:

{
    // 傳輸狀態:
    // 1 - 首次傳輸
    // 2 - 正常通信
    // 3 - 頁面關閉
    status: 1 | 2 | 3,
    data: {}
}

接收方需要基於收到信息所展示的不同的狀態,做出不同的反饋。

當然,還有一個問題,我們如何知道頁面被關閉了?基於組件的 onUnmounted 發送當前頁面關閉的信息或者基於 window 對象的 beforeunload 事件發送當前頁面關閉的信息?

這些信息都有可能因為 Tab 頁面失活,導致關閉的信息無法正常被髮送出去。所以,實際應用中,我們經常用的一項技術是心跳上報/心跳廣播,一旦建立連接後,間隔 X 秒發送一次心跳廣播,告訴其他接收端,我還線上。一旦超過某個時間閾值沒有收到心跳上報,各個訂閱方可以認為該設備已經下線。

總而言之,跨 Tab 視窗通信應用在實際應用的過程中,我們需要思考更多可能隱藏的問題。

跨 Tab 視窗通信應用場景

當然,除了最近大火的跨 Tab 動畫應用場景,實際業務中,還有許多場景是它可以發揮作用的。這些場景利用了跨 Tab 通信技術,增強了用戶體驗並提供了更豐富的功能。

以下是一些常見的應用場景:

  1. 實時協作:多個用戶可以在不同的 Tab 頁上進行實時協作,比如編輯文檔、共用白板、協同編輯等。通過跨Tab通信,可以實現實時更新和同步操作,提高協作效率。

譬如這個:

  1. 多標簽頁數據同步:當用戶在一個標簽頁上進行了操作,希望其他標簽頁上的數據也能實時更新時,可以使用跨 Tab 通信來實現數據同步,保持用戶在不同標簽頁上看到的數據一致性。
  2. 跨標簽頁通知:在某些場景下,需要向用戶發送通知或提醒,即使用戶不在當前標簽頁上也能及時收到。通過跨 Tab 通信,可以實現跨頁面的消息傳遞,向用戶發送通知或提醒。
  3. 多標簽頁狀態同步:有些應用可能需要在不同標簽頁之間同步用戶的狀態信息,例如登錄狀態、購物車內容等。通過跨 Tab 通信,可以確保用戶在不同標簽頁上看到的狀態信息保持一致。
  4. 頁面間數據傳輸:有時候用戶需要從一個頁面跳轉到另一個頁面,並攜帶一些數據,通過跨Tab通信可以在頁面之間傳遞數據,實現數據的共用和傳遞。

舉幾個實際的例子:

  1. 某系統是一個國際化電商的倉庫管理系統,系統能切換到全球各地不同的倉庫進行數據操作,當用戶打開了頁面後,又新開了一個 Tab 頁面,並且切換到另外一個倉庫進行操作。當用戶重新回到第一個打開的頁面時,為了防止用戶錯誤操作數據(前端界面是一致的,可能忘記了自己切換過倉庫),通過彈窗提醒用戶你已經切換過倉庫;
  2. 某音樂播放器 PC 頁面,在列表頁面進行歌曲播放點擊,如果當前沒有音樂播放詳情頁,則打開一個新的播放詳情頁。但是,如果頁面已經存在一個音樂播放詳情頁,則不會打開新的音樂播放詳情頁,而是直接使用已經存在的播放詳情頁面;
  3. 系統有與列表頁與內容頁,在內容頁點擊已閱,如果用戶同時打開了上級列表頁,要取消列表頁關於該內容頁的未讀的提示;

總之,跨 Tab 視窗通信在實時協作、數據同步、通知提醒等方面都能發揮重要作用,為用戶提供更流暢、便捷的交互體驗。

最後

本文只羅列了 3 種較為常見,適用性強的方式。除去本文羅列的方式,肯定還有其他方式能夠實現跨 Tab 通信。

譬如,基於 Window: opener property 配合 postMessage 也可以實現跨 Tab 視窗通信,但是這種通信僅僅適用於當前視窗以及通過當前視窗新開的視窗之間的通信。

更多有意思的方式,期待大家的補充與探索。

好了,本文到此結束,希望對你有幫助

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文地址: Android app相容低版本Java環境 - Stars-One的雜貨小窩 起因是修複一個Bug遇到的問題,找到了一個可以讓app相容低版本java的方法 眾所周知,Android版本更新後,其內置的JRE環境也隨之更新了 假如我們在app中用到了JDK8的特有的屬性,而用戶的手機設 ...
  • 原來錯誤代碼 mounted() { .... this.map.addEventListener("click", function(e){//地圖單擊事件 var geocoder = new BMap.Geocoder(); var point = new BMap.Point(e.latln ...
  • typora-copy-images-to: media 數組 一、概念 對象中可以通過鍵值對存儲多個數據,且數據的類型是沒有限制的,所以通常會存儲一個商品的信息或一個人的信息: var obj = { goodsname:"手機", price:"5000", introduce:"手機很時尚,很 ...
  • 不要問自己需要什麼樣的人生,而要問自己想要成為什麼樣的人。 我們從前面的學習知道一個 React 組件不僅僅只包含 DOM 結構的,還應該樣式和 Javascript 邏輯的。這裡我們認識邏輯構造之事件處理。 1. React 事件處理 這裡列舉了在 React 中事件的幾種綁定處理方式: impo ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 仿貝殼地圖畫圈找房功能實現(高德地圖) 前言 在最近租房時,看到貝殼找房上線了一個地圖畫圈找房的功能,感覺很是新奇。接觸地圖開發也有很長一段時間了,以前大部分都是基於web pc端開發,所以一般遇到這種圈選,繪製多邊形圓形都是直接基於官方 ...
  • 項目代碼同步至碼雲 weiz-vue3-template 要求代碼規範,主要是為了提高多人協同和代碼維護效率,結合到此項目,具體工作就是為項目配置 eslint 和 prettier。 editorconfig 安裝 EditorConfig for VS Code 插件,根目錄下新建 .edito ...
  • 迴圈練習 1、列印100以內7的倍數 // 需要驗證的是1-100之間的數字 迴圈計數器正好可以表示 // i初始值設置為1 正好可以表示出需要驗證的數字 for (var i = 1; i <= 100; i++) { if (i % 7 == 0) { console.log(i) } } 2、 ...
  • 簡介 nodemon用來監視node.js應用程式中的任何更改並自動重啟服務,非常適合用在開發環境中。以前,我們開發一個node後端服務時,每次更改文件,均需重啟一下,服務才能生效。這使我們的開發效率降低了很多。nodemon的出現,可以隨時監聽文件的變更,自動重啟服務,我們開發時只需關註代碼即可, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...