Next.js 从/到另一个页面重定向

新手上路,请多包涵

我是 Next.js 的新手,我想知道如何从起始页 ( / ) 重定向到 /hello-nextjs 。一旦用户加载页面,然后确定路径 === / 是否重定向到 /hello-nextjs

react-router 中,我们执行以下操作:

 <Switch>
  <Route path="/hello-nextjs" exact component={HelloNextjs} />
  <Redirect to="/hello-nextjs" /> // or <Route path="/" exact render={() => <Redirect to="/hello-nextjs" />} />
</Switch>

原文由 Arthur 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.1k
2 个回答

警告

首先,您应该评估是否需要客户端重定向(在 React 内)、服务器端重定向(301 HTTP 响应)或服务器端重定向 + 身份验证(301 HTTP 响应但也有一些逻辑来检查身份验证)

这是我能写的最完整的答案。但是,在大多数情况下,您不需要任何这些。就像在任何 React 应用程序中一样重定向。 首先首选客户端重定向。 只需使用 useEffect + router.push ,就是这样。

服务器端重定向很诱人,特别是当您想要“保护”私人页面时,但您应该评估您是否真的需要它们。通常,你不会。它们会引发意想不到的复杂性,例如管理身份验证令牌和刷新令牌。相反,您可能希望将网关服务器、反向代理或任何前端服务器添加到您的体系结构中,例如处理此类检查。

请记住,Next.js 只是 React 应用程序,使用 Next.js 高级功能(如 SSR)需要付出一定的代价,这在您的上下文中应该是合理的。

旧下一个 9.4 答案

嗨,这是一个适用于所有场景的示例组件:

Vulcan next starter with Private access

此处的示例用法

答案是巨大的,如果我以某种方式违反了 SO 规则,我很抱歉,但我不想粘贴 180 行代码。如果您想同时支持 SSR 和静态导出,则 Next 中没有简单的模式来处理重定向。

以下场景都需要特定的模式:

  • 服务器端渲染:如果允许,我们会渲染页面,如果不允许,则进行 HTTP 重定向
  • 静态渲染(服务器端):我们什么都不渲染,但我们仍然将页面包含在构建中
  • 客户端渲染,静态导出后:我们检查客户端是否用户是 auth,并且重定向与否。在此检查期间或如果我们正在重定向,我们不会显示任何内容(或加载程序)。
  • 客户端使用下一个/路由器重定向后的客户端渲染:相同的行为。
  • SSR 之后的客户端渲染:我们使用 getInitialProps 传递的 props 来判断用户是否被允许,直接在第一次渲染时。它只是快一点,你避免了空白闪光。

在撰写本文时(下一个 9.4),您必须使用 getInitialProps ,而不是 getServerSideProps ,否则您将失去执行能力 next export

下一个 9.5 更新

正如@Arthur 在评论中所说,9.5 还包括 在 next.config.js 中设置重定向 的可能性。我还不清楚这个功能的局限性,但它们似乎是全局重定向,例如当您需要移动页面或仅在有限的时间内允许访问时。因此,它们并不意味着例如处理身份验证,因为它们似乎无权访问请求上下文。再次,有待确认。

接下来 10 个新的文档更新

此解决方案特定于根据身份验证的重定向。

现在记录身份验证模式

我不喜欢从 getServerSideProps 进行身份验证,因为在我看来为时已晚,并且很难使用高级模式(例如处理刷新令牌)进行设置。但这是官方的解决方案。

您可能还想根据 Vercel 仪表板的工作方式(在撰写本文时)检查 此票证中 记录的方法,以防止未经身份验证的内容闪现

下一个 10.2 标头和基于 cookie 的重写更新

Next 10.2 引入了基于 headers 和 cookie 的 重写。这是基于身份验证 cookie 或标头的存在重定向服务器端的好方法。

但是,请记住,这不是 安全 重定向。 用户可以使用虚假令牌更改其请求标头。您仍然需要网关、反向代理或前端服务器来实际检查令牌有效性并正确设置标头。

编辑:请注意,URL 不会改变。重写将 URL 指向应用程序的现有页面,而不更改 URL => 它允许您拥有“虚拟”URL。

示例用例:假设您有一个已翻译的页面 src/contact.tsx 和 i18n 重定向设置。您可以通过将 — 重写为 /de/contact /de/kontact 来翻译页面名称本身(“联系人”)。

下一个 12 更新

现在, 中间件 让您可以完全控制服务器端重定向。

但是,请再次记住,大多数情况下,客户端重定向和检查就足够了。

旧答案(有效,但会有混乱的静态渲染)

半官方示例

with-cookie-auth 示例重定向到 getInitialProps 。我不确定它是否是一个有效的模式,但这里是代码:

 Profile.getInitialProps = async ctx => {
  const { token } = nextCookie(ctx)
  const apiUrl = getHost(ctx.req) + '/api/profile'

  const redirectOnError = () =>
    typeof window !== 'undefined'
      ? Router.push('/login')
      : ctx.res.writeHead(302, { Location: '/login' }).end()

  try {
    const response = await fetch(apiUrl, {
      credentials: 'include',
      headers: {
        Authorization: JSON.stringify({ token }),
      },
    })

    if (response.ok) {
      const js = await response.json()
      console.log('js', js)
      return js
    } else {
      // https://github.com/developit/unfetch#caveats
      return await redirectOnError()
    }
  } catch (error) {
    // Implementation or Network error
    return redirectOnError()
  }
}

