javascript - 使用 react、react-router 和 express 进行服务器端渲染

标签 javascript node.js express reactjs react-router

我正在尝试为我的 react 应用程序设置服务器端渲染,并且我正在尝试使用出色的 react-router模块以允许它处理非 js 情况(一些爬虫,当用户出于某种原因关闭 js 时)。但是,我遇到了麻烦。我一直在这里使用好评 https://stackoverflow.com/a/28558545/3314701作为各种指南,但我收到了一些奇怪的错误。我得到一个持久的Syntax Error尝试使用 react.renderToString() 时.我是否错误地设置了服务器端渲染、遗漏了一些明显的东西或其他任何东西?

我的设置:

非常基本的 Express 服务器

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});
  });
});

顶级 react <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)

最佳答案

所以,我最终自己解决了这个问题。我得到的错误来自未渲染的嵌套组件,这就是 js 引擎提示随机 < 的原因字符。

现在是我的快速设置。对于那些不知道如何将 react 与服务器端渲染一起使用的人来说,这相当简单:Node 或 io.js 可用于调用 React 的 renderToString()组件上的方法,然后将其发送给请求的客户端。您可能已经听说过这种方法带来的好处,但对于那些不知道的人:

  1. 即使 google 已经可以在它的爬虫中执行 JS,你也会获得更多的 SEO 友好性;这几乎只是一个更安全的选择
  2. 非 js 情况的后备方案。如果您的应用脚本加载缓慢,您仍然可以将实际页面呈现给您的客户端,而不是让他们在盯着空白屏幕时等待。这也允许在浏览器上禁用 JS 的人在大多数情况下仍与您的应用程序交互;链接仍然有效,表单仍然可以提交,等等。
  3. 您可以获得客户端和服务器之间代码共享的额外好处。除了降低复杂性这一事实之外,没有什么令人难以置信的,因此,您可以获得降低复杂性的所有好处(可能减少耦合、更易于维护、结构更简单、同构等)
  4. 另一个好处是能够使用 react-router 的 html5 历史 API,而不是您必须使用的烦人的哈希片段。

您甚至可以对这种方法感到疯狂,并在加载时为您的应用处理占位符之类的事情,或者为加载缓慢的状态(例如加载时的 Facebook)提供其他反馈机制。

基本方法大致按以下方式运行:

  1. 在引导时, Node 应用会根据 routes.jsx 实例化一个 react-router 实例
  2. 请求到达服务器,然后使用 express' req.path提供一个路由字符串供 react-router 处理。
  3. React 路由器然后匹配提供的路由并尝试渲染相应的组件以便 express 发回。
  4. React 发送 html 响应,而您的客户端可以绘制一些东西,而不管您的应用脚本的速度如何。我们通过一个出色的 CDN 为我们的 CDN 提供服务,但即使有最好的分发和压缩,慢速网络仍然会给人们留下暂时的空白屏幕。
  5. 加载所需的应用脚本后,React 可以使用相同的 routes.jsx文件接管并生成带有 react-router 的 html从这里开始了。这里的另一个好处是您的应用程序代码可以被缓存, future 的交互甚至不必依赖另一个调用。

还有一点值得注意:我使用 webpack 来打包我的 react 代码,现在 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>

有用的链接:

关于javascript - 使用 react、react-router 和 express 进行服务器端渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30626410/

相关文章:

javascript - 文件中的 Laravel javascript 不起作用,但如果通过 CDN 包含则可以

javascript - 正则表达式: replace string in javascript

javascript - 类型错误 : "method" is not a function

node.js - WebdriverIO 屏幕截图在等待执行条件后失败

javascript - 使用 MySQL 查询或 javascript 优化数据

javascript - 在 JavaScript 中格式化

node.js - 在 Express 中存储环境/配置变量的首选方法是什么?

javascript - 如何让 Typescript 检查器相信某个值是 Javascript 中的特定子类?

javascript - 如何使用 JsDoc 记录解构的参数

python - plotly 地表达|更改 y Axis 上的刻度单位