วิธีสร้างแอปพลิเคชั่น React Chat อย่างง่ายโดยใช้ Socket.IO

เผยแพร่แล้ว: 2022-02-10

เฮ้ คุณเคยสงสัยหรือไม่ว่าแอปพลิเคชั่นอย่าง Slack ทำงานอย่างไร? หรือว่ามันยากแค่ไหนที่จะสร้างแอพแบบนั้น?

ในบทความนี้ เราจะแสดงคำแนะนำทีละขั้นตอนเกี่ยวกับวิธีสร้างแอปพลิเคชันการแชทแบบโต้ตอบแบบ Slack แบบง่ายๆ โดยใช้ ReactJS และ SocketIO เราจะสร้างเวอร์ชันที่ค่อนข้างง่ายของฟีเจอร์ทั้งหมดที่ Slack นำเสนอ ดังนั้นให้ลองใช้บทช่วยสอนนี้เป็นตัวอย่างการเริ่มต้นที่ดี

ก่อนที่เราจะเข้าสู่งานการพัฒนาที่สำคัญ มีข้อมูลสำคัญบางอย่างที่คุณต้องเตรียมให้พร้อม

ข้อกำหนดเบื้องต้นที่สำคัญ 3 ประการ:

  1. คุณจะต้องมีความรู้พื้นฐานเกี่ยวกับ JavaScript
  2. คุณควรมี NodeJS และ NPM ติดตั้งอยู่ในอุปกรณ์ของคุณ
  3. มี IDE หรือโปรแกรมแก้ไขข้อความที่ต้องการ

เมื่อคุณมีการตั้งค่าเหล่านี้แล้ว เราจะดำเนินการตามขั้นตอนต่างๆ เพื่อให้ได้แอปพลิเคชันที่มีคุณลักษณะง่ายๆ 3 อย่าง:

  1. เข้าสู่ระบบผ่านการให้ชื่อเล่น
  2. สลับระหว่างช่องสัญญาณแบบคงที่
  3. ส่งข้อความไปยังช่อง (รวมถึงอีโมจิ)

เมื่อเสร็จแล้ว คุณควรมีแอปพลิเคชันที่มีลักษณะดังนี้:

เข้าสู่ระบบแอพแชทและหลัก

คุณมีทุกอย่างพร้อมหรือยัง? ใช่!? มาเริ่มกันเลยดีกว่า …

1. เริ่มต้น ReactJS Application

ขั้นแรก เราต้องสร้างและเริ่มต้นแอปพลิเคชัน ReactJS เพื่อที่เราจะใช้ create-react-app.

เปิดเทอร์มินัลของคุณและเรียกใช้:
npx create-react-app simple-react-js-chat-application

สิ่งนี้จะสร้างไดเร็กทอรีใหม่ simple-react-js-chat-application ด้วยโครงกระดูก ReactJS พื้นฐาน เราจะไม่ผ่านโครงสร้างของโครงการฐานในขณะนี้

2. ติดตั้งการพึ่งพา

ขั้นตอนต่อไปคือการติดตั้งการพึ่งพาที่จำเป็นสำหรับไคลเอนต์ส่วนหน้าของเรา ที่เทอร์มินัลของคุณ:

  • ไปที่ไดเร็กทอรีโครงการ:
    cd simple-react-js-chat-application
  • วิ่ง:
    npm install axios emoji-mart node-sass skeleton-css socket.io-client uuid

สิ่งนี้จะติดตั้งการพึ่งพาข้อกำหนดเบื้องต้น:

  • axios – เราใช้มันเพื่อโทรไปยังแบ็กเอนด์เพื่อดึงช่องและข้อความ
  • emoji-mart - เป็นส่วนประกอบ React สำหรับอีโมจิ
  • skeleton-css - เทมเพลต CSS ที่ตอบสนองอย่างง่าย
  • socket.io-client – ​​แพ็คเกจ NPM สำหรับเชื่อมต่อกับซ็อกเก็ต
  • uuid – ไลบรารีรหัสผู้ใช้ที่ไม่ซ้ำกัน
  • node-sass – เราจะใช้ SCSS

3. สร้างเซิร์ฟเวอร์ส่วนหลัง

ในการใช้ Socket.IO เราจำเป็นต้องสร้างเซิร์ฟเวอร์ที่จะจัดการกับเหตุการณ์และจุดปลาย API บางส่วน เช่น ดึงช่องสัญญาณและข้อความ ในกรณีนี้ เราจะใช้เซิร์ฟเวอร์ที่จัดการง่ายที่สุดใน NodeJS