它同时处理服务器端和客户端。 fetch 调用是实际获取身份验证令牌的调用,您可能希望将其封装到单独的函数中。

我会建议什么

1. 在服务器端渲染上重定向(避免在 SSR 期间闪烁)

这是最常见的情况。此时您要重定向以避免初始页面在首次加载时闪烁。

 MyApp.getInitialProps = async appContext => {
    const currentUser = await getCurrentUser(); // define this beforehand
    const appProps = await App.getInitialProps(appContext);
    // check that we are in SSR mode (NOT static and NOT client-side)
    if (typeof window === "undefined" && appContext.ctx.res.writeHead) {
      if (!currentUser && !isPublicRoute(appContext.router.pathname)) {
          appContext.ctx.res.writeHead(302, { Location: "/account/login" });
          appContext.ctx.res.end();
      }
    }
    return { ...appProps, currentUser };
  };

2. 在 componentDidMount 中重定向(在禁用 SSR 时有用,例如在静态模式下)

这是客户端渲染的后备。

   componentDidMount() {
    const { currentUser, router } = this.props;
    if (!currentUser && !isPublicRoute(router.pathname)) {
      Router.push("/account/login");
    }
  }

我无法避免在静态模式下闪烁初始页面添加这一点,因为您无法在静态构建期间重定向,但它似乎比通常的方法更好。随着我的进步,我会尝试编辑。

完整的例子在这里

相关问题,遗憾的是最终只有客户回答

我打开的关于重定向的新问题

原文由 Eric Burel 发布,翻译遵循 CC BY-SA 4.0 许可协议

更新:Next.js >= 12.1

正如 @warfield 在 next.js >= 12.1 的 回答 中指出的那样,重定向中不再允许使用相对 URL,使用它们会引发错误。我在这里重新发布他的答案以获得更多可见性:

使用 Next.js >= 12.1 的 中间件 进行重定向:

  1. 在与您的 pages 目录相同的级别创建一个 middleware.ts (或 .js)文件
  2. 导出一个 middleware 函数
  3. 创建一个 绝对 URL 并将其传递给 redirect

TypeScript 示例 middleware.ts

>
> import { NextResponse } from 'next/server'
> import type { NextRequest } from 'next/server'
>
> export function middleware(request: NextRequest) {
>   const url = request.nextUrl.clone()
>   if (url.pathname === '/') {
>     url.pathname = '/hello-nextjs'
>     return NextResponse.redirect(url)
>   }
> }
>
> ```

**更新:Next.js >= 12**

现在您可以使用 [中间件](https://nextjs.org/docs/advanced-features/middleware) 进行重定向,在页面文件夹(或页面内的任何子文件夹)中创建一个 `_middleware.js` 文件

import { NextResponse, NextRequest } from ‘next/server’ export async function middleware(req, ev) { const { pathname } = req.nextUrl if (pathname == ‘/’) { return NextResponse.redirect(‘/hello-nextjs’) } return NextResponse.next() }


**更新:Next.js >= 10**

从 Next.js 10 开始,您可以使用 `redirect` 中的密钥进行 **服务器端重定向**(客户端重定向见下文) `getServerSideProps` 或 `getStaticProps` :

export async function getServerSideProps(context) { const res = await fetch(https://.../data) const data = await res.json() // or use context.resolvedUrl for conditional redirect // if(context.resolvedUrl == “/”) if (!data) { return { redirect: { destination: ‘/hello-nextjs’, permanent: false, }, } }

return { props: {}, // will be passed to the page component as props } }


**注意**:使用 `getServerSideProps` 将强制应用程序进入 SSR,也不支持在构建时重定向,如果重定向在构建时已知,您可以在 [next.config.js](https://nextjs.org/docs/messages/middleware-relative-urls) 中添加它们

在 `next.js` 你可以 **在页面加载后** 重定向使用 `Router` 例如:

import Router from ‘next/router’

componentDidMount(){ const {pathname} = Router if(pathname == ‘/’ ){ Router.push(‘/hello-nextjs’) } }


或者用钩子:

import React, { useEffect } from “react”; import Router from ‘next/router’

… useEffect(() => { const {pathname} = Router if(pathname == ‘/’ ){ Router.push(‘/hello-nextjs’) } });


如果你想防止在重定向之前闪烁,你可以使用一个简单的技巧:

import React, { useEffect,useState } from “react”; import Router from ‘next/router’ const myPage = ()=>{ const [loaded,setLoaded] = useState(false) useEffect(() => { const {pathname} = Router // conditional redirect if(pathname == ‘/’ ){ // with router.push the page may be added to history // the browser on history back will go back to this page and then forward again to the redirected page // you can prevent this behaviour using location.replace Router.push(‘/hello-nextjs’) //location.replace(“/hello-nextjs”) }else{ setLoaded(true) } },[]);

if(!loaded){
    return <div></div> //show nothing or a loader
}
return (
    <p>
        You will see this page only if pathname !== "/" , <br/>
    </p>
)

} export default myPage

”`

我会说,当您可以使用 next.config.js 重定向或什至更好地使用组件的条件渲染时,通常不是进行客户端重定向的好/优雅方法。

我在 这里 创建了一个包含上面所有示例的简单存储库。

原文由 Nico 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题