[Из песочницы] Как мы готовим React, Require и Backbone
Как следует из официальной документации, React.js — V из MVC, и, как правило, вместе с ним применяются другие решения, в данном случае — Backbone.js и Require.js. А еще Jasmine, Karma и Grunt. Сегодня я поделюсь наброском проекта с применением этих инструментов.Ссылка для нетерпеливых.
ХотелкиПрозрачная структура проекта; Автоматизация всей рутинной работы; Автоматизация тестирования; Модульность; Повторное использования кода; Производительность. Чего добились Примерно так выглядит «дерево» проекта: . ├── app │ ├── app.js # Главный файл приложения │ ├── bower_components # Зависимости, описанные в bower.json │ │ └── … │ ├── index.html │ ├── scripts │ │ ├── controllers # Backbone контроллеры │ │ │ └── src │ │ │ ├── hello.jsx │ │ │ ├── main.jsx │ │ │ └── notfound.jsx │ │ ├── router.js # Конфигурация роутинга │ │ └── ui-components # React компоненты │ │ └── src │ │ └── panel │ │ ├── panel.jsx │ │ └── panel.less │ └── styles # Стили │ └── src │ └── main.less ├── bower.json # Описание зависимостей ├── Gruntfile.js ├── install-deps.bat # Скрипты, ├── install-deps.sh # устанавливающие ├── install-env.bat # зависимости ├── install-env.sh # и окружение ├── package.json # зависимости рабочего окружения node.js, на продакшене не нужны (Конечно, если серверная часть не на node.js) ├── server └── test # Тесты ├── test.config.js └── ui-components └── src └── panel.test.jsx А так код Подробно рассматривать весь исходный код не вижу смысла, для этого есть гитхаб, остановлюсь на ключевых моментах: /* * app/app.js * Только этот файл подключается в index.html, все остальное делает require * Описывает пути к файлам проекта и запускает роутинг (Backbone). */ 'use strict'; requirejs.config ({ baseUrl: './', paths: { app: './scripts', controllers: './scripts/controllers/dest', // dest — папки с результатами «компиляции» .jsx и .less ui: './scripts/ui-components/dest', // В системе контроля версий не хранятся underscore: './bower_components/underscore/underscore', backbone: './bower_components/backbone/backbone', jquery: './bower_components/jquery/dist/jquery.min', react: './bower_components/react/react' } }); Собственно, сам роутинг. Комментарии, думаю, излишни.
/* * app/scripts/router.js */ 'use strict'; define (function (require) { var Backbone = require ('backbone');
var AppRouter = Backbone.Router.extend ({ routes: { '': 'MainCtrl', 'hello/: name (/)': 'HelloCtrl', '*actions': 'NotFoundCtrl' },
MainCtrl: require ('controllers/main'), HelloCtrl: require ('controllers/hello'), NotFoundCtrl: require ('controllers/notfound') });
return new AppRouter (); }); В ui-components описываются обычные React-компоненты в синтаксисе .jsx и таблицы стилей для каждого отдельного компонента. Есть нечто общее с БЭМ. Каждый компонент лежит в отдельной папке и зависит только от самого React’а.
Не только компоненты интерфейса, но и контроллеры пишутся в синтаксисе .jsx, чтобы можно было сделать вот так:
/*
* app/scripts/controllers/src/hello.jsx
*/
'use strict';
define (['react', 'ui/panel/panel'], function (React, Panel){
/* Аргумент из строки запроса */
return function (name){
/*
* Реализуем логику приложения, например, отправляя запрос к серверу.
* А потом рендерим компонент (ы).
*/
React.render (
Hello, {name}!
/* * app/scripts/ui-components/src/panel.jsx */ define (['react'], function (React){ 'use strict'; var Panel = React.createClass ({ render: function (){ return (
{this.props.title}
return Panel; }); А это — тесты для panel, написанные с применением Jasmine, можно использовать любой фреймворк который вам нравится, например, разработчики React используют Jest. Тесты запускаются при помощи Karma, к сожалению пока и не смог завести PhantomJS для этих тестов, так что приходится мириться с постоянно всплывающим хромом.
/*
* test/ui-components/src/panel.test.jsx
*/
'use strict';
define (['react', 'ui/panel/panel'], function (React, Panel) {
describe ('Panel behaviour tests', function () {
var TestUtils = React.addons.TestUtils;
var panel;
var p;
/*
* Аналог this.setUp () из xxxUnit
*/
beforeEach (function (){
panel = TestUtils.renderIntoDocument ((
Paragraph content
/* Проверяем что компонент вообще рендерится */ it ('Should render itself into DOM', function (){ expect (TestUtils.isCompositeComponent (panel)).toBe (true); });
/* И что заголовок, переданный атрибутом отображается */ it ('Should render title from props', function (){ var h1 = TestUtils.findRenderedDOMComponentWithTag (panel, 'h1'); expect (h1.getDOMNode ().innerHTML).toBe ('Test'); }); /* А также потомки никуда не исчезли */ it ('Should render children from props', function (){ var paragraph = TestUtils.findRenderedDOMComponentWithTag (panel, 'p'); /* * Specific react feature, it does not render text node directly, * but renders Paragraph content */ expect (paragraph.getDOMNode ().innerHTML).toContain ('Paragraph content'); });
}); }); Кстати, index.html выглядит довольно коротко и аккуратно:
Автоматизация С ней прекрасно справляется grunt который «компилирует» less и jsx, прогоняет тесты, обновляет страничку в браузере при сохранении файлов и делает еще много прикольных вещей.Повторное использование и модульность В принципе, любой компонент UI можно просто взять и скопировать в другой проект, вместе со стилями и тестами (разумеется, там тоже нужен React). И он (компонент) заработает сразу, без лишних телодвижений. Особенно это актуально это для админок и типовых компонентов, там даже стили менять не надо.И зачем все это нужно? Во-первых, хотелось собрать все нужные инструменты в одном месте, чтобы они еще и работали. Во-вторых, я очень люблю React, использовать его с Backbone, наверное, стоит, оба легкие, шустрые и расширяемые, а Require может сделать структуру приложения прозрачнее. В-третьих получился (хочется верить) небольшой «шаблон» типового проекта, начиная разработку можно просто стянуть репозиторий и «всё сразу заработает ©».И что дальше? Всё и сразу пока не работает. В ближайших планах реализовать сборку проекта на продакшен, с минификацией всего, что можно минифицировать. В чуть более далеких — написание yoman генератора для скаффолдинга котроллеров и компонентов.