服务器端渲染 React

Server Side Rendering React

一段时间以来,我一直在尝试为 Redux/React 应用程序实现服务器端呈现。我已经根据我遵循的示例设置了所有内容,但发生了一些奇怪的事情。

当我查看 Google Chrome 时间轴中的渲染过程时,我注意到我的 html 出现了一瞬间,然后消失了,然后渲染了来自再次刮擦(有点像当 React 尝试挂载它然后只使用正常的客户端渲染时它忽略了我的服务器端内容)。

我已经检查过发送给客户端的内容,似乎没问题。所有 html、head 标签和分配给 window 的 javascript 都在那里。此外,当它最初尝试从服务器端渲染 html 时,它在那一瞬间看起来不错(我检查了 Chrome 时间轴并逐帧查看它正在渲染的图像)。

我想知道从以下设置中是否可以立即看出任何内容或可能发生的任何想法。以下是一些伪代码,显示了我实现的内容。如果任何评论需要更多代码或信息,我将进行编辑。

// client - configureStore and configureRoutes are custom 
// functions that just return the store with initial state and the routes.
const serverState = JSON.parse(window._SERVERSTATE);
const store = configureStore(browserHistory, serverState);
const history = syncHistoryWithStore(browserHistory, store);
const routes = configureRoutes(store);

render(
    <Provider store={store}>
        <Router history={history} routes={routes}/>
    </Provider>,
    document.getElementById('main')
);

// server - node.js
const initialState = setupState();
const memoryHistory = createMemoryHistory(req.url);
const store = configureStore(memoryHistory, initialState);
const history = syncHistoryWithStore(memoryHistory, store);
const routes = configureRoutes(store);

match({ history, routes, location: req.url }, (err, redirectLocation, renderProps) => {

    if (err) {
        return next(err)
    }

    if (redirectLocation) {
        return res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    }

    // Fetches all data need to render components by calling their static initializeActions functions
    fetchData(store.dispatch, renderProps.components, renderProps.params)
        .then(() => {

            const body = renderToString(
                <Provider store={store}>
                    <RouterContext {...renderProps} />
                </Provider>
            );

            const helmetHeaders = Helmet.rewind();

            const state = JSON.stringify(store.getState());

            const html = `
                <!DOCTYPE html>
                <html>
                    <head>
                        ${helmetHeaders.title.toString()}
                        <link rel="stylesheet" type="text/css" href="/styles.css">
                    </head>
                    <body>
                        <div id="main">
                            ${body}
                        </div>
                        <script> 
                            window._SERVERSTATE = ${JSON.stringify(state)}
                        </script>
                        <script src="/app.js"></script>
                    </body>
                </html>
        })
})

// Typical component
class Example extends React.Component{
    static initializeActions = [ ExampleAction ]

    render() {
        <div>Hello</div>
    }
}

如果您的客户端代码再次渲染,则意味着您的服务器渲染与客户端渲染不同,因此您的客户端代码只需清除所有内容并重新渲染即可。

这是我的做法,只需使用您的本地 bundle.js 替换您的 bundle.min.js 来调试您的应用程序,您应该会发现有关 react markup checksum 的警告,然后比较什么你的服务器渲染和你的客户端渲染,看看有什么区别。

我的问题是我的服务器上有 data-react-id 属性。所以我用 ReactDom.renderToStaticMarkup 来代替 ReactDom.renderToString。你可以先试试

我终于知道哪里出了问题。如果您使用带有 require.ensure 的异步路由,那么您必须在呈现客户端之前调用 match:

match({ history, routes }, (err, redirectLocation, renderProps) => {
    render(
        <Provider store={store}>
            <Router history={history} routes={routes}/>
        </Provider>,
        document.getElementById('main')
    );
});

我已经为 React 服务器端渲染创建了 starter。它是使用 redux 和 react-router v4 的关闭建议构建的。请查看

https://github.com/gzoreslav/react-redux-saga-universal-application