Здравствуйте, хабражители!
Не так давно на просторах интернета появилась javascript библиотека для построения пользовательских интерфейсов от facebook — Reactjs. Данная библиотека идеально подходит для создания простых и сложных javascript приложений. Позволяет организовывать ваш клиент-сайд в виде независимых компонентов. Берет на себя всю заботу по модификации DOM структуры дерева. И делает это весьма эффективно и разумно.
В общем, в результате небольшого знакомства с reactjs появилось такое приложение — demo. Цель данного поста поделиться впечатлениями от работы с reactjs + gruntjs + browserify.
Ниже будет изложено:
Тех кого заинтересовал прошу под кат…
Не буду кривить душой, идея online генератора гамм не нова, и свое вдохновение я почерпнул из этого ресурсика, добавив несколько плюшек от себя. Гаммы, в первую очередь, полезны для людей, которые пытаются освоить импровизацию. И так же полезны как упражнение для усовершенствования техники игры. Каждая гамма, по сути, состоит из определенных нот, которые вычисляются по определенной формуле. Мне, как любителю гитары, доставляет удовольствие угадывать тональность и пытаться подбирать соло под любимую музыку. И данное приложение очень хорошо в этом помогает.
Для хорошей кармы, да и просто люблю с этим повозиться — весь javascript был оформлен в виде commonjs модулей, склеивается в 1 файл и минифицируется а reactjs-компоненты пререндерятся во время сборки и вставляются в html странички.
Данный эффект достигался при помощи таких инструментов:
Формат commonjs очень удобен и позволяет использовать один и тот же javascript код как на сервере так и на клиенте.
Одной из наиболее привлекательных возможностей в reactjs для меня является возможность серверного пререндеринга компонентов. Ведь прекрасно же иметь возможность инициализировать начальное состояние вашего приложения и отрисовывать копоненты до того как они попадают в браузер. Прелесть reacjs в том, что он сам понимает когда компонент уже отрисован, и не пытается сделать это снова. Если просмотреть код странички из demo, то видно что главный компонент страници уже был отрисован до отдачи контента в браузер. Главным требованием для серверного рендеринга является возможность сделать require компонента на сервере. Поскольку процедура пререндеринга является необходимой только при деплое — решил сделать под нее отдельный grunt plugin, который будет обрабатывать html файлы перед загрузкой на gh-pages — grunt-react-render.
Плагин достаточно прост и действует по такому алгоритму:
Вот участок html кода главной страници, которая обрабатывается этим плагином:
Так выглядит grunt конфигурация плагина:
Reactjs подталкивает к тому, чтобы формировать логику приложения в виде отдельных конфигурируемых компонентов, которые по возможности, можно повторно использовать. Философия reactjs говорит о том, что компоненты должны хранить по минимуму состояния, так как хранимое состояние в разных местах — это источник магии и side-эффектов. Поэтому, лучше сохранять и изменять state в компонентах верхнего уровня и передавать внутренним компонентам через свойства (props).
В процессе создания приложения столкнулся с тем, что в интернете мало готовых решений для простых компонентов. Сначала меня это очень смущало и я тратил некоторое время на поиски готовых решений, например для тех же выпадающих списков. Пытался интегрировать их в свой проект и адаптировать верстку, но потом попробовал реализовать функциональность вот такого выпадающего списка с нуля:
И был удивлен насколько это просто с reactjs. По сути, компонент становится очень прост, когда весь рендеринг берет на себя reactjs, а тебе остается только задать то, как нужно отрисовать компонент при разном состоянии и правильно его переключать.
Coffeescript оказался очень удобен для описания html структуры компонентов. Особенно полезно его реструктурирующие присваивание.
Как по мне, вполне достаточно чтобы не использовать jsx. Так же пришлось повозиться с bootstrap кнопками и draggable поведением компонентов.
Так как приложение создавалось не только в целях освоить реакт, а и упростить себе жизнь и другим в изучении гамм на гитаре — всю логику старался делать как можно проще для того, чтобы при возможности, кто-то мог без особых усилий добавить другую гамму или строй гитары. Так вот, например, выглядит описание гамм в коде:
Каждая гамма имеет свою формулу(size), функцию для генерации нот и описание. Показав этот проект одному другу, он имея более глубокие познания в музыке, без особого труда добавил несколько других гамм.
Так же просто выглядят данные для гитарных строев:
Поочередное воспроизведение звуков реализовано с помощью библиотеки howler.js.
Если у кого-то из тех кто дочитал до этого места появится желание добавить и усовершенствовать что-то в проекте — буду этому безумно рад.
Многие совсем не заморачиваются над сборкой проекта и организацией кода, может это и правильно. Лично я получаю от процесса автоматизации удовольствие, поэтому немного заморочился над этим и организовал сборку и деплой проекта на gruntjs. Есть 3 основных команды:
Подробнее о сборке проекта может рассказать Gruntfile в репозитории.
В целом, работа с reactjs оставила позитивное впечатление, в будущем хочу попробовать его вместе с какой-то FRP библиотекой(RxJs или bacon.js). Так же, планирую добавлять другой полезный функционал в этот проект.
Благодарю за внимание! Любая критика, пожелания и отзывы — приветствуются.
Исходный код — github
Не так давно на просторах интернета появилась javascript библиотека для построения пользовательских интерфейсов от facebook — Reactjs. Данная библиотека идеально подходит для создания простых и сложных javascript приложений. Позволяет организовывать ваш клиент-сайд в виде независимых компонентов. Берет на себя всю заботу по модификации DOM структуры дерева. И делает это весьма эффективно и разумно.
В общем, в результате небольшого знакомства с reactjs появилось такое приложение — demo. Цель данного поста поделиться впечатлениями от работы с reactjs + gruntjs + browserify.
Ниже будет изложено:
- Основные моменты создания приложения и личные впечатления (симбиоз reactjs + browserify + gruntjs + coffeescript).
- Серверный пререндериг reactjs компонентов для статических страниц.
- Подход к сборке reactjs приложения с помощью gruntjs и деплой на gh-pages одной командой.
Тех кого заинтересовал прошу под кат…
Идея приложения
Не буду кривить душой, идея online генератора гамм не нова, и свое вдохновение я почерпнул из этого ресурсика, добавив несколько плюшек от себя. Гаммы, в первую очередь, полезны для людей, которые пытаются освоить импровизацию. И так же полезны как упражнение для усовершенствования техники игры. Каждая гамма, по сути, состоит из определенных нот, которые вычисляются по определенной формуле. Мне, как любителю гитары, доставляет удовольствие угадывать тональность и пытаться подбирать соло под любимую музыку. И данное приложение очень хорошо в этом помогает.
Что было реализовано
- Выбор тональности.
- Выбор гаммы (минор, мажор, блюз, пентатоника).
- Выбор гитарного строя.
- Поочередное воспроизведение нот для выбранного участка грифа гитары.
- Смена направления воспроизведения нот (сверху вниз/снизу вверх).
- Возоможность циклического воспроизведения (для заучивания).
- Автоматическая смена направления воспроизведения нот.
Организация приложения
Для хорошей кармы, да и просто люблю с этим повозиться — весь javascript был оформлен в виде commonjs модулей, склеивается в 1 файл и минифицируется а reactjs-компоненты пререндерятся во время сборки и вставляются в html странички.
Данный эффект достигался при помощи таких инструментов:
- gruntjs — сборка и деплой проэкта
- browserify — для работы commonjs модулей в браузере
- grunt-react-prerender — пререндеринг компонентов на сервере
Формат commonjs очень удобен и позволяет использовать один и тот же javascript код как на сервере так и на клиенте.
Серверный пререндеринг
Одной из наиболее привлекательных возможностей в reactjs для меня является возможность серверного пререндеринга компонентов. Ведь прекрасно же иметь возможность инициализировать начальное состояние вашего приложения и отрисовывать копоненты до того как они попадают в браузер. Прелесть reacjs в том, что он сам понимает когда компонент уже отрисован, и не пытается сделать это снова. Если просмотреть код странички из demo, то видно что главный компонент страници уже был отрисован до отдачи контента в браузер. Главным требованием для серверного рендеринга является возможность сделать require компонента на сервере. Поскольку процедура пререндеринга является необходимой только при деплое — решил сделать под нее отдельный grunt plugin, который будет обрабатывать html файлы перед загрузкой на gh-pages — grunt-react-render.
Плагин достаточно прост и действует по такому алгоритму:
- Читаем файл для обработки.
- Парсим html структуру, находим теги с атрибутом data-rcomp.
- Считываем относительный путь к компоненту из атрибута data-rcomp.
- Делаем require компонента и вызываем метод react.renderComponentToString().
- Вставляем внутрь тега с атрибутом data-rcomp.
- Сохраняем файл.
Вот участок html кода главной страници, которая обрабатывается этим плагином:
<div class="container">
<h1>Scales generator</h1>
<div data-rcomp="./lib-js/pages/scales_page_component" id="container">
</div>
</div>
Так выглядит grunt конфигурация плагина:
react_render:
index:
options:
src: '.dist/index.html'
Впечатления о Reactjs
Reactjs подталкивает к тому, чтобы формировать логику приложения в виде отдельных конфигурируемых компонентов, которые по возможности, можно повторно использовать. Философия reactjs говорит о том, что компоненты должны хранить по минимуму состояния, так как хранимое состояние в разных местах — это источник магии и side-эффектов. Поэтому, лучше сохранять и изменять state в компонентах верхнего уровня и передавать внутренним компонентам через свойства (props).
В процессе создания приложения столкнулся с тем, что в интернете мало готовых решений для простых компонентов. Сначала меня это очень смущало и я тратил некоторое время на поиски готовых решений, например для тех же выпадающих списков. Пытался интегрировать их в свой проект и адаптировать верстку, но потом попробовал реализовать функциональность вот такого выпадающего списка с нуля:
И был удивлен насколько это просто с reactjs. По сути, компонент становится очень прост, когда весь рендеринг берет на себя reactjs, а тебе остается только задать то, как нужно отрисовать компонент при разном состоянии и правильно его переключать.
код выпадающего списка
React = require 'react'
{ul, li, span, div, a} = React.DOM
SimpleDropdown = React.createClass
displayName: "SimpleDropdown"
getDefaultProps: -> onChange: ->
getInitialState: ->
isOpen: false
value: @props.value or ""
toggle: -> @setState {isOpen: !@state.isOpen}
itemClick: (ev) ->
ev.preventDefault()
value = (ev.target.getAttribute "value")
@setState {value, isOpen: false}
@props.onChange value
render: ->
self = @
options = @props.options.map ([value, text]) ->
(li {key: "opt_#{value}"},
(a {value, href: "#", onClick: self.itemClick}, text))
openCls = if @state.isOpen then "open" else ""
currentOption = (@props.options.filter ([value, text]) =>
value.toString() is @state.value.toString())?[0]
(div
className: "btn-group #{openCls}"
(button
className: "btn btn-default"
onClick: @toggle
(span {className: "glyphicon"}, "")
currentOption?[1]
(span {className: "caret"}, ""))
(ul
className: "dropdown-menu"
ref: "select"
options))
Coffeescript оказался очень удобен для описания html структуры компонентов. Особенно полезно его реструктурирующие присваивание.
{div, ul, span, li} = React.DOM
(div {className: "div"}
(span {className: "span", "span Text")
(ul
(li "option 1")
(li "option 2")
)
Как по мне, вполне достаточно чтобы не использовать jsx. Так же пришлось повозиться с bootstrap кнопками и draggable поведением компонентов.
Бизнес-логика
Так как приложение создавалось не только в целях освоить реакт, а и упростить себе жизнь и другим в изучении гамм на гитаре — всю логику старался делать как можно проще для того, чтобы при возможности, кто-то мог без особых усилий добавить другую гамму или строй гитары. Так вот, например, выглядит описание гамм в коде:
SCALES =
Minor:
desc: "Minor scale"
size: [STEP, hSTEP, STEP, STEP, hSTEP, STEP, STEP]
get_notes: (Tonica) -> generate_scale Tonica, SCALES.Minor
Major:
desc: "Major scale"
size: [STEP, STEP, hSTEP, STEP, STEP, STEP, hSTEP]
get_notes: (Tonica) -> generate_scale Tonica, SCALES.Major
...
Каждая гамма имеет свою формулу(size), функцию для генерации нот и описание. Показав этот проект одному другу, он имея более глубокие познания в музыке, без особого труда добавил несколько других гамм.
Так же просто выглядят данные для гитарных строев:
TUNINGS =
"Standart":
name: "Standart E"
notes: [E, B, G, D, A, E]
offset: [0, 0, 0, 0, 0, 0]
"DropD":
name: "Dropped D"
notes: [E, B, G, D, A, D]
offset: [0, 0, 0, 0, 0, -2]
...
Поочередное воспроизведение звуков реализовано с помощью библиотеки howler.js.
Если у кого-то из тех кто дочитал до этого места появится желание добавить и усовершенствовать что-то в проекте — буду этому безумно рад.
Сборка и деплой 1й командой
Многие совсем не заморачиваются над сборкой проекта и организацией кода, может это и правильно. Лично я получаю от процесса автоматизации удовольствие, поэтому немного заморочился над этим и организовал сборку и деплой проекта на gruntjs. Есть 3 основных команды:
- grunt build (компиляция coffeescript, обертка в commonjs модули для браузера, склейка js и css в 1 файл)
- grunt deploy (build + минификация js и css, копирование в дирректорию для деплоя, пререндеринг реакт компонентов)
- grunt deploy-gh (deploy + деплой на github pages)
Подробнее о сборке проекта может рассказать Gruntfile в репозитории.
Выводы
В целом, работа с reactjs оставила позитивное впечатление, в будущем хочу попробовать его вместе с какой-то FRP библиотекой(RxJs или bacon.js). Так же, планирую добавлять другой полезный функционал в этот проект.
Благодарю за внимание! Любая критика, пожелания и отзывы — приветствуются.
Исходный код — github