Рендеринг на стороне сервера с помощью реакции, реакции-маршрутизатора и экспресса

Я пытаюсь настроить рендеринг на стороне сервера для своего приложения реагирования, и я пытаюсь использовать отличный модуль реагирования-маршрутизатора, чтобы он мог обрабатывать ситуации, отличные от js (некоторые сканеры, когда у пользователя отключался js для некоторых причина). Однако у меня проблемы. Я использовал отличный ответ здесь /questions/12322703/marshrutizatsiya-klienta-s-ispolzovaniem-reaktsii-marshrutizatora-i-marshrutizatsiya-na-storone-servera/12322714#12322714 в качестве руководства, но я получаю странные ошибки. Я настойчиво Syntax Error при попытке использовать react.renderToString(), Я неправильно настраиваю рендеринг на стороне сервера, упускаю что-то очевидное или что-то еще?

Моя настройка:

Действительно базовый экспресс-сервер

require('babel/register');

var app = express();


// misc. express config...

var Router = require('react-router'),
    routes = require('../jsx/app').routes,
    React = require('react');


app.use(function(req, res, next) {
  var router = Router.create({location: req.url, routes: routes});
  router.run(function(Handler, state) {
    console.log(Handler);
    var html = React.renderToString(<Handler/>);
    return res.render('react_page', {html: html});
  });
});

Реакция на высшем уровне <App/> составная часть

// Shims
require('intl');
require('es5-shim');

var React = require('react/addons'),
  Router = require('react-router'),
  Nav = require('./nav'),
  injectTapEventPlugin = require("react-tap-event-plugin"),


  window.React = React; // export for http://fb.me/react-devtools

// Intl
var ReactIntl = require('react-intl'),
  IntlMixin = ReactIntl.IntlMixin;

var Route = Router.Route,
  DefaultRoute = Router.DefaultRoute,
  NotFoundRoute = Router.NotFoundRoute,
  RouteHandler = Router.RouteHandler;


var App = React.createClass({
      mixins: [IntlMixin],

      getInitialState: function() {
        return {
          connected: false,
          loaded: false,
          user: true
        };
      },
      render: function() {
          return ( 
            <div className="container-fluid">
              <Nav/>
              <RouteHandler/>
              <Footer/>
            </div>
      );
  }

});

var routes = (
<Route name="Home" path="/" handler={App}>
    <DefaultRoute name="Welcome " handler={Welcome}/>
    <Route name="Bar" path="/bar" handler={Bar}>
    <Route name="foo" path="/foo" handler={Foo}></Route>
 </Route>
);

Router.run(routes, Router.HistoryLocation , function(Handler) {
  React.render(<Handler/>, document.getElementById('app'));
});

module.routes = routes;

выход:

flo-0,1,2 (err):       <div className="progressbar-container" >
flo-0,1,2 (err):       ^
flo-0,1,2 (err): SyntaxError: Unexpected token <
flo-0,1,2 (err):     at exports.runInThisContext (vm.js:73:16)
flo-0,1,2 (err):     at Module._compile (module.js:443:25)
flo-0,1,2 (err):     at Module._extensions..js (module.js:478:10)
flo-0,1,2 (err):     at Object.require.extensions.(anonymous function) [as .js] (/Users/user/Code/foobar/apps/flo/node_modules/babel/node_modules/babel-core/lib/babel/api/register/node.js:161:7)
flo-0,1,2 (err):     at Module.load (module.js:355:32)
flo-0,1,2 (err):     at Function.Module._load (module.js:310:12)
flo-0,1,2 (err):     at Function.<anonymous> (/Users/user/.nvm/versions/node/v0.12.4/lib/node_modules/pm2/node_modules/pmx/lib/transaction.js:62:21)
flo-0,1,2 (err):     at Function.cls_wrapMethod (/Users/user/Code/foobar/apps/bar/node_modules/newrelic/lib/shimmer.js:230:38)
flo-0,1,2 (err):     at Function.<anonymous> (/Users/user/Code/foobar/apps/bar/node_modules/pmx/lib/transaction.js:62:21)
flo-0,1,2 (err):     at Module.require (module.js:365:17)
flo-0,1,2 (err):     at require (module.js:384:17)

1 ответ

Решение

Итак, я сам решил эту проблему. Ошибка, которую я получал, была от не визуализированного вложенного компонента, поэтому движок js жаловался на случайный < голец.

