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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30626410/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 12:26:55  来源:igfitidea点击:

Server side rendering with react, react-router, and express

javascriptnode.jsexpressreactjsreact-router

提问by markthethomas

I'm trying to set up server-side rendering for my react app and I'm trying to use the great react-routermodule to allow it to handle non-js situations (some crawlers, when a user had js turned off for some reason). However, I'm running into trouble. I've been using the great response here https://stackoverflow.com/a/28558545/3314701as a guide of sorts, but I'm getting strange errors thrown at me. I get a persistent Syntax Errorwhen trying to use react.renderToString(). Am I setting up the server-side rendering incorrectly, missing something obvious, or anything else?

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

My setup:

我的设置:

Really basic Express server

真正基本的 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});
  });
});

Top-level react <App/>component

顶级反应<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;

output:

输出:

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)

回答by markthethomas

So, I ended up solving this one myself. The error I was getting was from an un-rendered nested component, which is why the js engine was complaining about a random <char.

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

And now to my express setup. For those who aren't aware of how react can be used with server-side rendering, it's fairly straightforward: Node or io.js can be used to call React's renderToString()method on a component and then sending that to the requesting client. You've probably heard the benefits this approach brings already, but for those who don't know:

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

  1. you get more SEO-friendliness, even though google can already execute JS in it's crawlers; this is pretty much just a safer bet
  2. Fallback for non-js situations. If your app script is loading slowly, you can still render the actual page to your client and not make them wait while staring at a blank screen. This also allows someone with JS disabled on their browser to still interact with your app for the most part; links will still work, forms can still submit, &c.
  3. You can get the additional benefits of code-sharing between the client and server. There's nothing necessarily incredible about this aside from the fact that complexity is decreased and, as such, you get all the benefits of decreased complexity (potentially less coupling, easier maintainability, greater simplicity in structure, isomorphic-ness, &c.)
  4. A further side benefit is the ability to use react-router's html5 history API instead of the annoying hash-fragment stuff you have to otherwise use.
  1. 即使谷歌已经可以在它的爬虫中执行 JS,你也会获得更多的 SEO 友好性;这几乎只是一个更安全的赌注
  2. 非 js 情况的回退。如果您的应用程序脚本加载缓慢,您仍然可以将实际页面呈现给您的客户端,而不会让他们在盯着空白屏幕时等待。这也允许在浏览器上禁用 JS 的人在大多数情况下仍然与您的应用程序交互;链接仍然有效,表单仍然可以提交,&c。
  3. 您可以获得客户端和服务器之间代码共享的额外好处。除了降低复杂性这一事实之外,没有什么必然令人难以置信的,因此,您可以获得降低复杂性的所有好处(可能减少耦合,更容易维护,结构更简单,同构性,等等。)
  4. 另一个附带好处是能够使用 react-router 的 html5 历史 API,而不是您必须使用的烦人的哈希片段内容。

You could even get crazy with this approach and handle things like placeholders for your app while it loads or provide other feedback mechanisms for a slow-loading state (a la Facebook while it loads).

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

The basic approach operates roughly in the following manner:

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

  1. Upon bootstrap, the node app instantiates a react-router instance based on routes.jsx
  2. Request goes to the server, which then uses express' req.pathto provide a route string for react-router to handle.
  3. React router then matches the provided route and tries to render the corresponding component for express to send back.
  4. React sends down the html response and your client gets to paint something regardless of the speed of your app script. We serve ours over a great CDN, but even with the best distribution and compression slow networks would still otherwise leave people with a temporarily blank screen.
  5. Having loaded the needed app script, React can use the same routes.jsxfile to take over and generate html with react-routerfrom here on out. Another benefit here is that your app code can be cached and future interactions hopefully won't even have to rely on another call.
  1. 在引导时,节点应用程序实例化一个 react-router 实例基于 routes.jsx
  2. 请求到达服务器,然后服务器使用 express'req.path为 react-router 提供路由字符串来处理。
  3. React 路由器然后匹配提供的路由并尝试渲染相应的组件以供 express 发回。
  4. React 发送 html 响应,无论您的应用程序脚本的速度如何,您的客户端都可以绘制一些东西。我们通过出色的 CDN 为我们提供服务,但即使有最好的分发和压缩,慢速网络仍然会让人们暂时处于空白屏幕。
  5. 加载所需的应用程序脚本后,React 可以使用相同的routes.jsx文件来接管并生成 html,react-router从现在开始。这里的另一个好处是您的应用程序代码可以被缓存,未来的交互有望甚至不必依赖另一个调用。

One more point worth noting: I use webpack to bundle my react code and now browser.jsxis the entry point. Before refactoring for server-side rendering it was previously app.jsx; you might need to re-configure your structure to accommodate what gets rendered where. :)

还有一点值得注意:我使用 webpack 来捆绑我的 React 代码,现在browser.jsx是入口点。在为服务器端渲染重构之前,它以前是app.jsx; 您可能需要重新配置您的结构以适应在何处呈现的内容。:)

le Code:

代码:

Browser.jsx

浏览器.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 (express server):

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

路由.jsx

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

App.jsx

应用程序.jsx

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

helpful links:

有用的网址: