Javascript 使用react router v4和express.js进行服务器渲染
我正在尝试使用最新版本的react router v.4设置服务器端渲染。我遵循了这个教程 刷新浏览器时,我收到以下错误:不变冲突:React.Children。仅应接收单个React元素子元素。 我的路由.jsx文件:Javascript 使用react router v4和express.js进行服务器渲染,javascript,node.js,reactjs,express,react-router,Javascript,Node.js,Reactjs,Express,React Router,我正在尝试使用最新版本的react router v.4设置服务器端渲染。我遵循了这个教程 刷新浏览器时,我收到以下错误:不变冲突:React.Children。仅应接收单个React元素子元素。 我的路由.jsx文件: export default () => <div> <Header /> <Match pattern="/" component={Home} /> <Match pattern="/about"
export default () =>
<div>
<Header />
<Match pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NotFound} />
</div>;
导出默认值()=>
;
在index.jsx中,我正在渲染应用程序
import BrowserRouter from 'react-router';
import Routes from './routes';
ReactDOM.render(<BrowserRouter> <Routes /> </BrowserRouter>, document.getElementById('app'));
从“反应路由器”导入浏览器路由器;
从“./Routes”导入路由;
ReactDOM.render(,document.getElementById('app'));
现在作为服务器,我使用的是express.js。以下是我的配置:
import Routes from '../routes';
server.use((req, res) => {
const context = createServerRenderContext();
let markup = renderToString(
<ServerRouter location={req.url} context={context} > <Routes /> </ServerRouter>);
const result = context.getResult();
if (result.redirect) {
res.writeHead(301, {
Location: result.redirect.pathname,
});
res.end();
} else {
if (result.missed) {
res.writeHead(404);
markup = renderToString(
<ServerRouter location={req.url} context={context}> <Routes /> </ServerRouter>);
}
res.write(markup);
res.end();
}
});
从“../Routes”导入路由;
服务器使用((请求、恢复)=>{
const context=createServerRenderContext();
让markup=renderToString(
);
const result=context.getResult();
if(result.redirect){
文书标题(301{
位置:result.redirect.pathname,
});
res.end();
}否则{
如果(结果未命中){
书面记录(404);
markup=renderToString(
);
}
res.write(标记);
res.end();
}
});
除了官方版本外,我没有找到任何关于此版本react routes的服务器渲染教程。
谁能帮我一下我做错了什么?谢谢 最大的问题是
只希望有一个孩子,因此您应该将它的孩子包装在div
中。这样做是为了使React路由器不受环境影响(您不能在React Native中呈现div
,因此RR希望您包含适当的包装器)
导出默认值()=>
;
作为第二个问题,您将
包含在
组件中,因此它将在服务器上呈现。你不想要这个。只有
应该在服务器上呈现。您应该将
进一步向上移动客户端组件层次结构,以避免出现这种情况
// App
export default () =>
<div>
<Header />
<Match pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Miss component={NotFound} />
</div>;
// index.js
render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('app'))
//应用程序
导出默认值()=>
;
//index.js
渲染((
),document.getElementById('app'))
已解决强>
第一个问题是,
标记周围有空格
正确的解决方案:
<ServerRouter location={req.url} context={context}><Routes /></ServerRouter>);
正确的解决方案:(我忘了放花括号):
由于
react-router
上不存在BrowserRouter
,请尝试从react-router-dom
中安装并导入它,以便以后的任何人使用,Ryan Florence添加了一段如何实现此目的的代码
//routes.js
常数路由=[
{
路径:“/”,
组成部分:家庭,
确切地说:是的
},
{
路径:'/gists',
组成部分:GIST
},
{
路径:'/settings',
组件:设置
}
]
//组成部分
类Home扩展了React.Component{
//在服务器渲染或cDM中调用
静态获取数据(匹配){
//我想在这里为params等做“匹配”。
返回获取(/*…*/)
}
状态={
//如果这是最初渲染的,我们将从服务器渲染中获取数据
数据:this.props.initialData | | null
}
componentDidMount(){
//如果最初呈现,我们已经有来自服务器的数据
//但是当在客户端中导航到时,我们需要获取
如果(!this.state.data){
this.constructor.fetchData(this.props.match)。然后(data=>{
this.setState({data})
})
}
}
// ...
}
//App.js
const App=({routes,initialData=[]})=>(
{routes.map((路由,索引)=>(
//为此特定路由从服务器传入initialData
))}
)
//server.js
从“反应路由器”导入{matchPath}
HandlerRequest((请求、回复)=>{
//我们可能需要一些递归,这样我们的路线就可以
//子路由,如`{path,component,routes:[{route,route}]}`
//然后减少到匹配路径的整个分支,但是
//出于说明目的,坚持平面布线配置
常量匹配=路由。减少((匹配,路由)=>{
常量匹配=匹配路径(req.url、route.path、route)
如果(匹配){
推({
路线,,
匹配,
承诺:route.component.fetchData?
route.component.fetchData(匹配):Promise.resolve(null)
})
}
复赛
}, [])
if(matches.length==0){
资源状态(404)
}
const promises=matches.map((match)=>match.promise)
承诺。所有(承诺)。然后(…数据)=>{
常量上下文={}
常量标记=renderToString(
)
if(context.url){
res.redirect(context.url)
}否则{
res.send(`
${markup}
DATA=${escapeBadStuff(JSON.stringify(DATA))}
`)
}
},(错误)=>{
handleError(res,error)
})
})
//client.js
渲染(
,
document.getElementById('root'))
)
我认为上面列出的答案已经过时了。截至今天,官方的文件建议使用服务器端渲染应用程序代替ServerRouter
您可以找到精彩的文档谢谢您的回答。我修复了此问题,但仍然出现相同的错误。您能用代码的当前状态更新您的问题吗?我更新了代码并删除了App.jsx文件,因为不再需要使用它。完整错误是什么?它是否引用了代码中的特定组件或行?请删除
组件周围的空格。当JSX转换为React.createElement
表达式时,这些将被解释为空格。
<ServerRouter location={req.url} context={context}><Routes /></ServerRouter>);
import Link from 'react-router';
import { Link } from 'react-router';
// routes.js
const routes = [
{
path: '/',
component: Home,
exact: true
},
{
path: '/gists',
component: Gists
},
{
path: '/settings',
component: Settings
}
]
// components
class Home extends React.Component {
// called in the server render, or in cDM
static fetchData(match) {
// going to want `match` in here for params, etc.
return fetch(/*...*/)
}
state = {
// if this is rendered initially we get data from the server render
data: this.props.initialData || null
}
componentDidMount() {
// if rendered initially, we already have data from the server
// but when navigated to in the client, we need to fetch
if (!this.state.data) {
this.constructor.fetchData(this.props.match).then(data => {
this.setState({ data })
})
}
}
// ...
}
// App.js
const App = ({ routes, initialData = [] }) => (
<div>
{routes.map((route, index) => (
// pass in the initialData from the server for this specific route
<Route {...route} initialData={initialData[index]} />
))}
</div>
)
// server.js
import { matchPath } from 'react-router'
handleRequest((req, res) => {
// we'd probably want some recursion here so our routes could have
// child routes like `{ path, component, routes: [ { route, route } ] }`
// and then reduce to the entire branch of matched routes, but for
// illustrative purposes, sticking to a flat route config
const matches = routes.reduce((matches, route) => {
const match = matchPath(req.url, route.path, route)
if (match) {
matches.push({
route,
match,
promise: route.component.fetchData ?
route.component.fetchData(match) : Promise.resolve(null)
})
}
return matches
}, [])
if (matches.length === 0) {
res.status(404)
}
const promises = matches.map((match) => match.promise)
Promise.all(promises).then((...data) => {
const context = {}
const markup = renderToString(
<StaticRouter context={context} location={req.url}>
<App routes={routes} initialData={data}/>
</StaticRouter>
)
if (context.url) {
res.redirect(context.url)
} else {
res.send(`
<!doctype html>
<html>
<div id="root">${markup}</div>
<script>DATA = ${escapeBadStuff(JSON.stringify(data))}</script>
</html>
`)
}
}, (error) => {
handleError(res, error)
})
})
// client.js
render(
<App routes={routes} initialData={window.DATA} />,
document.getElementById('root')
)