0
0

Node.js, Express, Socket.io 使った簡易チャットの作成

Last updated at Posted at 2023-11-28

Node.js, Express, Socket.io(双方向通信) 使って簡易チャットを作ってみる
前に作った WebSocket のやつでフロント、バック分かれるのやだなと思い Express 使ってます
あと Socket.io も使ってみたかったのでお試しでやってみました
完成したやつは github においてます
https://github.com/sueasen/chat-socket-sample

前に作ったやつはこちら...

見た目は某〇INE風、実行した環境とかは以下です

  • Windwos11
  • Node.js v18.17.1
  • express v4.18.2
  • socket.io v4.7.2
  • nodemon v3.0.1

プロジェクトフォルダ作成・移動

mkdir chat
cd chat

node 初期化

npm init -y

express, socket.io インストール

npm i socket.io express

nodemon インストール

こちらだけ開発時のみインストール、モジュール修正が起動時に反映されるようになる

npm i -D nodemon

server.js 作成

フォルダ分けてもOK、ここでは直下に作成していく
やってることはざっくりと以下になる

  • express を生成して app に設定、それからhttpサーバ作成
  • 上で作ったサーバから socket.io のサーバ生成して io に設定
  • app にベースのディレクトリを設定(htmlとか置くディレクトリ)
  • 接続時の処理を設定 io.on('connection', (socket) => {処理}
  • 上の処理の中でクライアントとのやり取りを設定
    • socket.on('処理名', 処理) でクライアントからくるイベント設定
    • io.emit('処理名', 値) でクライアントにおくるイベント設定
server.js
const path = require('path');
const http = require('http');
const express = require('express');
const { Server } = require('socket.io');

// ポート番号
const port = '3000';

// express, socket.io 生成
const app = express();
const server = http.createServer(app);
const io = new Server(server);

// サーバで公開するディレクトリ設定(htmlとか置く場所)
app.use(express.static(path.join(__dirname, 'public')));

// 接続時の処理、socket が接続情報
io.on('connection', (socket) => {

  // socket.id のクライアントのみに socket.id を送信(connectidイベント)
  io.to(socket.id).emit('connectid', socket.id);

  // messageイベント受信
  socket.on('message', (data) => {
    // 接続中のクライアント全員に data を送信(messageイベント)
    io.emit('message', data);
  });

  // 切断時処理
  socket.on('disconnect', () => {});
});

// ポート3000番でサーバを起動します。
server.listen(port, () => {
  console.log(`listening on *:${port}`);
});

チャット用html, css, js作成

express の app で設定した public に作る必要あり
socket.io のJSファイル (socket.io.min.js) を html から使うので public フォルダ直下 にコピーする
あとはチャット用の index.html, index.css, index.js を public フォルダ直下に作成する

  • public フォルダ作成
mkdir public
  • socket.io.min.js コピー
cp .\node_modules\socket.io\client-dist\socket.io.min.js .\public\
  • index.js
index.js
const socket = io();
let socketId;
const chatDiv = document.querySelector('#chat');

// socket.io 接続時イベント connectid
socket.on('connectid', function (id) {
  console.log(socketId + ':' + id);
  socketId = id;
});

// message送信処理
function sendMessage() {
  const now = new Date();
  const json = {
    name: document.querySelector('#nameInput').value,
    message: document.querySelector('#msgInput').value,
    time: `${now.toLocaleDateString()} ${now.toLocaleTimeString()}`,
    socketId: socketId,
  };
  // socket に message イベントで送信
  socket.emit('message', JSON.stringify(json));
  document.getElementById('msgInput').value = '';
}

// socket から message イベント受信時の処理
socket.on('message', function (data) {
  const json = JSON.parse(data);
  if (!json.message) return;
  chatDiv.appendChild(createMessage(json));
  chatDiv.scrollTo(0, chatDiv.scrollHeight);
});

// ここから下は DOM の操作
function createMessage(json) {
  const side = json.socketId === socketId ? 'mine' : 'other';
  const sideElement = createDiv(side);
  const sideTextElement = createDiv(`${side}-text`);
  const timeElement = createDiv('time');
  const nameElement = createDiv('name');
  const textElement = createDiv('text');
  timeElement.textContent = json.time;
  nameElement.textContent = json.name;
  textElement.textContent = json.message;
  sideElement.appendChild(sideTextElement);
  sideTextElement.appendChild(timeElement);
  sideTextElement.appendChild(nameElement);
  sideTextElement.appendChild(textElement);
  return sideElement;
}

function createDiv(className) {
  const element = document.createElement('div');
  element.classList.add(className);
  return element;
}

  • index.html
index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Socket.IO Chat</title>
    <link rel="stylesheet" href="index.css" type="text/css" />
  </head>
  <body>
    <div class="container">
      <div class="title">チャット</div>
      <div class="contents scroll" id="chat"></div>
      <div class="contents input">
        <div>
          <input class="name" type="text" id="nameInput" placeholder="name" />
        </div>
        <div>
          <input class="msg" type="text" id="msgInput" placeholder="message" />
        </div>
        <button onclick="sendMessage()">Send</button>
      </div>
    </div>
    <script src="/socket.io.min.js"></script>
    <script src="/index.js"></script>
  </body>
</html>

  • index.css
index.css
.container {
    padding: 0;
    background: #7494c0;
    overflow: hidden;
    max-width: 400px;
    margin: 20px auto;
    font-size: 80%;
}

/* タイトル部分 */
.container .title {
    background: #273246;
    padding: 10px;
    text-align: center;
    font-size: 150%;
    color: #ffffff;
}

/* 会話部分 */
.container .contents {
    padding: 10px;
    overflow: hidden;
    line-height: 135%;
}

.container .scroll {
    height: 500px;
    overflow-y: scroll;
}

/* 相手の会話 */
.container .other {
    width: 100%;
    position: relative;
    display: block;
    margin: 5px;
    clear: both;
}

.container .other .other-text {
    margin-left: 10px;
    max-width: 80%;
}

.container .other .other-text .name {
    font-size: 80%;
    color: #ffffff;
}

.container .other .other-text .time {
    font-size: 40%;
    color: #ffffff;
}

.container .other .text {
    margin: 0;
    position: relative;
    padding: 10px;
    border-radius: 10px 10px 10px 0px;
    background-color: #ffffff;
}


/* 自分の会話 */
.container .mine {
    width: 100%;
    max-width: 80%;
    position: relative;
    display: block;
    margin: 5px;
    clear: both;
    float: right;
}

.container .mine .mine-text {
    margin-right: 10px;
}

.container .mine .mine-text .name {
    font-size: 80%;
    color: #ffffff;
    text-align: right;
}

.container .mine .mine-text .time {
    font-size: 40%;
    color: #ffffff;
    text-align: right;
}

/* コメントエリア */
.container .mine .text {
    padding: 10px;
    border-radius: 10px 10px 0px 10px;
    background-color: #8de055;
    margin: 0;
}

/* 入力部分 */
.container .input {
    background: #a0a3aa;
    padding: 10px;
    width: 100%;
}

.container .input input {
    padding: 5px;
    border-radius: 10px;
    border-style: none;
    margin-right: 5px;
}

.container .input .name {
    width: 20%;
    float: left;
}

.container .input .msg {
    width: 50%;
    float: left;
}

.container .input button {
    padding: 5px 10px;
    border-radius: 5px;
    border-style: none;
    margin-right: 5px;
    margin-left: 10px;
    color: #606165;
}

.container .input button:hover {
    opacity: 0.5;
}

package.json に実行スクリプト追加

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "echo \"Error: no test specified\" && exit 1", 
+    "server": "nodemon server.js"
  },

server.js 実行

npm run server

ブラウザで接続

http://localhost:3000 でチャットのページ開けばOK
name, message 入力して Send 押すと他で開いてる人にメッセージが届く

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0