Статьи

Создайте веб-приложение с Backbone.js и Socket.IO

Эта статья была рецензирована Томасом Греко и Марком Таулером . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Как многие из вас знают, Backbone.js — это хорошо известная инфраструктура MV *. Он размещен на GitHub и дает структуру веб-приложениям, предоставляя моделям привязку значения ключа и настраиваемые события, коллекции с богатым API перечислимых функций, представления с декларативной обработкой событий и соединяет все это с существующим API через RESTful JSON. интерфейс.

В этой статье мы будем использовать встроенную функцию Backbone, называемую Events, для реализации парадигмы асинхронного обмена сообщениями во избежание связывания. Идея состоит в том, чтобы разделить группы кода, которые сильно зависят друг от друга.

Пример, который я собираюсь показать, — это инструмент визуализации графиков, где данные красиво синхронизируются между пользователями. Использование WebSockets позволит открыть интерактивный сеанс связи между браузером пользователя и сервером.

Цель состоит в том, чтобы сделать пример как можно более простым. Изученные здесь концепции помогут вам значительно снизить сцепление. Кроме того, это очень полезный подход для создания расширяемого, гибкого и обслуживаемого кода. В конце нашего эксперимента мы получим следующий результат:

инструмент визуализации графика

Backbone.js

Backbone.js — это инфраструктура для создания одностраничных приложений путем предоставления моделей , представлений , контроллеров , коллекций и пользовательских событий . Его структура помогает нам отделить пользовательский интерфейс от бизнес-логики. В этой статье я познакомлю вас только с некоторыми из этих элементов, но если вы хотите получить более подробное руководство, я предлагаю вам прочитать статью « Основы Backbone.js: модели, представления, коллекции и шаблоны ».

Модель представляет данные, и их можно создать, расширив Backbone.Model :

 var MyModel = Backbone.Model.extend({ initialize: function () { console.log('model initialized'); } }) 

Представление — это способ организации пользовательского интерфейса в логические представления, поддерживаемые моделями. Он не содержит HTML-разметки, а содержит лишь логику представления данных модели пользователю. Для создания представления вам необходимо расширить Backbone.View следующим образом:

 var MyView = Backbone.View.extend({ el: 'div#my-view-container', initialize: function (options) { this.model = new MyModel() console.log('view initialized') } }) 

События — это модуль, который можно смешивать с любым объектом, давая объекту возможность связывать и запускать пользовательские именованные события. И модель, и представление имеют это событие модуля, которое позволяет нам привязывать события к модели или представлению . Распространенный шаблон — создание представлений, которые прослушивают изменения в моделях. Этот метод обычно предназначен, чтобы позволить представлению автоматически повторно визуализировать себя, когда базовые данные изменяются.

Чтобы дать вам пример того, как эти элементы работают вместе, я создал демо на CodePen .

Всякий раз, когда мы меняем входные данные , представление меняет нашу модель . После нажатия кнопки ОК представление отображает новое значение id .

Связь в реальном времени между сервером и клиентом

WebSockets — это продвинутый способ, позволяющий открыть интерактивный сеанс связи между браузером пользователя и сервером. С помощью этого API пользователи могут отправлять сообщения на сервер и получать управляемые событиями ответы, не запрашивая ответ у сервера. Этот контракт представляет собой соглашение, которое описывает правила и ожидаемое поведение и влечет за собой действия с обеих сторон. Когда эти две части соединяются через API, это служит связующим звеном для интеграции сервера и клиента.

По аналогии с мостом, однажды введенные данные и инструкции могут пересечь мост. Например, сервер получает инструкцию, выполняет действие и после обработки инструкции возвращает информацию обратно в веб-приложение. Дари и получай между сторонами. С помощью этого уровня абстракции (API) мы скрываем сложные вещи веб-сервисов без необходимости разбираться в деталях о том, как сервер выполнил действие.

Socket.IO — это библиотека JavaScript для веб-приложений реального времени. Это позволяет двунаправленную связь между веб-клиентами и сервером. Обе стороны имеют идентичный API и управляются событиями, как Node.js. Чтобы открыть этот интерактивный сеанс связи между браузером и сервером, нам нужно создать HTTP-сервер для обеспечения связи в реальном времени. Это позволит нам излучать и получать сообщения. socket — это объект, который обрабатывает эту связь между веб-клиентами и сервером.

Приведенный ниже код создает этот сервер, используя socket.IO с платформой Express .

 var express = require('express'), app = express(), http = require('http').Server(app), io = require('socket.io')(http); http.listen(process.env.PORT || 5000, function(){ console.log('listening on *:5000'); }); 

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

 io.on('connection', function(socket) { console.log('a user connected'); socket.on('disconnect', function(){ console.log('user disconnected'); }); }); 

В браузере клиент соединяется с сервером, вызывающим функцию io которая возвращает socket для соединения:

 var socket = io("http://pubsub-example-with-backbone.herokuapp.com/"); 

Сказал, что давайте создадим простое сообщение, что данные и инструкции могут пересекать мост.

 var socket = io("http://pubsub-example-with-backbone.herokuapp.com/") // retrieve all nodes already stored in the server socket.emit('retrieve-all-nodes') $("button#add-new-node") .on('click', function () { socket.emit('add-node', {}, function (obj) { console.log(obj) }) }) socket.on('node-added', function (node) { var $nodes = $("ul#nodes").append(` <li id="${node.id}"> <span>${node.id}</span> <button>x</button> </li> `) $nodes .find(`li#${node.id} button`) .on('click', function () { socket.emit('remove-node', node) }) }) socket.on('node-removed', function (node) { $("ul#nodes").find(`#${node.id}`).remove() }) 

С этим кодом мы можем создать следующую демонстрацию :

Если вы хотите узнать больше о Socket.IO и Express, я предлагаю вам следующие статьи:

Backbone.js с Socket.IO

В этом разделе я собираюсь показать вам пример использования Socket.IO с Backbone.js:

 var MyView = Backbone.View.extend({ el: '#my-backbone-app', events: { 'click button#add-new-node': 'addNewNode' }, initialize: function (options) { var view = this view.socket = io("http://pubsub-example-with-backbone.herokuapp.com/") view.socket.emit('retrieve-all-nodes') view.model = new MyModel() view.socket.on('node-added', function (node) { var $node = $(` <li> <span>${node.id}</span> <button>x</button> </li> `) view.model.set(node.id, $node) view.$el.find("ul#nodes").append($node) $node.on('click', function () { view.socket.emit('remove-node', node) }) }) view.socket.on('node-removed', function (node) { view.model.get(node.id).remove() view.model.unset(node.id, {}) }) console.log('view initialized') }, addNewNode: function () { this.socket.emit('add-node', {}, function (obj) { console.log(obj) }) } }) 

И вот результат:

PubSub с Backbone.js

PubSub — это парадигма асинхронного обмена сообщениями. Это дает нам возможность избежать связи. Связывание — это когда группа кода сильно зависит друг от друга, что означает, что если часть кода изменяется, то требуется обновление всего, что использует этот фрагмент кода.

PubSub — это шаблон, который имеет развязку синхронизации. Он использует систему событий, подобную тому, как работает радио: радиостанция вещает ( публикует ), и любой может слушать ( подписывается ). Более того, вместо непосредственного общения с другими объектами вы можете публиковать сообщения на общей радиостанции. Эта система событий позволяет нам определять события, которые могут передавать аргументы, содержащие значения, необходимые подписчику. Backbone.js делает эту реализацию системы событий довольно простой. Вам просто нужно смешать Backbone.Events в пустой объект следующим образом:

 var EventChannel = _.extend({}, Backbone.Events); 

На данный момент вы можете использовать стандартный trigger и методы для публикации и подписки на сообщения:

 var socket = io("http://pubsub-example-with-backbone.herokuapp.com/") var MyPubSub = $.extend({}, Backbone.Events) MyPubSub.on('disconnect', function () { console.warn('disconnected') }) socket.on('node-added', function (node) { MyPubSub.trigger('node-added', node) }) socket.on('node-removed', function (node) { MyPubSub.trigger('node-removed', node) }) MyPubSub.on('retrieve-all-nodes', function () { socket.emit('retrieve-all-nodes') }) MyPubSub.on('add-node', function (node, cb) { socket.emit('add-node', node, function (obj) { if (cb) { node.id = obj.id cb(node) } }) }) MyPubSub.on('remove-node', function (node) { if (node && node.id) { socket.emit('remove-node', node) } }) 