เริ่มต้นด้วยการสร้างไดเร็กทอรี เซิร์ฟเวอร์ ใหม่ในโฟลเดอร์ src จากนั้นเริ่มสร้างไฟล์ต่อไปนี้:

ไฟล์ Package.json

ไฟล์ package.json ระบุการจัดการ npm การพึ่งพาและการพึ่งพา dev เป็นไฟล์ JSON จริง ไม่ใช่วัตถุ JavaScript

ฟิลด์หลักที่ Node.JS ต้องการคือชื่อและเวอร์ชัน ชื่อนี้มีชื่อและเวอร์ชันของโปรเจ็กต์ – เวอร์ชันของแพ็คเกจ

 { "name": "server", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "cors": "^2.8.5", "express": "^4.17.1", "socket.io": "^3.0.4", "uuid": "^8.3.2" } }

ไฟล์ Server.js

ไฟล์นี้เป็นไปตามตรรกะที่เซิร์ฟเวอร์ส่วนหลังจัดการการสร้างอินสแตนซ์ของเซิร์ฟเวอร์ เส้นทางที่กำหนดเอง และเหตุการณ์/ปล่อยตัวฟัง

 const http = require("http"); const express = require("express"); const cors = require("cors"); const socketIO = require("socket.io"); const { addMessage, getChannelMessages } = require("./messages"); const { channels, addUserToChannel } = require("./channels"); const { addUser, removeUser } = require("./users"); const app = express(); app.use(cors()); const server = http.createServer(app); const io = socketIO(server, { cors: { origin: "*", }, }); const PORT = process.env.PORT || 8080; io.on("connection", (socket) => { // Get nickname and channel. const { nickname, channel } = socket.handshake.query; console.log(`${nickname} connected`); // Join the user to the channel. socket.join(channel); addUser(nickname, socket.id); addUserToChannel(channel, nickname); // Handle disconnect socket.on("disconnect", () => { console.log(`${nickname} disconnected`); removeUser(nickname); }); socket.on("CHANNEL_SWITCH", (data) => { const { prevChannel, channel } = data; if (prevChannel) { socket.leave(prevChannel); } if (channel) { socket.join(channel); } }); socket.on("MESSAGE_SEND", (data) => { addMessage(data); const { channel } = data; socket.broadcast.to(channel).emit("NEW_MESSAGE", data); }); }); app.get("/channels/:channel/messages", (req, res) => { const allMessages = getChannelMessages(req.params.channel); return res.json({ allMessages }); }); app.get("/getChannels", (req, res) => { return res.json({ channels }); }); server.listen(PORT, () => console.log(`Server listening to port ${PORT}`));

ไฟล์ Users.js

องค์ประกอบนี้รับผิดชอบสำหรับ "ผู้ใช้" ของแอปพลิเคชันที่เรากำลังสร้าง ในกรณีนี้ เราต้องเพิ่ม/ลบผู้ใช้เท่านั้น

 const users = {}; const addUser = (nickname, socketId) => { users[nickname] = socketId; } const removeUser = (nickname) => { if(users.hasOwnProperty(nickname)) { delete users[nickname]; } } module.exports = { users, addUser, removeUser };

ไฟล์ Messages.js

ไฟล์นี้เพิ่มฟังก์ชันสำหรับข้อความ เช่น การเพิ่มลงในอาร์เรย์และรับข้อความช่องสัญญาณเฉพาะ

 const messages = []; const addMessage = (data) => { messages.push(data); return data; }; const getChannelMessages = (channel) => messages.filter((message) => message.channel === channel); module.exports = { addMessage, getChannelMessages };

A Channels.js FIle

ไฟล์นี้มีตรรกะสำหรับแชนเนล – เริ่มต้นค่าเริ่มต้นและฟังก์ชันเพื่อเพิ่มผู้ใช้ในแชนเนล

 const channels = [ { id: 1, name: "general", users: [], }, { id: 2, name: "test 1", users: [], }, { id: 3, name: "test 2", users: [], }, ]; const addUserToChannel = (channel, nickname) => { channels.filter((c) => c.name === channel).map((c) => { c.users.push(nickname); return c; }); } module.exports = { channels, addUserToChannel };

รันเทอร์มินัลและในไดเร็กทอรีติดตั้งการพึ่งพา NPM:

 npm run install

รอให้ NPM เสร็จสิ้นและเซิร์ฟเวอร์พร้อม ทดลองวิ่งได้

 npm start

เริ่มต้นเซิร์ฟเวอร์แอปแชท

4. สร้างส่วนหน้า

สิ่งสุดท้ายที่ต้องทำตามลำดับ แต่ไม่มีความสำคัญ เพื่อให้บรรลุคือการสร้างส่วนหน้าของแอปพลิเคชัน ส่วนหน้าจะสื่อสารกับเซิร์ฟเวอร์ส่วนหลังเพื่อให้คุณสมบัติหลัก เช่น ป้อนชื่อเล่น สลับระหว่างช่องสัญญาณ และส่งข้อความ

เริ่มจากหน้าจอเริ่มต้นของแอปพลิเคชันกันก่อน
ไปที่โฟลเดอร์ src และเปิด App.js จากนั้นแทนที่เนื้อหาด้วยรายการด้านล่าง:

 import "./index.css"; import "./App.css"; import { useState } from "react"; import Chat from "./components/Chat/Chat"; import LoginDialog from "./components/LoginDialog"; function App() { const [nickname, setNickname] = useState(""); const [loggedIn, setLoggedIn] = useState(false); const handleNicknameChange = (event) => { setNickname(event.target.value.trim()); }; const handleNicknameSubmit = (e) => { if (!nickname.length) return; e.preventDefault(); setLoggedIn(true); }; return ( <div className="main-div"> {!loggedIn ? ( <LoginDialog nicknameChange={handleNicknameChange} nicknameSubmit={handleNicknameSubmit} /> ) : ( <Chat nickname={nickname} /> )} </div> ); } export default App;

คอมโพเนนต์ของแอปประกอบด้วยตรรกะสำหรับสถานะของชื่อเล่นและระบุว่าผู้ใช้ "ลงชื่อเข้าใช้" อยู่หรือไม่ นอกจากนี้ยังแสดงองค์ประกอบที่เหมาะสม ไม่ว่าจะเป็น LoginDialog หรือ Chat – ซึ่งขึ้นอยู่กับสถานะ

เพิ่มรูปร่าง

เปิด App.css และแทนที่เนื้อหาด้วย:

 .login-container { position: fixed; left: 10%; right: 10%; top: 50%; transform: translate(0, -50%); display: flex; flex-direction: column; } .text-input-field { padding: 24px 12px; border-radius: 7px; font-size: 24px; } .text-input-field:focus { outline: none; } .login-button { margin-top: 20px; padding: 24px 12px; font-size: 28px; background-color: rgb(0, 132, 255); color: white; font-weight: 600; text-align: center; text-decoration: none; border-radius: 7px; }

สร้างตัว ช่วย โฟลเดอร์ใหม่และวางไฟล์ที่มีชื่อ socket.js ไว้ข้างใน

 import io from "socket.io-client"; import axios from "axios"; let socket; const SOCKET_URL = "http://localhost:8080"; export const initiateSocket = (channel, nickname) => { socket = io(SOCKET_URL, { query: { channel, nickname }, }); console.log("Connecting to socket"); if (socket && channel) { socket.emit("CHANNEL_JOIN", channel); } }; export const switchChannel = (prevChannel, channel) => { if (socket) { socket.emit("CHANNEL_SWITCH", { prevChannel, channel }); } }; export const subscribeToMessages = (callback) => { if (!socket) { return; } socket.on("NEW_MESSAGE", (data) => { callback(null, data); }); }; export const sendMessage = (data) => { if (!socket) { return; } socket.emit("MESSAGE_SEND", data); }; export const fetchChannels = async () => { const response = await axios.get(`${SOCKET_URL}/getChannels`); return response.data.channels; }; export const fetchChannelMessages = async (channel) => { const response = await axios.get( `${SOCKET_URL}/channels/${channel}/messages` ); return response.data.allMessages; };

ส่วนประกอบนี้ส่งออกฟังก์ชันตัวช่วยที่จำเป็นซึ่งเราจะใช้ในภายหลังในส่วนประกอบ React เพื่อสื่อสารกับเซิร์ฟเวอร์ส่วนหลัง

เราเกือบจะพร้อมแล้ว!
ตอนนี้เราจะดำเนินการสร้างกล่องโต้ตอบสำหรับการระบุชื่อเล่นและรูปแบบการแชทต่อไป

ตั้งค่าการเข้าสู่ระบบ

สร้าง ส่วนประกอบ โฟลเดอร์ใหม่และดำเนินการสร้างส่วนประกอบที่จำเป็นสำหรับแอปพลิเคชันต่อไป

เข้าสู่ระบบDialog.js:

 function LoginDialog({ nicknameChange, nicknameSubmit }) { return ( <div className="dialog-container"> <div className="dialog"> <form className="dialog-form" onSubmit={nicknameSubmit}> <label className="username-label" htmlFor="username"> Nickname: </label> <input className="username-input" autoFocus onChange={nicknameChange} type="text" name="userId" placeholder="Enter your nickname to continue" /> <button type="submit" className="submit-btn"> Continue </button> </form> </div> </div> ); } export default LoginDialog;

นี่คือองค์ประกอบหน้าจอการเข้าสู่ระบบซึ่งจะเปิดขึ้นเมื่อโหลดแอปพลิเคชันในครั้งแรก ณ จุดนี้ไม่มีชื่อเล่นให้ มันมีมาร์กอัปเท่านั้นและไม่มีอะไรเกี่ยวข้องกับสถานะที่นี่ ดูเหมือนว่าจากรหัสสถานะและตัวจัดการจะถูกส่งผ่านอุปกรณ์ประกอบฉาก

ทำให้การแชทมีชีวิตชีวา

ดำเนินการต่อด้วยการสร้างอีกหนึ่งโฟลเดอร์ – Chat ซึ่งเราจะสร้างองค์ประกอบหลายอย่าง:

Chat.js

 import { useEffect, useRef, useState } from "react"; import "skeleton-css/css/normalize.css"; import "skeleton-css/css/skeleton.css"; import "./Chat.scss"; import { initiateSocket, switchChannel, fetchChannels, fetchChannelMessages, sendMessage, subscribeToMessages, } from "../../helpers/socket"; import { v4 as uuidv4 } from "uuid"; import "emoji-mart/css/emoji-mart.css"; import Channels from "./Channels"; import ChatScreen from "./ChatScreen"; function Chat({ nickname }) { const [message, setMessage] = useState(""); const [channel, setChannel] = useState("general"); const [channels, setChannels] = useState([]); const [messages, setMessages] = useState([]); const [messagesLoading, setMessagesLoading] = useState(true); const [channelsLoading, setChannelsLoading] = useState(true); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const prevChannelRef = useRef(); useEffect(() => { prevChannelRef.current = channel; }); const prevChannel = prevChannelRef.current; useEffect(() => { if (prevChannel && channel) { switchChannel(prevChannel, channel); setChannel(channel); } else if (channel) { initiateSocket(channel, nickname); } }, [channel]); useEffect(() => { setMessages([]); setMessagesLoading(true); fetchChannelMessages(channel).then((res) => { setMessages(res); setMessagesLoading(false); }); }, [channel]); useEffect(() => { fetchChannels().then((res) => { setChannels(res); setChannelsLoading(false); }); subscribeToMessages((err, data) => { setMessages((messages) => [...messages, data]); }); }, []); const handleMessageChange = (event) => { setMessage(event.target.value); }; const handleMessageSend = (e) => { if (!message) return; e.preventDefault(); const data = { id: uuidv4(), channel, user: nickname, body: message, time: Date.now(), }; setMessages((messages) => [...messages, data]); sendMessage(data); setMessage(""); }; const handleEmojiSelect = (emoji) => { const newText = `${message}${emoji.native}`; setMessage(newText); setShowEmojiPicker(false); }; return ( <div className="chat-container"> <Channels nickname={nickname} channelsLoading={channelsLoading} channels={channels} }channel={channel} setChannel={setChannel} /> <ChatScreen channel={channel} messagesLoading={messagesLoading} messages={messages} showEmojiPicker={showEmojiPicker} handleEmojiSelect={handleEmojiSelect} handleMessageSend={handleMessageSend} setShowEmojiPicker={setShowEmojiPicker} message={message} handleMessageChange={handleMessageChange} /> </div> ); export default Chat;

นี่เป็นองค์ประกอบหลักที่ยึดตรรกะของแอปพลิเคชันแชทไว้ เมื่อผู้ใช้ "เข้าสู่ระบบ" มันรองรับตัวจัดการและสถานะสำหรับช่องทาง ข้อความ และการส่งข้อความ

สไตล์มันขึ้น

มาต่อกันที่สไตล์สำหรับส่วนประกอบและส่วนประกอบย่อย:

Chat.scss

 .chat-container { width: 100vw; height: 100vh; display: grid; grid-template-columns: 1fr 4fr; } .right-sidebar { border-left: 1px solid #ccc; } .left-sidebar { border-right: 1px solid #ccc; } .user-profile { height: 70px; display: flex; align-items: flex-start; padding-right: 20px; padding-left: 20px; justify-content: center; flex-direction: column; border-bottom: 1px solid #ccc; } .user-profile span { display: block; } .user-profile .username { font-size: 20px; font-weight: 700; } .chat-channels li, .room-member { display: flex; align-items: center; padding: 15px 20px; font-size: 18px; color: #181919; cursor: pointer; border-bottom: 1px solid #eee; margin-bottom: 0; } .room-member { justify-content: space-between; padding: 0 20px; height: 60px; } .send-dm { opacity: 0; pointer-events: none; font-size: 20px; border: 1px solid #eee; border-radius: 5px; margin-bottom: 0; padding: 0 10px; line-height: 1.4; height: auto; } .room-member:hover .send-dm { opacity: 1; pointer-events: all; } .presence { display: inline-block; width: 10px; height: 10px; background-color: #ccc; margin-right: 10px; border-radius: 50%; } .presence.online { background-color: green; } .chat-channels .active { background-color: #eee; color: #181919; } .chat-channels li:hover { background-color: #d8d1d1; } .room-icon { display: inline-block; margin-right: 10px; } .chat-screen { display: flex; flex-direction: column; height: 100vh; } .chat-header { height: 70px; flex-shrink: 0; border-bottom: 1px solid #ccc; padding-left: 10px; padding-right: 20px; display: flex; flex-direction: column; justify-content: center; } .chat-header h3 { margin-bottom: 0; text-align: center; } .chat-messages { flex-grow: 1; overflow-y: auto; display: flex; flex-direction: column; justify-content: flex-end; margin-bottom: 0; min-height: min-content; position: relative; } .message { padding-left: 20px; padding-right: 20px; margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center; } .message span { display: block; text-align: left; } .message .user-id { font-weight: bold; } .message-form { border-top: 1px solid #ccc; width: 100%; display: flex; align-items: center; } .message-form, .message-input { width: 100%; margin-bottom: 0; } .rta { flex-grow: 1; } .emoji-mart { position: absolute; bottom: 20px; right: 10px; } input[type="text"].message-input, textarea.message-input { height: 50px; flex-grow: 1; line-height: 35px; padding-left: 20px; border-radius: 0; border-top-left-radius: 0; border-top-right-radius: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; border: none; font-size: 16px; color: #333; min-height: auto; overflow-y: hidden; resize: none; border-left: 1px solid #ccc; } .message-input:focus { outline: none; } .toggle-emoji { border: none; width: 50px; height: auto; padding: 0; margin-bottom: 0; display: flex; align-items: center; justify-content: center; } .toggle-emoji svg { width: 28px; height: 28px; } /* RTA ========================================================================== */ .rta { position: relative; border-left: 1px solid #ccc; display: flex; flex-direction: column; } .rta__autocomplete { position: absolute; width: 300px; background-color: white; border: 1px solid #ccc; border-radius: 5px; } .rta__autocomplete ul { list-style: none; text-align: left; margin-bottom: 0; } .rta__autocomplete li { margin-bottom: 5px; padding: 3px 20px; cursor: pointer; } .rta__autocomplete li:hover { background-color: skyblue; } /* Dialog ========================================================================== */ .dialog-container { position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; } .dialog { width: 500px; background-color: white; display: flex; align-items: center; } .dialog-form { width: 100%; margin-bottom: 0; padding: 20px; } .dialog-form > * { display: block; } .username-label { text-align: left; font-size: 16px; } .username-input { width: 100%; } input[type="text"]:focus { border-color: #5c8436; } .submit-btn { color: #5c8436; background-color: #181919; width: 100%; } .submit-btn:hover { color: #5c8436; background-color: #222; }

ChatMessages.js

 import ChatMessages from "./ChatMessages"; import MessageForm from "./MessageForm"; function ChatScreen({ channel, messagesLoading, messages, showEmojiPicker, handleEmojiSelect, handleMessageSend, setShowEmojiPicker, message, handleMessageChange, }) { return ( <section className="chat-screen"> <header className="chat-header"> <h3>#{channel}</h3> </header> <ChatMessages messagesLoading={messagesLoading} messages={messages} /> <footer className="chat-footer"> <MessageForm emojiSelect={handleEmojiSelect} handleMessageSend={handleMessageSend} setShowEmojiPicker={setShowEmojiPicker} showEmojiPicker={showEmojiPicker} message={message} handleMessageChange={handleMessageChange} /> </footer> </section> ); } export default ChatScreen;

องค์ประกอบหลักสำหรับหน้าจอแชทประกอบด้วยส่วนประกอบ ChatMessages และ MessageForm

MessageForm.js

 import { Smile } from "react-feather"; import { Picker } from "emoji-mart"; function MessageForm({ emojiSelect, handleMessageSend, setShowEmojiPicker, showEmojiPicker, message, handleMessageChange, }) { let messageInput; const handleEmojiSelect = (emoji) => { emojiSelect(emoji); messageInput.focus(); }; return ( <div> {showEmojiPicker ? ( <Picker title="" set="apple" onSelect={handleEmojiSelect} /> ) : null} <form onSubmit={handleMessageSend} className="message-form"> <button type="button" onClick={() => setShowEmojiPicker(!showEmojiPicker)} className="toggle-emoji" > <Smile /> </button> <input type="text" value={message} ref={(input) => (messageInput = input)} onChange={handleMessageChange} placeholder="Type your message here..." className="message-input" /> </form> </div> ); } export default MessageForm;

ประกอบด้วยตรรกะและการแสดงผลของช่องป้อนข้อมูลสำหรับข้อความ – ตัวเลือกอิโมจิและช่องป้อนข้อมูลสำหรับข้อความ

สร้างแถบด้านข้าง

องค์ประกอบสุดท้ายของโครงสร้างแอปพลิเคชันประกอบด้วย "แถบด้านข้าง" - นั่นคือรายการช่อง

Channels.js

 import { useState } from "react"; function Channels({ nickname, channelsLoading, channels, channel, setChannel, }) { return ( <aside className="sidebar left-sidebar"> <div className="user-profile"> <span className="username">@ {nickname}</span> </div> <div className="channels"> <ul className="chat-channels"> {channelsLoading ? ( <li> <span className="channel-name">Loading channels....</span> </li> ) : channels.length ? ( channels.map((c) => { return ( <li key={c.id} onClick={() => setChannel(c.name)} className={c.name === channel ? "active" : ""} > <span className="channel-name">{c.name}</span> </li> ); }) ) : ( <li> <span className="channel-name">No channels available</span> </li> )} </ul> </div> </aside> ); } export default Channels;

แอปพลิเคชันพร้อมแล้ว สิ่งเดียวที่ต้องพิจารณาที่นี่คือการตรวจสอบ URL ของ API ใน helpers/socket.js :

 const SOCKET_URL = "http://localhost:8080";

ตรวจสอบให้แน่ใจว่าได้เปลี่ยนตาม URL และ PORT สำหรับเซิร์ฟเวอร์ส่วนหลังที่คุณใช้อยู่

เรียกใช้ทั้งส่วนหน้าและเซิร์ฟเวอร์:

ไปที่ไดเร็กทอรี root และรัน:
npm start


ไปที่ src/server และเรียกใช้:
npm start

ตอนนี้คุณสามารถเปิด http://localhost:3000 หรือพอร์ตอื่น ๆ ที่คุณใช้สำหรับส่วนหน้าและเข้าถึงได้

โปรดทราบว่าแอปพลิเคชันนี้มีคุณสมบัติพื้นฐานมาก ไม่มีการรับรองความถูกต้องในการเข้าสู่ระบบ – คุณต้องการเพียงชื่อเล่นเท่านั้น และคุณจะไม่สามารถสร้างช่องของคุณเองได้ - คุณสามารถสลับระหว่างช่องคงที่ที่มีให้เท่านั้น

ทำได้ดี!

คุณทำตามคำแนะนำของเราแล้ว และตอนนี้คุณมีแอปพลิเคชันแชทแบบ Slack ที่ใช้งานง่ายขึ้นแล้ว

อย่าลังเลที่จะทดลองและขยายแอป คุณสามารถเพิ่มสิ่งต่าง ๆ เช่น:

  • คุณลักษณะ "ผู้ใช้กำลังพิมพ์"
  • คุณลักษณะสำหรับการเข้าร่วม/สร้างช่อง
  • รับรองความถูกต้อง
  • รูปประจำตัวของผู้ใช้
  • ความสามารถในการแสดงว่าผู้ใช้รายใดใช้งานออนไลน์อยู่

แจ้งให้เราทราบว่าคุณชอบมันอย่างไร และหากคุณต้องการความช่วยเหลือเพิ่มเติม อย่าลังเลที่จะติดต่อเรา