Node.js і Express як вони є

Привіт, любителі нашого хаброблога і інші читачі!

Ми плануємо знову відзначитися на полі неувядающего Node.js і розглядаємо можливість видання цієї книги:



Оскільки цілком зрозумілий читацький інтерес «а як він упхнув все це у двісті сторінок, і навіщо мені це потрібно»? під катом пропонуємо переклад доскональної статті Томіслава Пастки про те, навіщо насправді потрібний Node.js.


Введення

Завдяки зростаючій популярності, мова JavaScript в наш час активно розвивається, і сучасна веб-розробка драматично змінилася в порівнянні з недавнім минулим. Ті речі, які ми сьогодні можемо робити в Інтернеті за допомогою JavaScript, що працює на сервері, а також в браузері, було складно собі уявити ще кілька років тому – у кращому випадку, такі можливості існували тільки в пісочницях начебто Flash або Java Applets.
Перш ніж докладно поговорити про Node.js можете почитати про переваги полностекового використання JavaScript. При цьому тісно переплітаються сам мову і формат даних (JSON), що дозволяє оптимально переиспользовать ресурси розробки. Оскільки це гідність притаманна не стільки Node.js скільки JavaScript в цілому, ми не будемо докладно зупинятися на цій темі. Але в цьому і полягає ключова перевага, яку ви купуєте, впроваджуючи Node у свій стек.

Вікіпедії читаємо: «Node.js – це пакет, що включає движок JavaScript V8 від Google, рівень абстракції платформи – бібліотеку libuv, а також базову бібліотеку, яка сама написана на JavaScript.» Крім того, необхідно відзначити, що Райан Даль, автор Node.js, прагнув створювати сайти, що працюють в реальному часі і оснащені push-функцією, під враженням від таких додатків як Gmail". В Node.js він надав програмістам інструмент для роботи з парадигмою неблокірующіх, подійно-орієнтованого введення/виводу.

Через 20 років панування парадигми «веб-додатки без збереження стану на базі комунікації запит-відгук без збереження стану» у нас нарешті є програми з двонаправленою зв'язком в реальному часі.
Коротше: Node.js блищить в додатках реального часу, так як задіює push-технологію через веб-сокети. Що ж в цьому такого революційного? Ну, як вже говорилося, через 20 років використання вищезгаданої парадигми з'явилися такі двосторонні програми, де зв'язок може ініціювати як клієнт, так і сервер, а потім приступати до вільного обміну даними. Така технологія різко контрастує з типовою парадигмою веб-відгуків, де комунікацію завжди ініціює клієнт. Крім того, вся технологія заснована на відкритому веб-стеку (HTML, CSS і JS), робота йде через стандартний порт 80.

Читач може заперечити, що все це було у нас вже не один рік — у вигляді Flash і Java-аплетів — але насправді це були просто пісочниці, використовували Веб в якості транспортного протоколу для доставки даних клієнта. Крім того, вони працювали ізольовано і часто діяли через нестандартні порти, що могло вимагати додаткових прав доступу тощо
Node.js при всіх його достоїнствах в даний час грає ключову роль у технологічному стеку багатьох видатних компаній, безпосередньо залежать від унікальних властивостей Node.

У цій статті ми поговоримо не тільки про те, як досягаються ці переваги, але і чому ви можете віддати перевагу Node.js — або відмовитися від нього — взявши в якості прикладів кілька класичних моделей веб-додатків.
Як це працює?

Основна ідея Node.js: використовувати неблокуючий подієво-орієнтований введення/виведення, щоб залишатися легковажним і ефективним при поводженні з додатками, обробними великі обсяги даних у реальному часі і функціонують на розподілених пристроях.

Докладно.

По суті, це означає, що Node.js не є платформою на всі випадки життя, яка буде домінувати у світі веб-розробки. Навпаки, це платформа для вирішення певних завдань. Розуміти це абсолютно необхідно. Зрозуміло, не варто використовувати Node.js для операцій, інтенсивно навантажувальних процесор, більше того — застосування Node.js у важких обчисленнях фактично анулює всі його переваги. Node.js дійсно хороший для створення швидких масштабованих мережевих додатків, оскільки дозволяє одночасно обробляти величезну кількість сполук з великою пропускною здатністю, що рівноцінно високої масштабованості.

