Vue 團(tuán)隊(duì)正式發(fā)布!未來 Vue 也能寫后端啦!
背景需求
最近,我遇到了一個(gè)需求:有兩個(gè)頁面,每個(gè)頁面各自連接到一個(gè)獨(dú)立的WebSocket
。這兩個(gè)頁面能夠通過WebSocket
相互影響。
圖片
為了更好地理解需求,以下是兩個(gè)頁面的簡化版本:
圖片
頁面1和頁面2的需求:
- 頁面1:當(dāng)點(diǎn)擊更新按鈕時(shí),頁面1的數(shù)值加1,同時(shí)頁面2的數(shù)值變?yōu)轫撁?數(shù)值的10倍。
- 頁面2:當(dāng)點(diǎn)擊清空按鈕時(shí),頁面1和頁面2的數(shù)值都會(huì)清零。
- 頁面2:定時(shí)獲取頁面1的數(shù)值,并展示出來。
效果演示:
圖片
簡單實(shí)現(xiàn)方案
實(shí)現(xiàn)這個(gè)需求并不復(fù)雜,核心思路是通過Node.js
創(chuàng)建兩個(gè)WebSocket
連接,并通過一個(gè)count
變量來同步頁面的數(shù)據(jù)。
首先,安裝ws
庫:
npm install ws
如何實(shí)現(xiàn)兩個(gè)WebSocket
之間的通信呢?其實(shí)就是維護(hù)兩個(gè)全局變量來存儲(chǔ)這兩個(gè)WebSocket
實(shí)例。
圖片
然后,在前端頁面通過 WebSocket 通信來實(shí)現(xiàn)數(shù)據(jù)同步。
頁面1
圖片
頁面2
圖片
通過這種方式,基本實(shí)現(xiàn)了需求,效果如下:
圖片
優(yōu)化思路
上述方法雖能實(shí)現(xiàn)需求,但維護(hù)起來可能會(huì)很麻煩,尤其是當(dāng)多個(gè)WebSocket
連接實(shí)例增加時(shí),代碼可能變得難以管理。
我有一個(gè)想法:能否讓每個(gè)WebSocket
連接管理自己的狀態(tài)?
問題在于,盡管每個(gè)連接管理自己的狀態(tài),它們?nèi)匀恍枰蕾囈粋€(gè)共同的變量(即count)。當(dāng)某個(gè)實(shí)例更新了
count
,另一個(gè)實(shí)例如何被通知并作出反應(yīng)呢?
想到這里,我就聯(lián)想到了 Vue3 的響應(yīng)式機(jī)制。Vue3 提供了一套非常優(yōu)秀的響應(yīng)式 API,當(dāng)數(shù)據(jù)變化時(shí),它能夠自動(dòng)通知相關(guān)的組件進(jìn)行更新。
圖片
能否將 Vue3 的響應(yīng)式 API 移植到Node.js
環(huán)境中呢?
答案是:可以!Vue3 將響應(yīng)式 API 拆分成了一個(gè)獨(dú)立的庫@vue/reactivity
,即使在 Node.js 環(huán)境下,也可以使用這個(gè)庫來實(shí)現(xiàn)響應(yīng)式功能。
通過安裝這個(gè)包:
npm install @vue/reactivity
我們可以使用ref、computed 和 watch
來讓每個(gè)WebSocket 實(shí)例獨(dú)立管理自己的狀態(tài)。
圖片
最終的效果展示:
圖片
代碼實(shí)現(xiàn)(完整方案)
可以將@vue/reactivity
作為一個(gè)響應(yīng)式工具庫使用,它不僅可以在Node.js
環(huán)境下使用,甚至可以在 React 項(xiàng)目中使用。
Node.js 端:index.js
// 引入 WebSocket 庫
const WebSocket = require('ws');
// 引入 Vue3 響應(yīng)式 API
const reactivity = require('@vue/reactivity');
const { ref, computed, watch } = reactivity;
// 創(chuàng)建 WebSocket 服務(wù)器
const wss1 = new WebSocket.Server({ port: 8001 });
const wss2 = new WebSocket.Server({ port: 8002 });
// 記錄數(shù)字
const count = ref(0);
// 計(jì)算數(shù)值的 10 倍
const sum = computed(() =>10 * count.value);
// 處理連接1
wss1.on('connection', (ws) => {
ws.on('message', (message) => {
// 更新 count 的值
count.value = Number(message);
});
// 監(jiān)聽 count 變化,并發(fā)送到頁面1
watch(count, (newValue) => {
ws.send(newValue);
});
});
// 處理連接2
wss2.on('connection', (ws) => {
ws.on('message', () => {
// 清空 count 和 sum
count.value = 0;
});
// 監(jiān)聽 sum 變化,并發(fā)送到頁面2
watch(sum, (newValue) => {
ws.send(newValue);
});
// 模擬定時(shí)任務(wù),定期發(fā)送 sum
setTimeout(() => {
ws.send(sum.value);
}, 3600 * 12); // 12小時(shí)后發(fā)送
});
頁面1:Page1.vue
<template>
<div class="flex justify-center mb-3 text-4xl font-bold">頁面1</div>
<Button type="primary" @click="click">點(diǎn)擊更新數(shù)據(jù)</Button>
<div class="text-lg">當(dāng)前數(shù)值:{{ count }}</div>
</template>
<script setup lang="ts">
import { Button } from'ant-design-vue';
import { ref } from'vue';
const count = ref(0);
// 創(chuàng)建 WebSocket 客戶端
const socket = new WebSocket('ws://localhost:8001');
const click = () => {
count.value++;
// 發(fā)送更新后的 count 到服務(wù)器
socket.send(`${count.value}`);
};
// 接收來自服務(wù)端的消息
socket.addEventListener('message', (e) => {
count.value = e.data;
});
</script>
頁面2:Page2.vue
<template>
<div class="flex justify-center mb-3 text-4xl font-bold">頁面2</div>
<Button type="primary" @click="click">清空數(shù)據(jù)</Button>
<div class="text-lg">當(dāng)前數(shù)值:{{ count }}</div>
</template>
<script setup lang="ts">
import { Button } from'ant-design-vue';
import { ref } from'vue';
const count = ref(0);
// 創(chuàng)建 WebSocket 客戶端
const socket = new WebSocket('ws://localhost:8002');
const click = () => {
// 向服務(wù)器發(fā)送清空命令
socket.send('Hello, server!');
};
// 接收來自服務(wù)端的消息
socket.addEventListener('message', (e) => {
count.value = e.data;
});
</script>
最終效果
圖片
這樣,通過 Vue3 的響應(yīng)式 API 和WebSocket
的結(jié)合,我們可以輕松地實(shí)現(xiàn)兩個(gè)頁面間的實(shí)時(shí)數(shù)據(jù)同步,并使代碼更加易于維護(hù)和擴(kuò)展。