【问题标题】:React-route and server-side rendering - how to render components with dataReact-route 和服务器端渲染 - 如何用数据渲染组件
【发布时间】:2016-03-21 17:31:02
【问题描述】:

当在服务器端渲染组件时,我已经能够将数据传递给响应组件,例如:

app.get('/', function (req, res, next) {
    var reactHtml = ReactDOMServer.renderToString(component({data}));
    res.render('index.jade', {reactOutput: reactHtml});
});

现在我把它改成了

server.js:

app.get('*', (req, res) => {
    // match the routes to the url
    match({ routes: routes, location: req.url }, (err, redirect, props) => {
        props.data = {q:123};
        const appHtml =  ReactDOMServer.renderToString(routerContext(props));
        res.render('index.jade', {reactOutput: appHtml});
    })
});

路由器上下文

module.exports = React.createClass({
    render: function(){return(<RouterContext {...this.props}  />)}
});

和路线

module.exports = (
    <Route path="/" component={App} {...this.props}>
        <IndexRoute component={List}/>
        <Route path="/about" component={About}/>
    </Route>
);

App.jsx

var App = React.createClass({
    render: function () {
        console.log(this.props);
        return (
            <div id="app-root" className="container">
                <Nav/>
                <h1>{this.props.q}</h1>
                {this.props.children}
                <div className="Row">
                    <div className="footer col-lg-12"></div>
                </div>
            </div>
        )
    }

});

因此,例如,我想在我的 App 组件中从这一点 props.data = {q:123}; 获取数据,但是当我在 App 组件渲染方法中执行 console.log(this.data); 时,我有历史、位置等,但没有我的数据。无论如何要从 server.js 逻辑将数据 {q:123} 传递给组件(应用程序或列表或关于)道具?

(假设我们现在总是加载一些数据)

【问题讨论】:

    标签: node.js express reactjs react-router isomorphic-javascript


    【解决方案1】:

    您需要序列化您的数据并将其添加到服务器渲染的页面中,例如,如果您将其作为initialState 传递给您的根组件:

    renderInitialState() {
        const innerHtml = `window.__INITIAL_STATE__ = ${JSON.stringify(this.props.initialState)}`;
        return <script dangerouslySetInnerHTML={{__html: innerHtml}} />;
    }
    

    所以:

    <script>window.__INITIAL_STATE__ = JSON.stringify(yourObject);</script>
    

    请注意,您似乎在使用 Jade 来渲染“shell”而不是 React。所以同样的事情也适用,但要为玉模板做。

    您通常会在 &lt;body&gt; 的底部呈现它(但在您的应用 js 脚本上方)。

    如果您在客户端状态中使用通量,您将在创建商店时传递此对象,例如对于还原:

    const store = configureStore(window.__INITIAL_STATE__);
    

    【讨论】:

    • 我以翡翠为例,我在 react-route 之前做了什么。我理解你的意思,但看起来它会消除服务器端渲染的优点(seo 友好等),因为包含的 js 必须在浏览器中执行。当我使用jade + react 时,我在服务器端使用数据渲染整页。这就是我现在尝试用 react-router 做的(并且没有玉)
    • 实际上,我的错 - 我写错了问题名称。所以,现在就改变它。
    • 这个想法是在服务器上渲染,然后传递所需的数据,以便可以在客户端上进行第二次渲染,这是相同的。所有同构应用程序都必须将数据/存储传递给客户端 - 例如,请参见 github.com/DominicTobias/universal-react
    【解决方案2】:

    您的 props.data = {q:123}; 不会在任何地方被调用,因为 RouterContext 不知道如何处理它。

    您传递给match 的回调函数的三个参数是:

    1. 错误:如果发生错误,则为 javascript Error 对象,否则为 undefined
    2. redirectLocation:如果路由是重定向,则为 Location 对象, undefined 否则
    3. renderProps:如果路由匹配,您应该传递给路由上下文的道具,undefined 否则。

      如果三个参数都是undefined,这意味着没有找到匹配的路由 给定位置。

    要了解机制,您可以在此处添加 console.dir

    match({ routes: routes, location: req.url }, (err, redirect, props) => {
            console.dir(props);
            const appHtml =  ReactDOMServer.renderToString(routerContext(props));
            res.render('index.jade', {reactOutput: appHtml});
        })
    });
    

    console.dir(props.components) 会向您显示所有传递给渲染的组件。你也可以在这里创建一个新的 react 元素并推送到props.components

    将数据传递到您的应用组件 没有商店

    修改此函数,使其具有检索数据的能力,并使用新数据创建虚拟组件

    routes.jsx

    module.exports = function(serverData){
      var Dummy= React.createClass({
         render: function () {
           return (
             <div> customProps=serverData </div>
                 );}
             });
    
      return(
        <Route path="/" component={App}>
            <Route path='data' component={{PonyMan: Dummy}}
            <IndexRoute component={List}/>
            <Route path="/about" component={About}/>
        </Route>
       );
    }
    

    server.jsx:

    app.get('*', (req, res) => {
        myCustomData = 'I like ponies'
        const routes = createRoutes(myCustomData); //from imported routes.jsx
        match({ routes: routes, location: req.url }, (err, redirect, props) => {
            const appHtml =  ReactDOMServer.renderToString(routerContext(props));
            res.render('index.jade', {reactOutput: appHtml});
        })
    });
    

    App.js

    var App = React.createClass({
        render: function () {
            return (
                <div id="app-root" className="container">
                    <Nav/>
                    <h1>{this.props.main}</h1>//this now shows <div>I Like Ponies</div>
    
                    //this can still display other routes
                    {this.props.children}
                    <div className="Row">
                        <div className="footer col-lg-12"></div>
                    </div>
                </div>
            )
        }    
    });
    

    附:使用 redux 商店会更容易 检查此项目以查看带有服务器端渲染的 react-router redux express mongoose 项目的一个很好的示例 https://github.com/choonkending/react-webpack-node

    【讨论】:

    • 非常感谢。我检查了一个演示。看起来它正是我需要的。我必须花几个小时阅读资源。实际上,我已经阅读了 react-route 源并找到了 fork 的路径并修改了 RouterContext 中的“getChildContext”函数,以使其将附加数据传输到子上下文。但是你的方式看起来更方便
    • 是的,我认为这更像是一个“黑客”。将 redux 商店之类的东西用于动态内容确实更容易。如果您想进行初始服务器渲染,您可以添加类似 this 的内容,它会在渲染之前为您的自定义数据解析承诺,因此您仍然可以获得所有 SEO 专家
    猜你喜欢
    • 2017-11-05
    • 1970-01-01
    • 2015-05-07
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多