我正在try 为我的react应用程序设置服务器端渲染,并try 使用great react-router模块来处理非js情况(一些爬虫,当用户出于某种原因关闭js时).然而,我遇到了麻烦.我一直在用https://stackoverflow.com/a/28558545/3314701的伟大回应作为某种指导,但我发现了一些奇怪的错误.当我try 使用react.renderToString()时,我得到了一个持续的Syntax Error.我是否设置了错误的服务器端渲染、遗漏了一些明显的内容或其他内容?

我的设置:

Really basic Express server

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 100 component

// 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. 虽然谷歌已经可以在它的爬虫程序中执行JS,但你可以获得更多的SEO友好性;这几乎只是一个更安全的赌注
  2. 非js情况下的回退.如果你的应用程序脚本加载缓慢,你仍然可以将实际页面呈现给你的客户机,而不是让他们盯着空白屏幕等待.这也允许浏览器上禁用JS的人在大多数情况下仍然与你的应用程序交互;链接仍然有效,表单仍然可以提交,&;C
  3. 您可以从客户端和服务器之间的代码共享中获得额外的好处.除了复杂性降低这一事实之外,这并不一定令人难以置信,因此,您可以获得降低复杂性的所有好处(可能更少的耦合、更容易的维护、更简单的 struct 、同构性等等)
  4. 另一个附带的好处是能够使用react router的html5历史API,而不是你不得不使用的恼人的散列片段.

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

基本方法大致如下:

  1. 在 bootstrap 时, node 应用程序将基于routes.jsx个实例实例化react路由实例
  2. 请求发送到服务器,然后服务器使用express'req.path提供路由字符串,以便react路由处理.
  3. React router然后匹配提供的路由,并try 呈现相应的组件,以便express发回.
  4. React发送html响应,无论应用程序脚本的速度如何,客户端都可以绘制一些内容.我们通过一个伟大的CDN为我们的用户服务,但即使有最好的分发和压缩,缓慢的网络仍然会给用户留下一个暂时的空白屏幕.
  5. 在加载了所需的应用程序脚本后,React可以使用相同的routes.jsx文件接管并生成html,从现在开始使用react-router.这里的另一个好处是,你的应用程序代码可以被缓存,future 的交互甚至不需要依赖另一个调用.

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

代码:

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 (express server):

//...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>

有用的链接:

Reactjs相关问答推荐

在此上下文中如何在缓冲(隐藏)视频和渲染视频之间切换?

useReducer和带有回调的useState之间是否有实际区别?

react 路由导航不工作,但手动路由工作

包装组件可以避免子组件重新渲染.?

后端关闭时如何在REACTION AXIOS拦截器中模拟数据?

如何将图像(在代码中称为自己)定位在蓝色圈内的点上?我正在使用material UI和Reaction

使用REACT-RUTER-V6和ZUSTANDreact 中的身份验证

是否在Extra Reducer完成作业(job)时触发createListenerMiddleware?

有没有一种惯用的方式来接受特定的子元素?

Next.js Next-Auth登录函数重定向到http://localhost:3000/api/auth/error

无法找出状态正在被更改的位置

useRef()的钩子调用无效

定位数组中的一项(React、useState)

警告:遇到两个子元素拥有同一把 keys .键应该是唯一的,以便组件在更新时保持其身份

显示我是否加入聊天应用程序时出现问题

将 clerk 与 supabase 集成的最佳实践

Cypress 组件测试不渲染react 组件(但它的内容)

需要帮助在 document.queryselector 的 nextjs 页面上插入 useEffect 状态

如何从 extrareducer redux 更改对象中元素的值

React 似乎在触发另一个组件时重新渲染了错误的组件