在服务器和客户端上呈现不同的React路由器链接

时间:2015-11-03 17:55:27

标签: reactjs react-router isomorphic-javascript

我在渲染同构应用程序时遇到问题,当我不使用react-router(1.0.0-rc3)时它工作正常,但是当我介绍路由器时,特别是一个呈现链接的组件:

const React = require('react');
const Link = require('react-router').Link;
module.exports = class About extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <ul>
        <li><Link to="/about">About</Link></li>
        <li><Link to="/list">List</Link></li>
      </ul>
    );
  }
}

服务器和客户端的输出不同,我收到此警告

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) n><a class="" href="#/about" data-reacti
(server) n><a class="" href="/about" data-reactid

所以服务器(或客户端)以不同的方式呈现href标记,这就是我呈现服务器端的方式

const React = require('react');
const reactDOMServer = require('react-dom/server');
const tmpl = require('blueimp-tmpl');
const fs = require('fs');
const path = require('path');
const templateFunction = tmpl.tmpl;
const match = require('react-router').match;
const RoutingContext = require('react-router').RoutingContext;

const routes = require('../../app/routes');
const App = require('../../app/app');
const serverProps = require('../../server.props');

templateFunction.load = function(id) {
  const filePath = path.resolve(serverProps.publicPath, id);
  return fs.readFileSync(filePath, "utf8");
};

module.exports = function*(next) {
  match({ routes, location: this.url }, (error, redirectLocation, renderProps) => {
      if (error) {
        this.throw(error.message);
      } else if (redirectLocation) {
        this.redirect(redirectLocation.pathname + redirectLocation.search);
      } else if (renderProps) {
        const html = reactDOMServer.renderToString( <RoutingContext {...renderProps} /> );        
        this.body = templateFunction("index.html", html);
      } else {
        this.throw(404);
      }
    });
};

我在这里使用的模板引擎是blueimp-tmpl,我首先怀疑它可能会在渲染时对href-hash-sign执行某些操作,但我记录了输出renderToString并且href-hash-sign已经消失了进入模板。

我在npm历史包中进行了一些挖掘(它是react-router的对等依赖项),它似乎是生成链接的href部分的组件,但是无法弄清楚为什么它会以不同的方式呈现它。 / p>

有什么想法吗?

编辑,这是路线

const React = require('react');
const Router = require('react-router').Router;
const Route = require('react-router').Route;

const BaseLayout = require("./components/base-layout/base-layout");
const List = require("./components/list/list");
const About = require("./components/about/about");

module.exports = (
  <Router>
      <Route path="/" component={BaseLayout}>
        <Route path="about" component={About} />
        <Route path="list" component={List} />
      </Route>
    </Router>
);

BR twd

1 个答案:

答案 0 :(得分:1)

好的,解决了。

最初的问题,客户端的href有哈希标签类型的链接是因为缺少历史配置解决了:

// router.js
const React = require('react');
const Router = require('react-router').Router;
const Route = require('react-router').Route;

const BaseLayout = require("./components/base-layout/base-layout");
const List = require("./components/list/list");
const About = require("./components/about/about");

// This was the missing part... 
const createBrowserHistory = require('history/lib/createBrowserHistory');

module.exports = (
  <Router history={createBrowserHistory()}>
      <Route path="/" component={BaseLayout}>
        <Route path="about" component={About} />
        <Route path="list" component={List} />
      </Route>
    </Router>
);

现在链接看起来很好,但这导致渲染在服务器端失败,因为我在渲染时使用了整个路由器,而createBrowserHistory需要DOM才能工作。通过将路径拉出到这样的不同文件来解决:

// routes.js
const React = require('react');
const Route = require('react-router').Route;

const BaseLayout = require("./components/base-layout/base-layout");
const List = require("./components/list/list");
const About = require("./components/about/about");

module.exports = (
  <Route path="/" component={BaseLayout}>
    <Route path="about" component={About}/>
    <Route path="list" component={List}/>
  </Route>
);

使用服务器上的纯路由,如此处所示Server Rendering Docs。 这使服务器端渲染工作,但现在我再次打破了客户端渲染,因为我的路由器组件不了解如何使用在不同文件中声明的路由,它使用正确的语法很容易修复:

// router.js
const React = require('react');
const Router = require('react-router').Router;

const routes = require("./routes");
const createBrowserHistory = require('history/lib/createBrowserHistory');
module.exports = (
  <Router routes={routes} history={createBrowserHistory()}>    
  </Router>
);