如何使用 Socket.io 通过 NodeJS 和 ReactJS 制作实时 API

2025-06-04

如何使用 Socket.io 通过 NodeJS 和 ReactJS 制作实时 API

图片描述

我们都热爱设计模式,也都想知道什么时候使用它们才是最佳选择。我将用其中一种模式来实践一个你在工作中可能会遇到的业务案例。我所说的模式是“发布者-订阅者”。

今天,我将制作一个实时 API,每当数据库上发生操作时,它会更新所有连接的客户端,因此使用仪表板的超级管理员用户可以立即知道其他管理员是否已登录或退出,而无需每隔几秒刷新一次页面,其他情况是立即知道您正在处理的平台上收到了订单。

本教程中我将使用:

  • NodeJS 与 Express 用于服务器端逻辑
  • 使用 ReactJS 构建简单的客户端应用程序
  • Socket.io 用于双方实时连接

为了继续,您可以逐步编写代码,因为我将介绍其中的大部分内容,或者您​​可以克隆这两个存储库:

首先让我们设置我们的服务器,首先初始化文件夹结构

npm init -y
Enter fullscreen mode Exit fullscreen mode

然后我们添加我们使用的包,在本教程中,我将在后端使用 ES6 语法,所以我们需要 babel 来捆绑我们的代码,以及我们稍后将使用的一些其他库。

npm add nodemon dotenv  babel-loader 
@babel/preset-env @babel/node @babel/core -D
Enter fullscreen mode Exit fullscreen mode

这些是 devDependencies,这就是我们使用 -D 标志的原因,因为我们不需要它们用于开发以外的用途。

1.nodemon 用于热运行
2.dotenv 用于 .env 配置
3.babel 用于打包

现在轮到举重运动员了

npm add express mongoose socket.io
Enter fullscreen mode Exit fullscreen mode

1.express 设置我们的服务器
2.mongoose 连接到我们的 mongodb
3.socket.io 负责实时连接

现在有点无聊了,让我们写一些 Javascript

index.js

import express from 'express'
import dotenv from 'dotenv'

dotenv.config()

const app = express()