А теперь к моей экспресс-настройке. Для тех, кто не знает, как реакция может использоваться с рендерингом на стороне сервера, это довольно просто: Node или io.js можно использовать для вызова React renderToString() метод на компоненте, а затем отправив его запрашивающему клиенту. Вы, наверное, уже слышали о преимуществах такого подхода, но для тех, кто не знает:

  1. Вы получаете больше SEO-дружественности, даже если Google уже может запускать JS в своих сканерах; это гораздо более безопасная ставка
  2. Откат для не-JS ситуаций. Если скрипт вашего приложения загружается медленно, вы все равно можете визуализировать реальную страницу своему клиенту и не заставлять их ждать, глядя на пустой экран. Это также позволяет кому-то с отключенным JS в их браузере по-прежнему взаимодействовать с вашим приложением по большей части; ссылки по-прежнему будут работать, формы по-прежнему могут отправляться и т. д.
  3. Вы можете получить дополнительные преимущества совместного использования кода между клиентом и сервером. В этом нет ничего невероятного, за исключением того, что сложность уменьшается, и, как таковая, вы получаете все преимущества уменьшенной сложности (потенциально меньшее сцепление, простота обслуживания, большая простота в структуре, изоморфность и т. Д.)
  4. Еще одним дополнительным преимуществом является возможность использовать html5 history API на базе router-маршрутизатора вместо раздражающего хеш-фрагмента, который вы должны использовать в противном случае.

Вы даже можете сойти с ума с этим подходом и обрабатывать такие вещи, как заполнители, для своего приложения, когда оно загружается, или предоставлять другие механизмы обратной связи для состояния медленной загрузки (например, Facebook, пока оно загружается).

Базовый подход работает примерно следующим образом:

  1. После начальной загрузки приложение узла создает экземпляр реакции-маршрутизатора на основе routes.jsx
  2. Запрос отправляется на сервер, который затем использует экспресс req.path предоставить строку маршрута для реакции-маршрутизатора для обработки.
  3. Затем маршрутизатор React сопоставляет предоставленный маршрут и пытается отобразить соответствующий компонент для экспресс-отправки.
  4. React отправляет html-ответ, и ваш клиент может что-то нарисовать независимо от скорости вашего скрипта приложения. Мы обслуживаем наших клиентов по большому CDN, но даже при лучшем распределении и сжатии медленные сети все равно оставляют людей с временно пустым экраном.
  5. Загрузив необходимый скрипт приложения, React может использовать тот же routes.jsx файл, чтобы взять на себя и генерировать HTML с react-router отныне и впредь. Еще одним преимуществом здесь является то, что код вашего приложения может быть кэширован, и будущие взаимодействия, надеюсь, даже не будут полагаться на другой вызов.

Еще один момент, который стоит отметить: я использую веб-пакет, чтобы связать свой реагирующий код, и теперь browser.jsx является точкой входа. До рефакторинга для рендеринга на стороне сервера это было ранее app.jsx; вам может понадобиться переконфигурировать вашу структуру, чтобы приспособиться к тому, что визуализируется где.:)

Ле Код:

Browser.jsx

const React = require('react');
const Router = require('react-router').Router;
const hist = require('history');
const routes = require('./routes');

const newHistory = hist.createHistory();

React.render(<Router history={newHistory}>{routes}</Router>, window.document);

App.js (экспресс-сервер):

//...other express configuration

const routes = require('../jsx/routes');
const React = require('react');
const {RoutingContext, match} = require('react-router');
const hist = require('history');

app.use((req, res, next) => {
  const location = hist.createLocation(req.path);
  match({
    routes: routes,
    location: location,
  }, (err, redirectLocation, renderProps) => {
    if (redirectLocation) {
      res.redirect(301, redirectLocation.pathname + redirectLocation.search);
    } else if (err) {
      console.log(err);
      next(err);
      // res.send(500, error.message);
    } else if (renderProps === null) {
      res.status(404)
        .send('Not found');
    } else {
      res.send('<!DOCTYPE html>' + React.renderToString(<RoutingContext {...renderProps}/>));
    }
  });
});

    //...other express configuration

Routes.jsx

<Route path="/" component={App}>
  <DefaultRoute component={Welcome}/>
  <Route path="dashboard" component={Dashboard}/>
  <Route path="login" component={Login}/>
</Route>

App.jsx

<html>
<head>
  <link rel="stylesheet" href="/assets/styles/app.css"/>
</head>
  <body>
    <Navigation/>
    <RouteHandler/>
    <Footer/>
  <body/>
</html>

Полезные ссылки:

Другие вопросы по тегам