Advertisement
  1. Code
  2. JavaScript
  3. Node

Чат в реальном времени с NodeJS, Socket.io и ExpressJS

Scroll to top
Read Time: 8 min

() translation by (you can also view the original English article)

NodeJS дает мне возможность писать код на одном из моих любимых языков: JavaScript. Это идеальная технология для создания приложений реального времени. В этом уроке я покажу вам, как создать приложение для веб-чата, используя ExpressJS и Socket.io.


Настройка окружения

Конечно, первое, что нужно сделать, - это установить NodeJS в вашей системе. Если вы являетесь пользователем Windows или Mac, вы можете посетить nodejs.org и загрузить программу установки. Если вы предпочитаете Linux, я бы предложил ссылаться на эту ссылку. Хотя я не буду вдаваться в подробности, если у вас возникнут какие-либо проблемы с установкой, я буду рад вам помочь; просто оставьте комментарий ниже этого сообщения.

После установки NodeJS вы готовы настроить необходимые инструменты.

  1. ExpressJS - это управление сервером и ответ пользователю
  2. Jade - шаблонный движок
  3. Socket.io - позволяет осуществлять связь в реальном времени между фронтендом и бэкендом.

Продолжая работу в пустом каталоге, создайте файл package.json со следующим содержимым.

1
{
2
    "name": "RealTimeWebChat",
3
    "version": "0.0.0",
4
    "description": "Real time web chat",
5
    "dependencies": {
6
        "socket.io": "latest",
7
        "express": "latest",
8
        "jade": "latest"
9
    },
10
    "author": "developer"
11
}

Используя консоль (в командной строке Windows), перейдите в свою папку и выполните:

1
npm install

В течение нескольких секунд вы будете иметь все необходимые зависимости, загруженные в каталог node_modules.


Разработка бэкэнда

Начнем с простого сервера, который будет доставлять HTML-страницу приложения, а затем продолжим с более интересными задачи: связь в реальном времени. Создайте файл index.js со следующим кодом expressjs:

1
var express = require("express");
2
var app = express();
3
var port = 3700;
4
5
app.get("/", function(req, res){
6
    res.send("It works!");
7
});
8
9
app.listen(port);
10
console.log("Listening on port " + port);

Выше мы создали приложение и определили его порт. Затем мы зарегистрировали маршрут, который в этом случае является простым запросом GET без каких-либо параметров. На данный момент обработчик маршрута просто отправляет клиенту некоторый текст. Наконец, внизу, мы запускаем сервер. Для инициализации приложения из консоли выполните:

1
node index.js

Сервер работает, поэтому вы можете открыть http://127.0.0.1:3700/ и увидеть:

1
It works!

Теперь вместо «Это работает» мы должны вернуть HTML. Вместо чистого HTML может быть полезно использовать механизм шаблонов. Jade - отличный выбор, который имеет хорошую интеграцию с ExpressJS. Это то, что я обычно использую в своих проектах. Создайте каталог с именем tpl, и поместите в него следующий файл page.jade:

1
!!!
2
html
3
    head
4
        title= "Real time web chat"
5
    body
6
        #content(style='width: 500px; height: 300px; margin: 0 0 20px 0; border: solid 1px #999; overflow-y: scroll;')

7
        .controls
8
            input.field(style='width:350px;')
9
            input.send(type='button', value='send')

Синтаксис Jade не настолько сложный, но для полного руководства я предлагаю вам обратиться к jade-lang.com. Чтобы использовать Jade с ExpressJS, нам требуются следующие настройки.

1
app.set('views', __dirname + '/tpl');
2
app.set('view engine', "jade");
3
app.engine('jade', require('jade').__express);
4
app.get("/", function(req, res){
5
    res.render("page");
6
});

Этот код информирует Express, где находятся ваши файлы шаблонов, и какой механизм шаблонов следует использовать. Все это определяет функцию, которая будет обрабатывать код шаблона. Как только все будет установлено, мы можем использовать метод .render объекта response и просто отправить наш Jade-код пользователю.

