Тонкости использования ReactJS в MeteorJS приложении

от автора

Я встречаю много статей, где описываются плюсы применения React вместе с Meteor. Ни разу не видел, чтобы кто-то шёл дальше «плюсов» и описал то, как, собственно, это сделать.

При имплементации возникает пара серьезных проблем.

Первая — перенос существующего фронтенда на React по частям, вторая — использование сторонних библиотек для React.

Решение проблем под катом.

Использование React компонентов в существующем Meteor приложении

Для того, чтобы установить поддержку React, достаточно поставить этот пакет: github.com/reactjs/react-meteor

Он добавляет обработчик jsx файлов и глобальный объект React на фронтенд и на бекенд. Так же он добавляет свою обертку для компонентов, но она в данный момент плоха, так как не работает с существующим в Meteor роутингом.

Выражается это в том, что при удалении родительского Blaze шаблона (при переключении пути) React компонент как ни в чем не бывало остается на странице.

А данный момент я использую свою обертку, позволяющую сделать из React компонента Blaze шаблон, который можно включать в существующие шаблоны, по частям переводя фронтенд с Blaze на React.

Пока я это писал, в комментариях к англоязычной статье подсказали github.com/grovelabs/meteor-react. Обновлю, как только протестирую.

Моя обертка выглядит так:

_ReactUtils.createClass = function(opts) {   var templateName = opts.templateName;   var templateClass = new Template(     templateName,     function() {       return new HTML.DIV;     }   );   Template[templateName] = templateClass;   var Component = React.createClass(opts);   templateClass.onRendered(function() {     var template = this;     var data = this.data || {};     var c = React.createElement(Component, data);     c._meteorTemplate = template;     template._reactComponent = React.render(c, template.firstNode);   });   templateClass.onDestroyed(function() {     var template = this;     React.unmountComponentAtNode(template._reactComponent.getDOMNode());   });   return Component; }; 

И позволяет создать Blaze шаблон из React компонента, при этом компонент будет знать о том, уничтожился ли шаблон и в этом случае удаляться со страницы.

Так же названные выше библиотеки предлагают свои варианты получения данных компонентом из рективных источников (Mongo курсоры, которые связывают бекенд и фронтенд Метеора, или Session/ReactiveVars, которые могут быть использованы для коммуникации внутри фронтенд приложения).

Так как в данный момент я использую первую библиотеку и ее вариант мне не нравится, я просто подписываюсь на реактивные источники сам, напрямую или через сервисы (в сервисах использую BaconJS, чтобы возвращать Streams а данными).

Другим, более «чистым» вариантом, было бы использование ванильного flux’a или reflux / fluxxor / любые другие его имплементации. Я не буду описывать это в деталях, так как пока толком не разобрался сам.

Сейчас делаю так:

componentWillMount: function() {     this._cancelSubscription = Tracker.autorun(...);  }  componentWillUnmount: function() {     this._cancelSubscription(); } 

Использование сторонних React библиотек в Meteor

Главная проблема здесь в том, что, чтобы использовать какую-либо библиотеку в Meteor, её надо либо засунуть в код твоего приложения (сразу отбрасываем), либо сделать специальный Meteor Package.

Meteor Packages необходимы, но неудобны (для создателей, не для пользователей) по двум причинам.

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

Вторая — связанная непосредственно с React, а, вернее, с повсеместным спользованием в React библиотеках webpack/browserify билдов — Meteor пакет не умеет работать с this. Экспортировать объект библиотеки надо глобально, в стиле:

MyLibrary = {...}; 

Это обязательное требование, и пока что разработчики не собираются его менять.

Webpack/Browserify же делают так:

(function(...){...})(this, ...); 

И это не работает. Каждый билд вручную необходимо вбивать специальный костыль в стиле

var meteorHack = {   React: React }; (function webpackUniversalModuleDefinition(root, factory) { 	... })(meteorHack, ...) // вместо this - meteorHack!  ...  MyLibrary = meteorHack.MyLibrary; 

Тогда все будет хорошо. Обращаю внимание, что React в билде игнорируется, так как мы зависим от пакета, который его уже инициализировал:

Package.onUse(function(api) {   api.use('reactjs:react@0.2.1', ['client', 'server']); // может быть, здесь я позже использую другую библиотеку, выкинув reactjs:react@0.2.1, так как он довольно несуразный   api.addFiles('dist/meteor-dist.js', ['client', 'server']);   api.export('MyLibrary', ['client', 'server']); }); 

Соответственно, при билде React так же игнорируется, пример для webpack:

module.exports = {     entry: "./index.js",     output: {         path: __dirname,         filename: "dist/meteor-dist.js",         libraryTarget: "umd",         library: "MyLibrary"     },     externals: {         react: 'React'     } }; 

Моей последней рекомендацией будет форкать библиотеку, которую хочется перенести в Meteor, а не создавать свой собственный репозиторий. Так чище.

ссылка на оригинал статьи http://habrahabr.ru/post/256035/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *