在底部找到编辑。
我一直在研究很多教程,甚至是位于此处的 React 培训服务器端渲染的文档:https : //reacttraining.com/react-router/web/guides/server-rendering
对我来说一直有问题的是使用其他路由以获得完整的堆栈应用程序。例如,我们将使用 MERN 堆栈 ( mongodb
express
React/Redux
nodejs
)。假设我们有两条路线
app.js
像这样:
app.use('/users', users);
app.use('/posts', posts);
并且我们的服务器端渲染的设置与这些教程中的大多数一样,实际上我找不到一个不是这样设置的,为了节省时间,我将把 SSR 代码放在那里。如果您不知道如何进行 SSR,请查看我上面提供的链接中的文档。我认为它可能不需要,但如果让我知道,我可以添加一个简单的设置。
就像是:
app.get('*', (req, res) => { //server side rendering code here});
所以这里的问题是它触及每条路由和服务器端渲染的内部React Router 4
,大多数人会使用matchRoutes
函数并传入他们用于反应端的路由。
好的,我已经解释了很多,但并不是真正的问题是什么:
问题:在反应方面,让我们说使用动作,我们将在这里使用 axios 作为一个简单的例子。
如果我做:
axios.get('/posts')
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
这遇到了问题。对于任何了解 express 和 SSR 的人,您都会知道原因。这app.get('*')
是命中每条路线,或者更确切地说,每条路线请求都将通过这里,问题是当我们向/posts
两种情况之一发送请求时会发生。
情况 1:/posts
反应端的路由文件中有一个客户端路由,这种情况将返回服务器端 html,response.data
这显然不是我们想要的。
情况 2:/posts
反应端的路由文件中没有客户端路由,如果您正确设置了 SSR,这种情况将返回 404 错误或类似于路由或页面未找到的内容。
我需要一种能够使用我的后端的方法,但也能够使用 SSR 进行反应。
可能的解决方案?
使用某种类型的代理,/api/routename
然后说如果然后使用代理,您的调用不会通过app.get('*')
.
或者某种仅在单个路由上执行 SSR 的方法。
或者排除某些路由,但能够使用所有客户端路由。在这我的意思是,如果我有 client side/users
和 express side /users
,如果我 exclude /users
,我不希望 client 端中断,但有些人仍然排除 express 端。
如果可能的话,我不完全确定如何执行这些解决方案,但我需要能够使用我的快速路线,而且我找不到具体的方法来做到这一点,或者什么是最佳实践。
编辑:
服务器端 app.js:
require('babel-core/register')({
presets: ['env', 'react', 'stage-0', 'stage-1']
});
const pkg_json = require('./package.json');
const vertex = require('vertex360')({ site_id: pkg_json.app });
var renderer = require('./renderer.js');
// initialize app
const app = vertex.app();
// import routes
const index = require('./routes/index');
const api = require('./routes/api');
const users = require('./routes/users');
// set routes
app.use('/api/users', users);
// hopefully will be used on every Route, this should handle SSR RR4
app.use(renderer);
module.exports = app;
渲染器.js:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import { Helmet } from 'react-helmet';
import { matchRoutes } from 'react-router-config';
import routes from './src/routes';
import createStore from './src/stores';
function handleRender(req, res) {
const store = createStore.configure(null); // create Store in order to get data from redux
const promises = matchRoutes(routes, req.path)
.map(({ route }) => {
// Matches the route and loads data if loadData function is there
return route.loadData ? route.loadData(store) : null;
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve); // lets all data load even if route fails
});
}
});
Promise.all(promises).then(() => {
const context = {};
if (context.url) {
return res.redirect(301, context.url); // redirect for non auth users
}
if (context.notFound) {
res.status(404); // set status to 404 for unknown route
}
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(routes)}</div>
</StaticRouter>
</Provider>
);
const initialState = serialize(store.getState());
const helmet = Helmet.renderStatic();
res.render('index', { content, initialState, helmet });
});
}
module.exports = handleRender;
反应入口点:
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import { Helmet } from 'react-helmet';
import { matchRoutes } from 'react-router-config';
import routes from './src/routes';
import createStore from './src/stores';
function handleRender(req, res) {
const store = createStore.configure(null); // create Store in order to get data from redux
const promises = matchRoutes(routes, req.path)
.map(({ route }) => {
// Matches the route and loads data if loadData function is there
return route.loadData ? route.loadData(store) : null;
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve); // lets all data load even if route fails
});
}
});
Promise.all(promises).then(() => {
const context = {};
if (context.url) {
return res.redirect(301, context.url); // redirect for non auth users
}
if (context.notFound) {
res.status(404); // set status to 404 for unknown route
}
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(routes)}</div>
</StaticRouter>
</Provider>
);
const initialState = serialize(store.getState());
const helmet = Helmet.renderStatic();
res.render('index', { content, initialState, helmet });
});
}
module.exports = handleRender;
感谢@VivekN,您这样做的方式是确保您app.get('*')
的路线低于每个声明的路线。这应该允许您的其他路线首先通过。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句