我们在使用nextjs作为react服务端渲染的时候,一般情况下,只有使用nextjs的文件夹路径作为url地址就可以了。但是,如果有部分页面我们不想用服务端渲染,这部分页面可以使用单页应用来做。
那么问题来了,服务端渲染和单页应用怎么融合?首先,我们看理论上是否可行,单页应用,就是在一个服务端渲染的页面上,通过HashRoute来控制页面的展示,那么我们就用其中一个页面来做这个单页应用的载体好了。
1.首先我们以HashRoute来测试,按照正常的react-router-dom模式来写。
layout
const UserLayout = ({route})=>{
return <>
<NormalAppBar/>
<Link to={`/article-collections`}>article-collections</Link>
<div>{renderRoutes(route.routes)}</div>
</>
}
export default UserLayout
articles页面
const UserArticles = ({route})=>{
return <>article</>
}
export default UserArticles;
article-collections页面
const UserArticleCollections = ({route})=>{
return <>article collections</>
}
export default UserArticleCollections
routes.js
export default [
{
path: '/', component: UserLayout, routes: [
{path: '/article-collections', component: UserArticleCollections},
{path: '/', component: UserArticles},
]
}
]
user,这个页面就类似单页应用的index文件,由于页面是服务端渲染的,所以不需要render到元素下
import React from "react";
import {
HashRouter as Router
} from 'react-router-dom';
import {renderRoutes} from 'react-router-config';
import userRoutes from "./routes/user"
const UserView = () => {
return <>
<Router>{renderRoutes(userRoutes)}</Router>
</>
}
export default UserView
在pages/user/index.jsx下把组件加载进来
import React from "react";
import dynamic from "next/dynamic";
const UserView = dynamic(()=>import('../../app/views/user/index'),{ ssr: false }) //此处一定是动态加载,不要服务端渲染
const UserPage = ()=>{
return <>
<UserView/>
</>
}
export default UserPage
下面看下效果
可见react-router-dom是生效了。
2.但是如果不是管理后台的话,我们一般不会使用hashRoute来作为路径的,所以,我们还是要改成BrowserRouter模式。我们知道,在webpack-dev-server作为web服务的时候,是需要配置 historyApiFallback 的,historyApiFallback的作用就是把请求都指向index.html页面。那么我们根据这个思路,调整下配置。
next.config.js
async rewrites() {
return [
{
source: '/user/:path*',
destination:'/user'
},
]
},
同时routes也要做相应的调整,改成全路径
export default [
{
path: '/', component: UserLayout, routes: [
{path: '/user/article-collections', component: UserArticleCollections},
{path: '/', component: UserArticles},
]
}
]
看下效果
达到期望。
另外,如果既想用react-router-dom 又想要ssr,要怎么办呢?
在react-router里还有一个StaticRouter 这个是不需要dom的,所以我们可以如下操作
import React from "react";
import {StaticRouter} from 'react-router';
import {BrowserRouter} from 'react-router-dom';
import UserView from "../../frontend/views/user";
const isServer = (typeof window === 'undefined')
const UserPage = ({visitor, owner}) => {
return <>
{isServer && <StaticRouter>
<UserView visitor={visitor} owner={owner}/>
</StaticRouter>}
{!isServer && <BrowserRouter>
<UserView visitor={visitor} owner={owner}/>
</BrowserRouter>}
</>
}
判断是否是服务端渲染,客户端和服务端采用两套机制。