На данный момент вывод не является чем-то особенным; не более чем элемент div (с id content), который будет использоваться как контейнер для сообщений чата и два элемента управления (поле ввода и кнопка), которые мы будем использовать для отправки сообщения.

Поскольку мы будем использовать внешний файл JavaScript, который будет содержать интерфейсную логику, нам нужно сообщить ExpressJS, где искать такие ресурсы. Создайте пустой каталог public и добавьте следующую строку перед вызовом метода .listen.

1
app.use(express.static(__dirname + '/public'));

Все идет нормально; у нас есть сервер, который успешно отвечает на запросы GET. Теперь пришло время добавить интеграцию с Socket.io. Измените эту строку:

1
app.listen(port);

на:

1
var io = require('socket.io').listen(app.listen(port));

Выше мы передали сервер ExpressJS на Socket.io. Фактически, наша связь в реальном времени все равно будет происходить в том же порту.

Двигаемся вперед, нам нужно написать код, который получит сообщение от клиента, и отправить его всем остальным. Каждое приложение Socket.io начинается с обработчика connection. У нас должен быть один:

1
io.sockets.on('connection', function (socket) {
2
    socket.emit('message', { message: 'welcome to the chat' });
3
    socket.on('send', function (data) {
4
        io.sockets.emit('message', data);
5
    });
6
});

Объект, socket, который передается вашему обработчику, на самом деле является сокетом клиента. Подумайте об этом как о соединении между вашим сервером и браузером пользователя. После успешного соединения мы отправляем сообщение типа welcome и, конечно же, связываем другой обработчик, который будет использоваться в качестве получателя. В результате клиент должен выпустить сообщение с именем, send, которое мы поймаем. После этого мы просто пересылаем данные, отправленные пользователем во все другие сокеты с помощью io.sockets.emit.

С помощью приведенного выше кода наш сервер готов принимать и отправлять сообщения клиентам. Давайте добавим код фронтенда.


Разработка Front-end

Создайте chat.js и поместите его в каталог public вашего приложения. Вставьте следующий код:

1
window.onload = function() {
2
3
    var messages = [];
4
    var socket = io.connect('http://localhost:3700');
5
    var field = document.getElementById("field");
6
    var sendButton = document.getElementById("send");
7
    var content = document.getElementById("content");
8
9
    socket.on('message', function (data) {
10
        if(data.message) {
11
            messages.push(data.message);
12
            var html = '';
13
            for(var i=0; i<messages.length; i++) {
14
                html += messages[i] + '<br />';
15
            }
16
            content.innerHTML = html;
17
        } else {
18
            console.log("There is a problem:", data);
19
        }
20
    });
21
22
    sendButton.onclick = function() {
23
        var text = field.value;
24
        socket.emit('send', { message: text });
25
    };
26
27
}

Наша логика завернута в обработчик .onload, чтобы обеспечить полную загрузку всей разметки и внешнего JavaScript. В следующих нескольких строках мы создаем массив, который будет хранить все сообщения, объект socket и несколько ярлыков для наших элементов DOM. Опять же, подобно back-end, мы связываем функцию, которая будет реагировать на активность сокета. В нашем случае это событие message. Когда такое событие происходит, мы ожидаем получить объект, данные, со свойством message. Добавьте это сообщение в наше хранилище и обновите div content. Мы также включили логику отправки сообщения. Это довольно просто, просто испуская сообщение с именем, send.

Если вы откроете http://localhost:3700, вы столкнетесь с некоторыми всплывающими ошибками. Это потому, что нам нужно обновить page.jade, чтобы он содержал необходимые файлы JavaScript.

1
head
2
    title= "Real time web chat"
3
    script(src='/chat.js')
4
    script(src='/socket.io/socket.io.js')

Обратите внимание, что Socket.io управляет доставкой socket.io.js. Вам не нужно беспокоиться о ручной загрузке этого файла.

