Обмен мгновенными сообщениями — одна из самых важных функций, которые мы используем каждый день. Используя только JavaScript и помощь socket.io, узнайте вместе со мной, как создать простое веб-приложение для чата!

Оглавление

  1. "Введение"
  2. Ступеньки
    2а. Сервер
    Бонус! CORS
    2б. Клиент
    Бонус! Использование идентификатора пользователя
  3. "Заключение"
  4. "Хочу больше? Другие ресурсы, которые стоит проверить!»

Введение 🧑‍🍳

Завершив 2022 год и имея за плечами более года опыта работы инженером-программистом, я начал 2023 год с размышлений и воспоминаний о своем пребывании в буткемпе. В частности, когда это было время проекта и все удовольствие (и стресс 😅) от придумывания и выполнения проекта. Это заставило меня вспомнить, как многие из нас хотели включить функцию обмена мгновенными сообщениями, но в то время она казалась слишком сложной с точки зрения знаний, навыков и, честно говоря, времени на создание. Именно тогда я решил начать этот год с изучения веб-сокетов и того, как их создать за относительно короткое время.

Цель этой статьи — помочь вам создать простое приложение для чата с использованием Express, React и Socket.io. Перейдите к следующему разделу для шагов, обратитесь к Заключение, если вы визуальный человек и вам нужно увидеть все, прежде чем разбивать его, или если вы хотите узнать больше о веб-сокетах и ​​socket.io, вот видео с боевого корабля:

Шаги 🥖

Чтобы начать сборку, нам нужно сначала убедиться, что у нас установлен узел, а также создать новую папку для этого проекта. За подробностями вы можете обратиться к моей другой статье «Как настроить простой API с узлом и экспрессом», в противном случае мы готовы перейти за вами cd в новую папку проекта.

Сервер 🍅

Теперь, когда мы находимся в папке проекта, давайте начнем сборку с сервера.

  1. Создайте новую папку для сервера:
mkdir server

2. Инициализируйте этот проект как проект узла:

npm init -y

3. Установите эти пакеты:

npm install express --save
npm install --save-dev nodemon
npm install socket.io

4. Обновите сценарии package.json с помощью:

start: “nodemon index.js”

5. В index.js добавляем следующее (взято из документации socket.io- Инициализация и обработка CORS)

const express = require("express");
const { createServer } = require("http");
const { Server } = require("socket.io");

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: "*",
  },
});

io.on("connection", (socket) => {
  // ...
});

httpServer.listen(8080);

Мы включаем внутрь cors, origin: “*”,, чтобы разрешить запросы из любого места и упростить этот шаг, так как это больше для изучения того, как создать базовое приложение для чата. Однако вы можете указать порт (например: http://localhost:8080'), если хотите. Чтобы узнать больше о Cors, посмотрите это видео с Fireship ниже:

6. Добавьте другие события после подключения (примеры ниже)

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

  socket.on("send message", (body) => {
    io.emit("message", body);
  });

  socket.on("disconnect", () => {
    console.log("user disconnected");
  });
});

Что приятно в том, как работает socket.io, так это то, что он вдохновлен Node.js EventEmitter. Другими словами, socket.io похож на то, как вы можете добавить прослушиватель событий onClick к кнопке; одна сторона генерирует событие, а другая сторона может иметь для него прослушиватель. Примеры событий можно найти в этой шпаргалке на сайте socket.io, однако вы также можете создавать свои собственные события!

Чтобы еще больше продемонстрировать, как работают события, вот пример, взятый из socket.io, где hello — это событие:

// Server
io.on("connection", (socket) => {
  socket.emit("hello", "world");
});

// Client
socket.on("hello", (arg) => {
  console.log(arg); // world
});

Клиент 🥑

Теперь, когда сервер настроен и работает, давайте перейдем к клиенту, вернувшись в главный каталог проекта с помощью cd ...

  1. Создайте интерфейс React:
npx create-react-app <app-name>

2. Установите клиент пакета socket.io:

npm install socket.io-client

3. Удалите StrictMode в App/index.js, чтобы компоненты не отображались дважды, что приводит к множественным соединениям при обновлении.

4. Добавьте эти импорты в App.js

import { useRef, useState, useEffect } from “react”;
const { io } = require("socket.io-client");

5. Создайте ссылку на сокет

const socketRef = useRef();

6. Добавьте useEffect для запуска при загрузке страницы и подключения к серверу.

useEffect(() => {
    // or whichever port you set on the server
    socketRef.current = io.connect("http://localhost:8080"); 

    socketRef.current.on("connected", (message) => {
      receivedMessage(message);
    });

    socketRef.current.on("message", (message) => {
      receivedMessage(message);
    });

    socketRef.current.on("disconnected", (message) => {
      receivedMessage(message);
    });

    return () => socketRef.current.disconnect();
  }, []);

7. Чтобы получить ввод пользователя для сообщения, которое он хочет отправить, давайте добавим простую форму:

const [message, setMessage] = useState(“”);
const handleInputChange = (event) => {
    setMessage(event.target.value);
  };

return(
  <div className="App">
    <form onSubmit={sendMessage}>
        <input type="text" value={message} onChange={handleInputChange} />
        <input type="submit" />
    </form>
  </div>
)

8. Чтобы на самом деле отправить информацию на сервер от клиента, нам нужно сделать socketRef.current.emit(<eventName>, message);

const sendMessage = (event) => {
    event.preventDefault();

    socketRef.current.emit("send message", message);

    setMessage("");
};

9. Наконец, чтобы отобразить сообщения, которые получает клиент:

const [messages, setMessages] = useState([]);:

// this function is used within the useEffect from step 6 with the event listeners 
const receivedMessage = (message) => {
    setMessages((prevMessages) => [...prevMessages, message]);
};

return(
  <div className="App">
    {messages.map((message, index) => {
        return <p key={index}>{message}</p>;
    })}
  </div>
)

Бонус! Использование идентификатора пользователя

Как бы ни была важна отправка сообщений, не менее важно знать, кто и какое сообщение отправил, особенно по мере увеличения количества людей в чате. В этом нам снова помогает Socket.io, так как при каждом новом подключении присваивается случайный 20-символьный идентификатор, который синхронизируется со значением на стороне сервера. Теперь мы можем обновить наш код следующим образом:

const [userId, setUserId] = useState("");

useEffect(() => {
  socketRef.current.on("connect", () => {
        receivedMessage("Welcome new user!");
        setUserId(socketRef.current.id);
  });
...
}

const sendMessage = (event) => {
    event.preventDefault();

    const messageObj = {
      userId,
      message,
    };

    socketRef.current.emit("send message", messageObj);

    setMessage("");
  };

return(
  <div className="App">
    <p>My userId is: {userId}</p>
  </div>
)

Вывод 😋

Поздравляем, теперь у вас должно быть работающее веб-приложение чата! Для проверки вы можете открыть две, три или более разных вкладок в своем браузере для любого порта, на который настроен внешний интерфейс (например, localhost: 3000). Тем не менее, поскольку мы не добавляли никаких стилей, пользовательский интерфейс не самый интуитивно понятный, поэтому я рекомендую вам использовать этот аспект самостоятельно. Чтобы помочь вам начать работу, подумайте, как сделать так, чтобы пользователю было легче узнать, какое сообщение кому принадлежит.

Если вы хотите сравнить и посмотреть, как должны выглядеть полные основные файлы сервера и клиента:

// Server/index.js

const express = require("express");
const { createServer } = require("http");
const { Server } = require("socket.io");

const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: "*", // or "http://localhost:3000"
  },
});

io.on("connection", (socket) => {
  console.log(`User ${socket.id} joined the chat`);
  // inform everyone in chat a new user joined
  io.emit("connected", `User ${socket.id} joined the chat`);

  socket.on("send message", (body) => {
    const { userId, message } = body;
    io.emit("message", `User ${userId} typed ${message}`);
  });

  socket.on("disconnect", () => {
    console.log(`User ${socket.id} left the chat`);
    io.emit("disconnected", `User ${socket.id} left the chat`);
  });
});

httpServer.listen(8080, () => {
  console.log("server is running on 8080");
});
// Client/src/App.js

import "./App.css";
import { useRef, useState, useEffect } from "react";
const { io } = require("socket.io-client");

function App() {
  const [messages, setMessages] = useState([]);
  const [message, setMessage] = useState("");
  const [userId, setUserId] = useState("");

  const socketRef = useRef();

  useEffect(() => {
    // or whichever port you set on the server
    socketRef.current = io.connect("http://localhost:8080");

    socketRef.current.on("connect", () => {
      receivedMessage("Welcome new user!");
      setUserId(socketRef.current.id);
    });

    socketRef.current.on("connected", (message) => {
      receivedMessage(message);
    });

    socketRef.current.on("message", (message) => {
      receivedMessage(message);
    });

    socketRef.current.on("disconnected", (message) => {
      receivedMessage(message);
    });

    return () => socketRef.current.disconnect();
  }, []);

  const receivedMessage = (message) => {
    setMessages((prevMessages) => [...prevMessages, message]);
  };

  const sendMessage = (event) => {
    event.preventDefault();

    const messageObj = {
      userId,
      message,
    };

    socketRef.current.emit("send message", messageObj);

    setMessage("");
  };

  const handleInputChange = (event) => {
    setMessage(event.target.value);
  };

  return (
    <div className="App">
      <div>
        {messages.map((message, index) => {
          return <p key={index}>{message}</p>;
        })}
      </div>
      <div>
        <form onSubmit={sendMessage}>
          <input type="text" value={message} onChange={handleInputChange} />
          <input type="submit" />
        </form>
      </div>
      <p>My userId is: {userId}</p>
    </div>
  );
}

export default App;

Большое спасибо за прочтение ❤️ Надеюсь, это помогло вам начать работу над новым проектом и/или вы можете включить часть мгновенных сообщений в один из ваших текущих проектов!

Если вам понравилось это читать, ознакомьтесь с другими моими статьями! Хорошим местом для начала является Как настроить простой API с Node.js и Express, Запросы на выборку и действия контроллера: подключение интерфейса к серверу или Все о рендеринге: клиент и Сервер».

Хотите больше? Другие ресурсы, чтобы проверить! 💾







Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.