Сделав это, вы теперь можете удалить socket.io из нашего Backbone View.

 var MyView = Backbone.View.extend({ el: '#my-backbone-app', events: { 'click button#add-new-node': 'addNewNode' }, initialize: function (options) { var view = this view.model = new MyModel() MyPubSub.on('node-added', function (node) { var $node = $(` <li> <span>${node.id}</span> <button>x</button> </li> `) view.model.set(node.id, $node) view.$el.find("ul#nodes").append($node) $node.on('click', function () { MyPubSub.trigger('remove-node', node) }) }) MyPubSub.on('node-removed', function (node) { view.model.get(node.id).remove() view.model.unset(node.id, {}) }) MyPubSub.trigger('retrieve-all-nodes') console.log('view initialized') }, addNewNode: function () { MyPubSub.trigger('add-node', {}, function (obj) { console.log(obj) }) } }) 

Цель состоит в том, чтобы избежать зависимостей между модулями. Каждый модуль может иметь канал, такой как радиостанция, запускающая события (издатели), а любые другие модули могут прослушивать свои события, желая получать уведомления (подписчики).

Полученный результат следующий:

Пример визуализации графика

Наша графическая визуализация использует два модуля на стороне клиента: один для рисования ориентированных графов, а другой для хранения и извлечения данных. Модуль рисования графиков использует инструмент под названием Force Editor . Этот модуль, который мы называем ForceView в коде, позволяет нам позиционировать узлы графа в двумерном пространстве простым и интуитивно понятным способом. Модуль хранения, который мы называем DBaaS , использует Socket.IO для обеспечения двусторонней связи в реальном времени между веб-клиентами и сервером. Никто из них, ForceView и DBaaS , не знает ни о каком другом. Их поведение все в изоляции.

Оба модуля настроены в стиле публикации / подписки, чтобы избежать зависимостей. Они используют систему событий так же, как работает радиостанция, при этом радиостанция вещает (публикует), а радиоприемники слушают (подписываются). Вместо того, чтобы напрямую общаться с другим, каждый модуль публикует свои сообщения на общей « радиостанции », запускающей события на своем собственном канале, а другой также может прослушивать любые другие каналы для событий.

Единственная зависимость здесь — от каждого радиоканала, который имеет очень маленький API. Важно то, какое сообщение запускает канал, и чтобы система правильно реагировала на события. Если они инициируют событие и дают правильную вещь, система будет работать в целом. Посмотрите на изображения ниже, чтобы увидеть, какие события генерируются каждым из этих модулей.

модуль dbaas

модуль принудительного просмотра

Backbone View действует как посредник между ForceView и DBaaS. Это позволяет нам разложить все на полезные маленькие кусочки, а затем заставить эти маленькие кусочки прекрасно работать вместе. Таким образом, код становится более простым для понимания и легко поддерживаемым.

Например, если мы хотим немного настроить его под конкретный вкус, мы можем легко подобрать любой модуль и изменить его так, как нам нравится. Мы могли бы заменить визуализацию графа другими библиотеками графов, например, jqPlot , Dracula , ArborJS , sigmajs , RaphaelJS и так далее . Или мы можем использовать любую базу данных в реальном времени, такую ​​как Firebase , Appbase , Neo4j , TitanDB и т. Д. Хорошей новостью является то, что нам просто нужно изменить один файл, чтобы перейти на другую библиотеку. Изображение ниже иллюстрирует взаимодействие между Backbone View и этими двумя модулями.

Обратите внимание, что мы не используем какую-либо базу данных. Данные хранятся в памяти. То, как мы развязали код, позволяет нам подключаться к любой базе данных.

PubSub как радиостанция

Запуск нашего примера визуализации графика локально

Весь код доступен на GitHub . Вы можете клонировать репозиторий или скачать код .

 git clone https: //github.com/sitepoint-editors/pubsub-example-with-backbone.git 

Затем выполните npm install из консоли, чтобы установить все зависимости.

Затем выполните node server.js чтобы запустить приложение.

Перейдите на http: // localhost: 5000 с вашим браузером, чтобы увидеть запущенное приложение. Если вы просто хотите увидеть приложение в действии, вы можете найти демо здесь .

Выводы

Законченный! Мы просто использовали встроенную функцию Backbone для реализации шаблона PubSub. Кроме того, мы использовали этот шаблон для представления и хранения графических данных в режиме реального времени, когда данные были красиво синхронизированы между пользователями. Как вы видели, мы смешали несколько интересных концепций в отличном примере, чтобы увидеть, как разъединенные части кода работают вместе.

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

Не стесняйтесь делиться своими комментариями в разделе ниже.