Тонкощі роботи Node.js «під капотом» досить цікаві. У порівнянні з традиційними варіантами веб-сервісів, де кожне з'єднання (запит) породжує новий потік, навантажуючи оперативну пам'ять системи і, зрештою, розбираючи цю пам'ять без залишку, Node.js набагато економічніше: він працює в єдиному потоці, при викликах використовує неблокуючий введення/виведення, дозволяє підтримувати десятки тисяч конкурентних сполук (які існують в циклі подій).



Простий розрахунок: припустимо, кожен потік потенційно може зажадати 2 Мб пам'яті і працює в системі з 8 Гб оперативної пам'яті. В такому разі ми теоретично можемо розраховувати максимум на 4000 конкурентних сполук плюс витрати на перемикання контексту між потоками. Саме з таким сценарієм доводиться мати справу при використанні традиційних веб-сервісів. Node.js, уникаючи всього цього, може масштабуватися більш ніж до мільйона конкурентних з'єднань (якексперименту для підтвердження концепції).

Зрозуміло, виникає питання про поділ єдиного потоку між всіма клієнтськими запитами, саме в цьому полягає основна «пастка» при написанні програм із застосуванням Node.js. По-перше, складні обчислення можуть забити єдиний потік Node.js, що загрожує проблемами для всіх клієнтів (детальніше про це нижче), оскільки вхідні запити будуть блокуватися аж до завершення запитаного обчислення. По-друге, розробники повинні бути дуже уважні і не допускати спливання винятків до базового (найвищого) циклу подій Node.js, оскільки в іншому випадку примірник Node.js завершиться (фактично ж, впаде вся програма).

Щоб уникнути спливання винятків до самої поверхні застосовується наступний прийом: помилки передаються викликає стороні як параметри зворотного дзвінка (а не викидаються, як в інших середовищах). На випадок, якщо якесь необроблене виняток проскочить і спливе, існує безліч парадигм та інструментів, які дозволяють стежити за процесом Node і виконувати необхідне відновлення аварійно завершився примірника (хоча, користувальницькі сеанси при цьому відновити не вдасться). Найбільш поширеними з них є модуль Forever, або робота із застосуванням зовнішніх системних інструментів вискочка і monit.

NPM: Менеджер пакетів Node

Обговорюючи Node.js просто необхідно згадати про існуючу в ньому вбудовану підтримку управління пакетами, для якої застосовується інструмент NPM, за замовчуванням присутній у будь-якій установці Node.js. Ідея модулів NPM багато в чому схожа з Ruby Gems: це набір загальнодоступних компонентів для багаторазового використання, які легко встановити через онлайновий репозиторій; для них підтримується управління версіями і залежностями.

Повний список упакованих модулів знаходиться на сайті NPM npmjs.org, а також доступний за допомогою інструменту NPM CLI, який автоматично встановлюється разом з Node.js. Екосистема модулів абсолютно відкрито, будь-хто може опублікувати в ній власний модуль, який з'явиться в списку репозиторію NPM. Короткий вступ в NPM (трохи староватое, але як і раніше актуальне) знаходиться на сайті howtonode.org/introduction-to-npm.
Деякі з найбільш популярних сучасних NPM-модулів:

  • express: Express.js, фреймворк для веб-розробки для Node.js, написаний в дусі Sinatra, де-факто – стандартний для більшості існуючих сьогодні програм Node.js.
  • connect: Connect – це розширюваний фреймворк, який працює з Node.js як HTTP-сервера, що надає колекцію високопродуктивних «плагінів», відомих під загальною назвою «сполучну» (middleware); служить основою для Express.
  • socket.io і sockjs – серверна частина двох найбільш поширених сьогодні веб-сокетных компонентів.
  • Jade – один з популярних шаблонизаторов, написаний в дусі HAML, за замовчуванням використовується в Express.js.
  • mongo і mongojs – обгортки MongoDB, надають API для об'єктних баз даних MongoDB в Node.js.
  • redis — клієнтська бібліотека Redis.
  • coffee-script – компілятор CoffeeScript, що дозволяє розробникам писати програми з Node.js за допомогою Coffee.
  • underscore lodash, lazy) – найпопулярніша допоміжна бібліотека JavaScript, упакована для використання з Node.js, а також дві аналогічні їй бібліотеки, що забезпечують підвищену продуктивність, оскільки реалізовані трохи інакше.
  • forever – Мабуть, найпоширеніша утиліта, що забезпечує безперебійне виконання сценарію на заданому сайті. Підтримує працездатність вашого процесу Node.js при будь-яких несподіваних збоїв.


Список можна продовжувати. Існує безліч загальнодоступних корисних пакетів, перерахувати їх все тут просто неможливо.

В яких випадках слід використовувати Node.js

ЧАТ

Чат — це найбільш типове багатокористувацький додаток, що працює в реальному часі. Від IRC (були часи), використовують різноманітні відкриті і відкриті протоколи, які функціонують через нестандартні порти, ми прийшли до сучасності, коли все можна реалізувати на Node.js із застосуванням веб-сокетів, що діють через стандартний порт 80.

Програма-чат — дійсно ідеальний продукт для використання Node.js: це легковажне додаток з високим трафіком, інтенсивно обробляє дані (проте майже не споживає обчислювальних потужностей), що працює на безлічі розподілених пристроїв. Крім того, на ньому дуже зручно повчитися, так як при всій простоті воно охоплює більшість парадигм, які може знадобитися використовувати в типовому додатку Node.js.

Спробуємо зобразити, як воно працює.

У найпростішому випадку у нас на сайті єдина кімната для чату, куди користувачі приходять і обмінюються повідомленнями в режимі «один до багатьох» (фактично — «до всіх»). Припустимо, у нас на сайті три відвідувача, і всі вони можуть писати повідомлення на нашому форумі.

На стороні сервера у нас просте додаток Express.js, в якому реалізуються дві речі: 1) обробник запитів GET '/', обслуговуючий веб-сторінку, на якій розташовується як форум з повідомленнями, так і кнопка 'Send', инициализирующая введене нове повідомлення і 2) сервер веб-сокетів, слухає нові повідомлення, що видаються клієнтами веб-сокетів.

На стороні клієнта у нас є HTML-сторінка, на якій налаштоване два обробника: один з них слухає події клацання по кнопці 'Send', яка підхоплює введене повідомлення і спускає його на веб-сокет, а інший слухає нові вхідні повідомлення на клієнті для обслуговування веб-сокетів (тобто, повідомлення, відправлені іншими клієнтами, які сервер збирається відобразити за допомогою цього спеціального клієнта).

Ось що відбувається, коли один з клієнтів відправляє повідомлення:

  1. 1. Браузер підхоплює клацання по кнопці 'Send' за допомогою JavaScript-обробника, забирає значення з поля (т. e., текст повідомлення) і видає повідомлення веб-сокета, скориставшись клієнтом веб-сокетів, підключеним до нашого серверу (цей клієнт ініціалізується разом з веб-сторінкою).
  2. 2. Серверний компонент веб-сокетного з'єднання отримує повідомлення і перенаправляє його всім іншим підключеним клієнтам широкомовним методом.
  3. 3. Всі клієнти отримують нове повідомлення за принципом push за допомогою веб-сокетного компонента, що виконується на веб-сторінці. Потім вони підхоплюють контент повідомлення і оновлюють веб-сторінку, додаючи на форум нову запис.




Це найпростіший приклад. Для більш надійного рішення можна використовувати простий кеш, заснований на сховище Redis. Ще більш просунуте рішення чергу повідомлень, що дозволяє обробляти маршрутизацію повідомлень до клієнтів і забезпечує більш надійний механізм доставки, яка дозволяє компенсувати тимчасові обриви з'єднання або зберігати повідомлення для зареєстрованих клієнтів, поки вони знаходяться в офлайні. Але незалежно від того, які оптимізації ви виконуєте, Node.js все одно буде діяти у відповідності з тими ж базовими принципами: реагувати на події, обробляти безліч конкурентних сполук, підтримувати плавність користувальницьких взаємодій.

API ПОВЕРХ ОБ'ЄКТНОЇ БАЗИ ДАНИХ

Хоча Node.js особливо гарний у контексті додатків, що працюють в реальному часі, він цілком підходить і для надання інформації з об'єктних баз даних (напр. MongoDB). Дані, збережені у форматі JSON, дозволяють Node.js функціонувати без втрати відповідності та без перетворення даних.

