在 Vue.js 中创建 Websocket 服务器

使用 websocket 服务器是加速应用程序的好方法。API 固有地带有自己的 HTTP 开销,这意味着每次调用 API 时,都必须等待一点 HTTP 响应。

这大部分都很好,但是如果您的应用程序具有大量时间敏感且频繁的服务器请求,则可能会成为问题。一个很好的例子是聊天应用程序,您需要在其中立即查看对方在说什么。API 仍然可以在这种情况下工作,但它不是该工作的最佳解决方案。

在本教程中,我们将介绍如何使用 Node.JS 和 express 中构建的 websocket 服务器在 Vue.JS 中设置 websocket 服务器。继续阅读以获取更多信息。我还在这里写了另一个关于如何在 Express 和 Javascript 中创建 websocket 服务器的教程。

入门:创建您的 Vue 应用程序#

像往常一样,使用 vue cli 启动一个 vue 应用程序。例如,编写以下代码以生成一个名为“fjolt-app”的 vue 应用程序:

vue create fjolt-app

如果这不起作用,请尝试通过 npm 安装 vue cli 工具。包可以在这里找到。

如果您是 Vue 的新手,您可以通过npm run serve在您创建应用程序的目录中运行来启动这个虚拟应用程序。

创建一个 websocket 服务器#

下一步是创建一个 websocket 服务器。如前所述,我在这里写了一篇关于如何做到这一点的深入教程。总之,您需要创建一个 index.js 文件。为了让事情变得更简单,我在 vue 目录本身中创建了我的,并将其命名为index.mjs,这样我就可以立即使用 import 语句。

然后我的文件结构如下所示:

| - node_modules
| - package.lock.json
| - package.json
| - public
| - README.md
| - src
  | - App.vue < -- 我们的应用!
  | - assets
  | - components
  | - main.js
| - index.mjs <-- 我们的 websocket 服务器

用 Javascript 创建我们的 websocket 服务器

接下来,让我们创建我们的 websocket 索引文件。在index.mjs中,如果您感到懒惰,可以使用以下代码。不要忘记npm i您导入的所有模块。

    import path from 'path'
    import { fileURLToPath } from 'url'
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(__filename);
    
    import express from 'express'
    import expressWs from 'express-ws'
    import http from 'http'
    
    // Our port
    let port = 3000;
    
    // App and server
    let app = express();
    let server = http.createServer(app).listen(port);    
    
    // Apply expressWs
    expressWs(app, server);
    
    app.use(express.static(__dirname + '/views'));
    
    // Get the route / 
    app.get('/', (req, res) => {
        res.status(200).send("Welcome to our app");
    });
    
    // Get the /ws websocket route
    app.ws('/ws', async function(ws, req) {
        ws.on('message', async function(msg) {
            console.log(msg);
            ws.send(JSON.stringify({ "message" : "hello" }));
            // Start listening for messages
        });
    });

所以客户端会将数据发送到我们的 websocket 服务器。该数据将作为msg变量到达,如上面代码的底部所示。然后,我们可以获取、存储或处理该消息数据。有时,我们可能希望将其发送到数据库。其他时候,我们可能想把它发回给用户。

无论哪种方式,ws.on我们都可以将消息发送回客户端。比方说,当收到一条消息时,我想向用户发送一个{ "message" : "hello" }回复给用户的对象。为此,我将执行以下操作:

    // Get the /ws websocket route
    app.ws('/ws', async function(ws, req) {
        ws.on('message', async function(msg) {
            // Let's put our message in JSON.stringify, and send it to the user who just sent the message
            ws.send(JSON.stringify({ "message" : "hello" }));
        });
    });

当我们对我们的 websocket 服务器感到满意时,我们可以在终端中通过键入 运行它node index.mjs,同时在index.mjs所在的目录中运行它。现在我们有了一个可以连接的实时 websocket。

简而言之,我们现在有一种方法可以在我们的服务器和用户之间创建直接连接,并且基本上是即时消息传递。现在我们已经解决了这个问题,我们需要能够从 Vue 发送和接收。接下来让我们看看。

向 Vue.JS 添加 websocket#

src文件夹中打开App.vue 。编辑 Javascript,使其看起来像这样:

export default {
    name: 'App',
    data() {
        return {
            socket: {},
            connectedStatus: 'Not connected!',
            message: 'No message yet!'
        }
    },
    async mounted() {
        // Calculate the URL for the websocket. If you have a fixed URL, then you can remove all this and simply put in
        // ws://your-url-here.com or wss:// for secure websockets.
        const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:')
        const port = ':3000';
        const echoSocketUrl = socketProtocol + '//' + window.location.hostname + port + '/ws'
    
        // Define socket and attach it to our data object
        this.socket = await new WebSocket(echoSocketUrl); 

        // When it opens, console log that it has opened. and send a message to the server to let it know we exist
        this.socket.onopen = () => {
            console.log('Websocket connected.');
            this.connectedStatus = 'Connected';
            this.sendMessage(JSON.stringify({"message" : "Hello, server."}));
        }

        // When we receive a message from the server, we can capture it here in the onmessage event.
        this.socket.onmessage = (event) => {
            // We can parse the data we know to be JSON, and then check it for data attributes
            let parsedMessage = JSON.parse(event.data);
            // If those data attributes exist, we can then console log or show data to the user on their web page.
            console.log(parsedMessage);
            if(typeof parsedMessage.message !== "undefined" && parsedMessage.message == "hello") {
                this.message = parsedMessage.message;
                console.log('We have received a message from the server!')
            }
        }
    },
    methods: {
        waitForOpenConnection: function() {
            // We use this to measure how many times we have tried to connect to the websocket server
            // If it fails, it throws an error.
            return new Promise((resolve, reject) => {
                const maxNumberOfAttempts = 10
                const intervalTime = 200 

                let currentAttempt = 0
                const interval = setInterval(() => {
                    if (currentAttempt > maxNumberOfAttempts - 1) {
                        clearInterval(interval)
                        reject(new Error('Maximum number of attempts exceeded.'));
                    } else if (this.socket.readyState === this.socket.OPEN) {
                        clearInterval(interval)
                        resolve()
                    }
                    currentAttempt++
                }, intervalTime)
            })
        },
        sendMessage: async function(message) {
            // We use a custom send message function, so that we can maintain reliable connection with the
            // websocket server.
            if (this.socket.readyState !== this.socket.OPEN) {
                try {
                    await this.waitForOpenConnection(this.socket)
                    this.socket.send(message)
                } catch (err) { console.error(err) }
            } else {
                this.socket.send(message)
            }
        }
    }
}

如何创建 Vue.JS websocket 服务器:细节#

虽然有内联注释,但让我们更详细地看一下。我们首先创建两个方法:

  • sendMessage – 这是一个自定义发送消息功能,使用socket.send(). 唯一的区别是我们在发送之前测试了一个 websocket 服务器连接,为我们省去了一点麻烦。
  • waitForOpenConnection – 这仅由 sendMessage 使用,它设置检查套接字连接的间隔,或引发错误。

将我们的套接字保存为数据

然后,我们使用以下行创建一个新的 websocket 连接,在mounted(). 这意味着当应用程序安装到浏览器窗口(或加载时)时,我们创建一个新的 websocket 连接,然后data()通过设置this.socket新的 websocket 服务器连接将其附加到我们的 .

const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:')
const port = ':3000';
const echoSocketUrl = socketProtocol + '//' + window.location.hostname + port + '/ws'

// Define socket and attach it to our data object
this.socket = await new WebSocket(echoSocketUrl); 

这样做的额外好处是我们现在可以跨方法、观察者和所有其他类型的 Vue 功能引用我们的套接字。创建连接后,我们添加两个事件。一种是检查套接字何时打开,另一种是中继消息。


    // When it opens, console log that it has opened. and send a message to the server to let it know we exist
    this.socket.onopen = () => {
        console.log('Websocket connected.');
        this.connectedStatus = 'Connected';
        this.sendMessage(JSON.stringify({"message" : "Hello, server."}));
    }

    // When we receive a message from the server, we can capture it here in the onmessage event.
    this.socket.onmessage = (event) => {
        // We can parse the data we know to be JSON, and then check it for data attributes
        let parsedMessage = JSON.parse(event.data);
        // If those data attributes exist, we can then console log or show data to the user on their web page.
        console.log(parsedMessage);
        if(typeof parsedMessage.message !== "undefined" && parsedMessage.message == "hello") {
            this.message = parsedMessage.message;
            console.log('We have received a message from the server!')
        }
    }

onmessage功能可能是 websocket 创建者最感兴趣的,因为这是处理来自服务器的数据的地方。我们之前创建的消息将到达此事件,我们可以将其显示在页面上。其他数据也可以在这里处理。

由于我还创建了另外两个数据变量messageconnectedStatus,我们可以在模板中使用它们来显示来自我们的 websocket 服务器的响应:

<template>
    <h2>Welcome to Websockets</h2>
    <p>You are: </p>
    <p>Your message is: </p>
</template>

我们的 websocket 服务器页面

结论#

vue 中的 Websocket 服务器实际上工作得非常优雅。在本教程中,我们已经介绍了所有基础知识。现在您可以继续从您的 websocket 服务器向浏览器发送或接收数据。如果您从 HTTP 切换,websocket 带来的效率意味着您的网站看起来会更快。

不要忘记,在单独的终端窗口中运行,这样npm run serve你的 websocket 服务器和 Vue.JS 应用程序都在运行像往常一样,这里有一些有用的链接:node index.mjs