Я встречаю много статей, где описываются плюсы применения 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/
Добавить комментарий