Мы снова можем запустить наш сервер командой node index.js в консоли и открыть http://localhost:3700. Вы должны увидеть приветственное сообщение. Конечно, если вы отправляете что-то, оно должно быть показано в div. Если вы хотите убедиться, что все работает, откройте новую вкладку (или, лучше, новый браузер) и загрузите приложение. Самое замечательное в Socket.io - это то, что он работает, даже если вы остановите NodeJS-сервер. Интерфейс будет продолжать работать. Как только сервер будет загружен снова, ваш чат тоже будет прекрасен.

В текущем состоянии наш чат не идеален и требует некоторых улучшений.


Улучшения

Первое изменение, которое нам нужно сделать, - это идентификация сообщений. В настоящее время неясно, какие сообщения отправляются и кем. Хорошо, что нам не нужно обновлять наш код NodeJS, чтобы добиться этого. Это потому, что сервер просто перенаправляет объект data. Итак, нам нужно добавить новое свойство и прочитать его позже. Прежде чем вносить поправки в chat.js, добавим новое поле input, в котором пользователь может добавить свое имя. Внутри page.jade измените  div controls:

1
.controls
2
    | Name: 
3
    input#name(style='width:350px;')

4
    br
5
    input#field(style='width:350px;')

6
    input#send(type='button', value='send')

Затем в code.js:

1
window.onload = function() {
2
3
    var messages = [];
4
    var socket = io.connect('http://localhost:3700');
5
    var field = document.getElementById("field");
6
    var sendButton = document.getElementById("send");
7
    var content = document.getElementById("content");
8
    var name = document.getElementById("name");
9
10
    socket.on('message', function (data) {
11
        if(data.message) {
12
            messages.push(data);
13
            var html = '';
14
            for(var i=0; i<messages.length; i++) {
15
                html += '<b>' + (messages[i].username ? messages[i].username : 'Server') + ': </b>';
16
                html += messages[i].message + '<br />';
17
            }
18
            content.innerHTML = html;
19
        } else {
20
            console.log("There is a problem:", data);
21
        }
22
    });
23
24
    sendButton.onclick = function() {
25
        if(name.value == "") {
26
            alert("Please type your name!");
27
        } else {
28
            var text = field.value;
29
            socket.emit('send', { message: text, username: name.value });
30
        }
31
    };
32
33
}

Чтобы обобщить эти изменения, мы имеем:

  1. Добавлен новый ярлык для input ввода имени пользователя
  2. Немного обновлено представление сообщений
  3. Добавлено новое свойство username к объекту, который отправляется на сервер

Если количество сообщений становится слишком высоким, пользователю необходимо прокрутить div:

1
content.innerHTML = html;
2
content.scrollTop = content.scrollHeight;

Имейте в виду, что вышеупомянутое решение, скорее всего, не будет работать в IE7 и ниже, но это нормально: пришло время для IE7 чтобы исчезнуть. Однако, если вы хотите обеспечить его поддержку, не стесняйтесь использовать jQuery:

1
$("#content").scrollTop($("#content")[0].scrollHeight);

Также было бы неплохо, если поле ввода очистится после отправки сообщения:

1
socket.emit('send', { message: text, username: name.value });
2
field.value = "";

Последней скучной проблемой является необходимость каждый раз нажимать кнопку send. С помощью jQuery мы можем слушать, когда пользователь нажимает клавишу Enter.

1
$(document).ready(function() {
2
    $("#field").keyup(function(e) {
3
        if(e.keyCode == 13) {
4
            sendMessage();
5
        }
6
    });
7
});

Функция, sendMessage, может быть зарегистрирована, например:

1
sendButton.onclick = sendMessage = function() {
2
    ...
3
};

Обратите внимание, что это не лучшая практика, так как она зарегистрирована как глобальная функция. Но, для нашего небольшого теста это вполне нормально.


Заключение

NodeJS - чрезвычайно полезная технология и дает нам большую силу и радость, особенно если учесть, что мы можем писать чистый JavaScript. Как вы можете видеть, всего с несколькими строками кода нам удалось написать полнофункциональное приложение чата реального времени. Довольно аккуратно!

Хотите узнать больше о создании веб-приложений с помощью ExpressJS? Мы тебя поймали!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.