Наприклад, якщо ви використовуєте Rails, то вам довелося б перетворювати JSON в двійкові моделі, а потім знову надавати їх у вигляді JSON по HTTP, коли дані будуть споживатися Backbone.js, Angular.js або навіть звичайними викликами jQuery, AJAX. Працюючи з Node.js можна просто надавати ваші об'єкти JSON клієнту через REST API, щоб клієнт споживав їх. Крім того, не доводиться турбуватися про перетворення між JSON і чим завгодно ще при зчитуванні бази даних і запису в неї (якщо ви використовуєте MongoDB). Отже, ви обходитеся без безлічі перетворень, використовуючи універсальний формат серіалізації даних, застосовується і на клієнтові і на сервері, і в базі даних.

ЧЕРЗІ ВВЕДЕННЯ

Якщо ви отримуєте великі обсяги конкурентних даних, база даних може стати вузьким місцем. Як показано вище, Node.js з легкістю обробляє конкурентні з'єднання як такі. Але оскільки звернення до бази даних є блокуючої операцією (в даному випадку), у нас виникають проблеми. Рішення у тому, щоб зафіксувати поведінку клієнта перед тим, як дані насправді будуть записані в базу.

При використанні такого підходу чуйність системи зберігається і під високим навантаженням, що особливо корисно, якщо клієнту не потрібно підтвердження про те, що запис даних пройшла успішно. Типові приклади: логування або запис даних про користувача активності (user tracking), що обробляються за пакетним принципом і не використовуються згодом; операції, підсумок яких повинен відображатися миттєво (наприклад, оновлення кількості «лайків» у Facebook), де прийнятна узгодженість в кінцевому рахунку, так часто використовувана в світі NoSQL.

Дані шикуються в чергу за допомогою спеціальної інфраструктури для кешування і роботи з чергами повідомлень (напр., RabbitMQ,ZeroMQ) і перетравлюються окремим процесом бази даних, призначеними для пакетної запису, або спеціальними сервісами інтерфейсу бази даних, розрахованих на інтенсивні обчислення. Аналогічне поведінку можна реалізувати і за допомогою інших мов/фреймворків, але на іншому апаратному забезпеченні і не з такою високою і стабільною пропускною здатністю.



Коротше кажучи, Node дозволяє відкладати на потім операції запису в базу даних, продовжуючи роботу в такому режимі, як якщо б ці записи завершилися успішно.

ПОТОКОВА ПЕРЕДАЧА ДАНИХ

На більш традиційних веб-платформах HTTP-запити і відгуки трактуються як ізольовані події; але фактично вони являють собою потоки. Цим моментом можна скористатися в Node.js для створення деяких класних можливостей. Наприклад, можна обробляти файли, поки вони ще закачуються, оскільки дані надходять потоком, і ми можемо працювати з ними в онлайновому режимі. Це можна зробити, наприклад, при кодуванні відео або аудіо в реальному часі, а також при встановленні проксі між різними джерелами даних (детальніше див. у наступному розділі).

ПРОКСІ

Node.js цілком можна використовувати в якості сервера проксі, і в такому разі він може обробляти велику кількість одночасних з'єднань в неблокирующем режимі. Це особливо зручно при посередництві між різними сервісами, у яких відрізняється час відгуку, або при зборі даних з безлічі джерел.
Для прикладу давайте розглянемо серверний додаток, обменивающееся інформацією зі сторонніми ресурсами, збирає інформацію з різних джерел або зберігає такі ресурси, як зображення та відео, які потім надаються стороннім хмарних сервісів.

Хоча і існують виділені проксі-сервери, замість них зручно використовувати Node, особливо якщо проксі-інфраструктури не існує, або якщо вам потрібно рішення для локальної розробки. Тут я маю на увазі, що можна створити клієнтське додаток, де буде застосовуватися сервер розробки Node.js, де ми будемо зберігати ресурси і робити проксі/заглушки для запитів до API, а в реальних умовах такі взаємодії вже будуть виконуватися за допомогою виділеного проксі-сервісу (nginx, HAProxy, тощо)

ІНФОРМАЦІЙНА ПАНЕЛЬ БІРЖОВОГО ТРЕЙДЕРА

Повернемося на рівень додатків. Ще один сегмент, де домінують програми для ПК, але їх можна з легкістю замінити веб-рішенням, що працюють в реальному часі — це софт для біржового трейдингу, де відслідковуються котирування, виконуються обчислення і технічний аналіз, вычерчиваются графіки і діаграми.
Якщо задіяти в такому разі веб-рішення для роботи в реальному часі, то користується ним брокер зможе легко переключатися між робочими станціями або місцями. Незабаром ми почнемо помічати таких брокерів на пляжах Флориди… Ібіци… Балі.

ПАНЕЛЬ ДЛЯ МОНІТОРИНГУ ДОДАТКІВ

Це ще один практичний випадок, для якого ідеально підходить модель «Node+веб-сокети». Тут ми відслідковуємо відвідувачів сайту і пропонуємо їх взаємодії в реальному часу (якщо вас цікавить така ідея, то вона вже вирішується за допомогою Hummingbird).

Можна збирати статистику про користувача в реальному часі і навіть перейти на більш високий рівень, доповнивши програму цільовими взаємодіями з відвідувачем, відкриваючи канал зв'язку, як тільки гість досягне певної точки у вашій воронці (якщо вас цікавить така ідея, то вона вже вирішується за допомогою CANDDi).

Уявіть, як можна було б оптимізувати бізнес, якби ви могли в реальному часі дізнаватися, чим займаються ваші користувачі, а також візуалізувати їх взаємодії. Двонаправлені сокети Node.js відкривають перед вами таку можливість.

ІНФОРМАЦІЙНА ПАНЕЛЬ ДЛЯ ВІДСТЕЖЕННЯ СИСТЕМИ

Тепер давайте поговоримо про інфраструктурних аспектах. Припустимо, є SaaS-провайдер, який бажає запропонувати користувачам сторінку для відстеження сервісів (скажімо, статусну сторінку GitHub). Маючи цикл подій Node.js можна створити потужний веб-інтерфейс, де стану сервісів будуть асинхронно перевірятися в реальному часі, а дані будуть відправлятися клієнту через веб-сокети.

Така технологія дозволяє повідомляти про статуси як внутрішніх (внутрішньокорпоративних), так і загальнодоступних сервісів у реальному часі. Давайте трохи розвинемо цю ідею і спробуємо уявити мережевий операційний центр (NOC), відслідковує роботу додатків оператора, провайдера хмарних сервісів/хостинг-провайдера або який-небудь фінансової організації. Все це працює у відкритому веб-стеку на основі Node.js і веб-сокетів, а не Java і/або Java-аплетів.

Примітка: Не намагайтеся створювати на Node жорсткі системи реального часу (т. e., вимагають чітко визначеного часу відгуку). Мабуть, подібні програмикраще розробляти на Erlang.

Де можна використовувати Node.js

СЕРВЕРНІ ВЕБ-ДОДАТКИ

Node.js c Express.js також можна застосовувати для створення класичних веб-додатків на серверній стороні. Проте, нехай це і можливо, така парадигма запит/відповідь, де Node.js буде переносити відображений HTML, нетипова для даної технології. Існують аргументи як на користь такого підходу, так і проти нього. Необхідно враховувати наступне:

За:

  • Якщо ваш додаток не виконує інтенсивних обчислень, навантажувальних процесор, то його можна написати повністю на JavaScript, включаючи навіть базу даних, якщо ви використовуєте об'єктну базу даних (наприклад, MongoDB) і JSON. Це значно спрощує не тільки розробку, але і підбір фахівців.
  • Пошукові роботи отримують у відповідь повністю відображений HTML, що набагато зручніше для пошукової оптимізації, ніж, наприклад, робота з одностраничными додатками або веб-сокетным додатком, що працює на базі Node.js.
  • Будь-які інтенсивні обчислення, навантажують процесор, будуть блокувати динамічність Node.js, тому в такому випадку краще використовувати багатопотокову платформу. Також можна спробувати горизонтальне масштабування обчислень [*].
  • Node.js з реляційною базою даних як і раніше досить незручно (детальніше див. нижче). Зробіть собі послугу і вибрати яку-небудь інше середовище, наприклад, Rails, Django або ASP.Net MVC, якщо збираєтеся займатися реляційними операціями.


[*] В якості альтернативи таким CPU-інтенсивних обчислень можна створити добре масштабовану MQ-середу з обробкою на інтерфейсі бази даних, щоб Node залишався «на передовій» і асинхронно обробляв клієнтські запити.

В яких випадках не слід використовувати Node.js

СЕРВЕРНЕ ВЕБ-ДОДАТОК, ЗА ЯКИМ РОЗТАШОВАНА РЕЛЯЦІЙНА БАЗА ДАНИХ