app.get('/', (req,res)=>{
   res.send('Hello')
})

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Server up and running on port ${PORT}`);
})
Enter fullscreen mode Exit fullscreen mode

在运行此代码之前,您必须设置一些配置

.env

PORT=5000
MONGO_DB_URL=mongodb://localhost:27017
MONGO_DB_DBNAME=store
Enter fullscreen mode Exit fullscreen mode

.babelrc

{
  "presets": [
    "@babel/preset-env"
  ]
}
Enter fullscreen mode Exit fullscreen mode

package.json

....
  "scripts": {
    "start": "babel-node index.js",
    "dev": "nodemon --exec babel-node index.js"
  },
....
Enter fullscreen mode Exit fullscreen mode

现在,当您输入时npm run dev,您会发现服务器已启动并正在运行,如果您在浏览器中输入,http://localhost:5000您将获得以下内容:

替代文本

现在让我们创建三个文件夹并调整我们的代码如下:

替代文本

然后为了更好地处理环境变量
config/variables.js

import dotenv from 'dotenv'
dotenv.config()

const DB_URL = `${process.env.MONGO_DB_URL}/${process.env.MONGO_DB_DBNAME}`;
const PORT = process.env.PORT;

export {
  DB_URL,
  PORT
}
Enter fullscreen mode Exit fullscreen mode

初始化并连接数据库
config/db.js

import {DB_URL} from '../config/variables'

mongoose.connect(DB_URL, {
  useNewUrlParser:true,
  useUnifiedTopology:true
}, () => {
  console.log(DB_URL);
  console.log(`DB up and running`);
})
Enter fullscreen mode Exit fullscreen mode

订单模型
models/order.js

import mongoose, {Schema} from 'mongoose'

const schema = new Schema({
  customer:{
    type:String,
    required:true
  },
  price:{
    type:Number,
    required:true
  },
  address:{
    type:String,
    required:true
  }
}, {
  timestamps:true
}) 

const Order = mongoose.model('order', schema)

export default Order;
Enter fullscreen mode Exit fullscreen mode

订单控制器
controllers/order.js

import express from 'express'
import Order from '../models/order'
import {io} from '../index' 

const router = express.Router()

router.get('/', async (req, res) => {
  try {
    const orders = await Order.find()
    res.send(orders)
  } catch (error) {
    res.send(error)
  }
})

router.post('/', async (req, res) => {
  try {
    const order = new Order(req.body)
    await order.save()
    res.status(201).send(order)
  } catch (error) {
    res.send(error)
  }
})

export default router
Enter fullscreen mode Exit fullscreen mode

现在最重要的部分
index.js

import express from 'express'
import {PORT} from './config/variables'
import cors from 'cors'
import http from 'http'
// import { Server } from 'socket.io';
import socketIO from 'socket.io';
// import './config/sockets'
import './config/db'

import orderRouter from './controllers/order'

const app = express()
const server = http.createServer(app)
const io = socketIO(server, {
  transports:['polling'],
  cors:{
    cors: {
      origin: "http://localhost:3000"
    }
  }
})

io.on('connection', (socket) => {
  console.log('A user is connected');

  socket.on('message', (message) => {
    console.log(`message from ${socket.id} : ${message}`);
  })

  socket.on('disconnect', () => {
    console.log(`socket ${socket.id} disconnected`);
  })
})

export {io};


app.use(express.json())
app.use(cors())
app.use('/orders', orderRouter)

app.get('/', (req,res) => {
  res.send('Hello')
})

server.listen(PORT, () => {
  console.log(`Server up and running on port ${PORT}`);
})
Enter fullscreen mode Exit fullscreen mode

让我解释一下这里发生了什么

使用 socket.io 时,我们配置服务器的方式会有所不同,因为它处理服务器实例本身,因此

const server = http.createServer(app)
Enter fullscreen mode Exit fullscreen mode

然后我们用 io 包装它,允许一些 cors,它们将在端口 3000 上短暂地成为客户端

const io = socketIO(server, {
  transports:['polling'],
  cors:{
    cors: {
      origin: "http://localhost:3000"
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

配置 io 并导出以在订单控制器中使用

io.on('connection', (socket) => {
  console.log('A user is connected');

  socket.on('message', (message) => {
    console.log(`message from ${socket.id} : ${message}`);
  })

  socket.on('disconnect', () => {
    console.log(`socket ${socket.id} disconnected`);
  })
})

export {io};
Enter fullscreen mode Exit fullscreen mode

然后我们进入订单控制器并将代码更改为
controllers/order.js

router.post('/', async (req, res) => {
  try {
    const order = new Order(req.body)
    await order.save()
    const orders = await Order.find()
    io.emit('order-added', orders)
    res.status(201).send(order)
  } catch (error) {
    res.send(error)
  }
})
Enter fullscreen mode Exit fullscreen mode

这意味着,每当有人添加订单时,它将被发布到连接套接字的所有客户端,因此将立即使用数据库中的订单数组进行更新

现在我们可以转到客户端并使用这个 API,create-react-app因为我们不需要复杂的应用程序,我们只需要演示行为

在这里,我制作了一个简单的 UI 组件,称为 Orders,您可以在 repo 中轻松找到代码,但我对这一部分感兴趣

  const [orders, setOrders] = useState([])

  useEffect(() => {
    const getOrders = async () => {
      const response = await axios.get('http://localhost:5000/orders')
      const ordersData = response.data;
      setOrders(ordersData)
    } 

    getOrders()
  }, [])

  useEffect(() => {
    const socket = io('ws://localhost:5000')

    socket.on('connnection', () => {
      console.log('connected to server');
    })

    socket.on('order-added', (newOrders) => {
      setOrders(newOrders)
    })

    socket.on('message', (message) => {
      console.log(message);
    })

    socket.on('disconnect', () => {
      console.log('Socket disconnecting');
    })

  }, [])

Enter fullscreen mode Exit fullscreen mode

首先,我们的状态最初是一个空数组

第一个 useEffect 调用是调用我们刚刚创建的获取订单端点来获取所有订单,然后我们用它填充视图

第二次 useEffect 调用,我们使用socket.io-client将在客户端安装的连接npm i socket.io-client,然后我们指定在来自套接字的订单添加事件上,我们将通过事件发送订单并将其设置为新数组,因此每当添加新订单时,我们都会收到数据库中新订单数组的通知。

为了测试它,我在端口 3000 上打开浏览器来打开我的 React 应用程序,然后使用 Postman 向端口 5000 上的服务器发送一条消息来添加订单,然后我的 React 应用程序就立即更新了

替代文本

这是我的第一篇帖子,希望你喜欢它。

文章来源:https://dev.to/omardiaa48/how-to-make-realtime-apis-with-nodejs-and-reactjs-using-socket-io-6ja
PREV
面试时你应该问的问题
NEXT
9 Steps to Get 100 Stars on GitHub 1. Create a READ-ME with a pretty top section 2. Be concise 3. Choose an open license 4. Have stars 5. Upload an eye-catching social card 6. Advertise 7. Engage in developer communities 8. Respond to feedback 9. Shout-out your supporters And a bonus... 💪 Awesome Personal Portfolio