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('処理名', 値) でクライアントにおくるイベント設定
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
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
<!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
.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 押すと他で開いてる人にメッセージが届く