Broadcast 過去在 Web 屬夢寐以求的功能,但透過 Vue-socket.io ,可以很輕易實現。
Version
macOS Mojave 10.14.5
WebStorm 2019.1.3
Node 12.4
Express 4.17.1
Vue 2.6.10
Vue-socket.io 3.0.7
Chrome
Server
$ yarn add body-parser
安裝 body-parser
module,為了能使用 req.body
讀取 POST 上傳內容。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.io</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<script>
let sendMessage = () => {
axios.post('/notification', {
message: document.getElementById('message').value,
color: document.querySelector('input[name="color"]:checked').value
});
};
</script>
</head>
<body>
<div>
<label>Message</label>
<input type="text" name="message" id="message"><br/>
<input type="radio" name="color" value="success" checked>Success <br/>
<input type="radio" name="color" value="info">Info <br/>
<input type="radio" name="color" value="error">Error <br/>
<button onclick="sendMessage(); return false">Send Message</button>
</div>
</body>
</html>
19 行
<input type="text" name="message" id="message"><br/>
<input type="radio" name="color" value="success" checked>Success <br/>
<input type="radio" name="color" value="info">Info <br/>
<input type="radio" name="color" value="error">Error <br/>
<button onclick="sendMessage(); return false">Send Message</button>
由 user 選擇 message color:共有 Success
、Info
或 Error
。
第 8 行
let sendMessage = () => {
axios.post('/notification', {
message: document.getElementById('message').value,
color: document.querySelector('input[name="color"]:checked').value
});
};
將 message 與 color 透過 POST 送到 /notification
。
server.js
let dotenv = require('dotenv').config({
path: '.env'
});
let express = require('express');
let path = require('path');
let bodyParser = require('body-parser');
let socket = require('./socket');
let app = express();
let port = process.env.PORT || 8500;
let http = require('http').Server(app);
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
let server = socket.initialize(http);
http.listen(port);
app.post('/notification', (req, res) => {
console.log(`Message received: ${ req.body.message }`);
server.emit('popupNotification', {
message: req.body.message,
color: req.body.color
});
res.send();
});
let heartbeat = () => {
let pulse = Math.ceil(Math.random() * (160 - 60) + 60);
console.log(`Heartbeat: ${ pulse }`);
return pulse;
};
let emitPulseEvent = () => server.emit('pulse', heartbeat());
setInterval(emitPulseEvent, 1000);
console.log('server started');
console.log(`running in ${ process.env.NODE_ENV }`);
console.log(`running on port ${ port }`);
15 行
app.use(bodyParser.json());
使用 bodyParser
。
20 行
app.post('/notification', (req, res) => {
console.log(`Message received: ${ req.body.message }`);
server.emit('popupNotification', {
message: req.body.message,
color: req.body.color
});
res.send();
});
新設定 /notification
為 POST,使用 req.body
讀取 POST 上傳資料。
將 POST 資料使用 server.emit()
對 client 發出 popupNotification
event。
Client
App.vue
<template>
<v-app>
<v-toolbar dark fixed app>
<v-toolbar-title>Vue with Socket.io</v-toolbar-title>
</v-toolbar>
<v-content>
<v-container fluid>
<v-layout row mb-4>
<v-flex>
<graph></graph>
</v-flex>
</v-layout>
</v-container>
</v-content>
<v-footer dark></v-footer>
<popup-message></popup-message>
</v-app>
</template>
<script>
import graph from "./components/graph.vue";
import popupMessage from './components/popup-message.vue';
export default {
components: {
graph,
popupMessage
}
};
</script>
16 行
<popup-message></popup-message>
新增 popup-message
component。
popup-message.vue
<template>
<v-snackbar v-model="snackbar" :timeout="timeout" :color="color" top>
{{ notificationText }}
<v-btn flat @click="snackbar = false">Close</v-btn>
</v-snackbar>
</template>
<script>
let popupNotification = function(args) {
this.notificationText = args.message;
this.color = args.color || 'success';
this.snackbar = true;
};
export default {
name: "popup-message",
data: () => ({
snackbar: false,
notificationText: '',
timeout: 3000,
color: 'success'
}),
sockets: {
popupNotification
}
}
</script>
23 行
sockets: {
popupNotification
}
在 sockets
property 內宣告 popupNotification()
。
第 9 行
let popupNotification = function(args) {
this.notificationText = args.message;
this.color = args.color || 'success';
this.snackbar = true;
};
args
會接收 WebSocket 所傳來的。
Conclusion
- Vue-socket.io 善用 Vue 特性,比原生 socket.io 更容易使用
Sample Code
完整範例可在我的 GitHub 上找到
Reference
Mark Barton, Display a broadcast message using a Vuetify snackbar component which is sent via Socket.io