Порівнюючи Node.js плюс Express.js з Ruby on Rails, ми впевнено вибираємо другий варіант, якщо мова йде про доступ до цих даних.

Інструменти реляційних баз даних для Node.js як і раніше, перебувають у зародковому стані, працювати з ними досить неприємно. З іншого боку, Rails автомагіческі налаштовує доступ до даних прямо при установці, плюс надає інструменти для підтримки міграцій схем баз даних та інші Gems. Rails і відповідні фреймворки мають зрілими і перевіреними реалізаціями доступу до рівня даних (Active Record або Data Mapper), яких вам буде гостро бракувати, якщо ви спробуєте відтворити таку конструкцію на чистому JavaScript.[*]

Все-таки, якщо ви твердо мають намір робити на JavaScript, зверніть увагу на Sequelize і Node ORM2— обидва інструменту поки не позбавлені помилок, але з часом можуть і дозріти.

[*] Можна використовувати Node виключно в клієнтській частині (нерідко так і робиться), а машинний інтерфейс виконати на Rails, зберігши, таким чином, легкий доступ до реляційної бази даних.

СКЛАДНІ СЕРВЕРНІ ОБЧИСЛЕННЯ І ОБРОБКА

Коли мова заходить про складних обчисленнях, Node.js залишає бажати кращого. Зрозуміло, ви не збираєтеся програмувати на Node сервер для обчислень Фібоначчі. В принципі, будь-які обчислювальні операції, сильно навантажують процесор, девальвують виграш у пропускної здатності, який Node досягається завдяки подійно-орієнтованого неблокирующему введення/висновку. Справа в тому, що будь-які вхідні запити будуть блокуватися, поки єдиний потік зайнятий переварюванням чисел.

Як було зазначено вище, Node.js однопотоковий і використовує лише одне ядро процесора. Може знадобитися реалізувати конкурентність на багатоядерний сервері, для цього команда розробників ядра Node вже займається підготовкою спеціального кластерного модуля. Крім того, ви без праці могли б запустити кілька примірників сервера Node.js за зворотним проксі з використанням nginx.

При кластеризації зберігається можливість вивантажити всі складні обчислення в фонові процеси, що працюють в більш підходящої середовищі, і забезпечити комунікацію між ними через сервер черги повідомлень, наприклад, RabbitMQ.

Хоча спочатку ваша фонова обробка могла протікати все на тому ж сервері, такий підхід забезпечує дуже високу масштабованість. Подібні служби фонової обробки легко можна розподілити на окремі робочі сервери без необхідності конфігурувати навантаження «фронтальних» веб-серверів.

Зрозуміло, такий підхід доречний і на інших платформах, але у випадку з Node.js купується та сама величезна пропускна здатність, про яку ми говорили вище, оскільки кожний запит — це маленька завдання, оброблювана дуже швидко і ефективно.

Висновок

Ми обговорили Node.js з теоретичної і практичної точки зору, почавши з його цілей та призначення і закінчивши розмовою про усіляких ласощів і підводних каменях. Якщо у вас виникнуть проблеми з Node.js, то пам'ятайте, що майже завжди вони зводяться до наступного факту: блокуючі операції — причина всіх бід. У 99% відсотках випадків всі проблеми починаються з-за неправильного використання Node.

Пам'ятайте: Node.js ніколи не призначався для вирішення проблеми масштабування обчислень. Він створювався для масштабування введення/виводу, з чим дійсно справляється дуже добре.

Навіщо використовувати Node.js? Якщо стоїть перед вами завдання не передбачає інтенсивних обчислень і звернення до блокуючим ресурсів, то можна повною мірою скористатися перевагами Node.js і насолоджуватися швидкими і легко масштабованими веб-додатками.
Книга про Node і Express

/>
/>


<input type=«radio» id=«vv68537»
class=«radio js-field-data»
name=«variant[]»
value=«68537» />
Видавайте, дуже потрібна така книга
<input type=«radio» id=«vv68539»
class=«radio js-field-data»
name=«variant[]»
value=«68539» />
Дякую, не треба
<input type=«radio» id=«vv68541»
class=«radio js-field-data»
name=«variant[]»
value=«68541» />
Є більш якісна література з Node.js (коментарі)

Проголосував 171 людина. Утрималося 58 осіб.


Тільки зареєстровані користувачі можуть брати участь в опитуванні. Увійдіть, будь ласка.


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.