一个 Next.js 路由中的两个不同子域

Posted

技术标签:

【中文标题】一个 Next.js 路由中的两个不同子域【英文标题】:Two different subdomains in one Next.js routing 【发布时间】:2020-07-29 14:37:06 【问题描述】:

我想使用Next.js(React.js 和React-router)构建一个新平台。会有两个空格。一个前端用户和一个所有者,允许他们管理所有用户。我想将这两个区域分成两个子域,如下所示:

front.domain.com panel.domain.com

React-router 不支持子域路由,所以我尝试寻找另一种方法。我发现了类似的东西,但我不确定这是我想要的。请指教。

<BrowserRouter>
  <Route path="/" render=props => 
    const [subdomain] = window.location.hostname.split('.');
    if (subdomain === 'panel') return <PanelLayout ...props/>;
    return <FrontLayout ...props/>;
  />
</BrowserRouter>

【问题讨论】:

next使用的时候不需要react-router,自带router。 【参考方案1】:

由于多种原因,您无法在子域之间拆分 1 个 Next.js 应用程序。 根据经验,我有一个类似的要求(3 个区域)我从一个应用程序分成 3 个开始(使用子路径)

    “区域”之间的资产(css、js 库)泄漏。 一个包含 3 个区域的大型应用意味着,每次更改都需要重新部署所有区域(一个大型可部署) 建造时间,建造 3 个区域会更长。 每个区域可能会引入不同的需求,例如管理区域的 UI 组件,但“前端”区域的自定义 UI 组件、身份验证、翻译等等

最终得到 3 个独立的 Next.js 应用程序,它们在 yarn 工作空间内管理并由特定区域部署。

在我解释了我的经验之后,您可以使用诸如 nginx 的反向代理来实现设置,以便在您的下一个应用中将子域映射到子路径。

假设您有 3 个区域,前台、管理员、用户。

www.domain.com/some-page => 应该映射到localhost:3000/front/some-pageusers.domain.com/some-page => 应该映射到localhost:3000/users/some-pageadmin.domain.com/some-page => 应该映射到localhost:3000/admin/some-page

// www.domain.com.conf 

server 
    listen       80;
    server_name  www.domain.com;
    access_log   /var/log/nginx/access.log  main;
    root         html;
 
    location / 
      proxy_pass   http://127.0.0.1:3000/front/; // <-- the last slash is important
    

  
// users.domain.com.conf

server 
    listen       80;
    server_name  users.domain.com;
    access_log   /var/log/nginx/access.log  main;
    root         html;
 
    location / 
      proxy_pass   http://127.0.0.1:3000/users/; // <-- the last slash is important
    

  

注意

    您还需要重写静态资产。

【讨论】:

将其拆分为 2 个单独的应用程序(例如 adminfront)将更容易管理代码库,但您也会丢失用户登录状态。例如,如果用户登录admin 并且您需要在front 应用程序上了解它,则此功能将更难实现并且需要更复杂的解决方案。 这是一个取舍的问题,这取决于什么是auth解决方案,您可以将管理令牌保存在一个 cookie中,可以在前面访问还有 感谢有关域 cookie 的提示,我不知道您可以在同一根的子域之间共享身份验证 cookie。它使事情变得更容易。 @felixmosh 能否提供“重写静态资产”的示例代码 @NitinSinghNaruka,给我一个静态资产路径的例子【参考方案2】:

我设法使用自定义快速服务器创建子域。这是一个没有资产的空白应用程序,我还没有在具有资产(CSS、图像等)的真实应用程序上尝试过这个

我有以下页面文件夹结构:

pages/
├── admin/
│   ├── index.js
│   └── sample-page.js
└── member/
    ├── index.js
    └── accounts/
        └── dashboard.js

当您使用next dev 时,这是默认设置。这将产生以下路线:

http://lvh.me:3000/admin http://lvh.me:3000/admin/sample-page http://lvh.me:3000/member http://lvh.me:3000/member/accounts/dashboard

但是使用自定义的server.js 文件并使用node server.js 运行我们的开发服务器,这将产生以下路由:

http://admin.lvh.me:3000 http://admin.lvh.me:3000/sample-page http://lvh.me:3000 http://lvh.me:3000/accounts/dashboard

我们server.js文件的内容:

const express = require('express')
const next = require('next')
const vhost = require('vhost')

const port = process.env.PORT || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next( dev )
const handle = app.getRequestHandler()

app.prepare().then(() => 
  const mainServer = express()
  const adminServer = express()
  const memberServer = express()

  adminServer.get('/', (req, res) => 
    return app.render(req, res, '/admin', req.query)
  )

  adminServer.get('/*', (req, res) => 
    return app.render(req, res, `/admin$req.path`, req.query)
  )

  adminServer.all('*', (req, res) => 
    return handle(req, res)
  )

  memberServer.get('/', (req, res) => 
    return app.render(req, res, '/member', req.query)
  )

  memberServer.get('/*', (req, res) => 
    return app.render(req, res, `/member$req.path`, req.query)
  )

  memberServer.all('*', (req, res) => 
    return handle(req, res)
  )

  mainServer.use(vhost('admin.lvh.me', adminServer))
  mainServer.use(vhost('lvh.me', memberServer))
  mainServer.use(vhost('www.lvh.me', memberServer))
  mainServer.listen(port, (err) => 
    if (err) throw err

    console.log(`> Ready on http://lvh.me:$port`)
  )
)

查看 repo 以了解实际情况。

回购:https://github.com/dcangulo/nextjs-subdomain-example

【讨论】:

【参考方案3】:

这是一个使用 Next.js 的 i18n 在同一个 Next.js 站点上托管多个域(同时保持多种语言和静态站点生成/SSG)的示例系统:

https://github.com/tomsoderlund/nextjs-multi-domain-locale

Vercel 现在支持通配符域 (*.mysite.com),这很酷!

【讨论】:

对我来说这是最好、最简单的解决方案。你不需要改变你的服务器,无论是 NGINX 还是 Express。如前所述,NextJS 实际上确实通过 Locales 支持这一点。谢谢@Tom 字面上支持,你只需要使用域名作为语言环境nextjs.org/docs/advanced-features/i18n-routing【参考方案4】:

如果您在 Now 上托管,那么您可以使用他们的通配符域并让您的客户端反应代码相应地安装与子域相关的组件树。

【讨论】:

【参考方案5】:

您检查来自 Next 路由器的 pathname 是否以预定义的允许子路径列表开始

例如

const subdomainPaths = 
  request: '/request',
  company: '/company',
;

const isSubdomainRoute = () => 
  const paths = Object.values(subdomainPaths).filter((path) => router.pathname.startsWith(path));
  return !!paths.length;
;

if (window.location.host.includes('subdomain.mysite') && !isSubdomainRoute()) 
  // show 404 or redirect somewhere else
  return <div>404 - not found</div>;

完整示例:

export const iss-s-r = typeof window === 'undefined';

export default function App(props: Props) 
  const  Component, pageProps  = props;
  const router = useRouter();

  const subdomainPaths = 
    request: '/request',
    company: '/company',
  ;

  const isSubdomainRoute = () => 
    const paths = Object.values(subdomainPaths).filter((path) => router.pathname.startsWith(path));
    return !!paths.length;
  ;

  if (!iss-s-r) 
    if (window.location.host.includes('subdomain.mysite') && !isSubdomainRoute()) 
      // show 404 or redirect somewhere else
      return <div>404 - not found</div>;
    
  

  // otherwise fallthrough to the normal Next.js return

  return (
    <>
      <Head>
        <title>My Site</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <AllTheProviders>
        <Component ...pageProps />
      </AllTheProviders>
    </>
  );

【讨论】:

以上是关于一个 Next.js 路由中的两个不同子域的主要内容,如果未能解决你的问题,请参考以下文章

在 Next.js 页面中检索子域

#yyds干货盘点# Next.js通配符子域

Next.js 在屏幕之间导航时维护路由

Next.js + react 中的路由守卫

网站教程中的 Next.js 路由错误

Next.js:在实现私有路由时,如何在重定向之前防止未经授权的路由/页面的闪烁?