Next.js 从/重定向到另一个页面
Next.js Redirect from / to another page
我是 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>
更新:Next.js >= 12
现在您可以使用 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 开始,您可以使用 getServerSideProps
中的 redirect
键执行 服务器端重定向 (客户端重定向见下文)或 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
中添加那些
在 next.js
中,您可以在页面加载后 重定向 使用 Router
例如:
import Router from 'next/router'
componentDidMount(){
const {pathname} = Router
if(pathname == '/' ){
Router.push('/hello-nextjs')
}
}
或使用 Hooks :
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
重定向甚至更好地使用组件的条件渲染时,通常不是 good/elegant 进行客户端重定向的方法。
我已经用上面的所有例子创建了一个简单的回购 here。
共有三种方法。
1.Redirect 事件或函数:
import Router from 'next/router';
<button type="button" onClick={() => Router.push('/myroute')} />
2.Redirect 带钩子:
import Router , {useRouter} from 'next/router';
const router = useRouter()
<button type="button" onClick={() => router.push('/myroute')} />
3.Redirect 与 Link:
基于 Nextjs 文档,<a>
标签在 link 中是必需的,例如在新标签页中打开!
import Link from 'next/link';
<Link href="/myroute">
<a>myroute</a>
</Link>
服务器端路由还有一些其他选项 asPath
。在所有描述的方法中,您可以添加 asPath 以重定向客户端和服务器端。
redirect-to.ts
import Router from "next/router";
export default function redirectTo(
destination: any,
{ res, status }: any = {}
): void {
if (res) {
res.writeHead(status || 302, { Location: destination });
res.end();
} else if (destination[0] === "/" && destination[1] !== "/") {
Router.push(destination);
} else {
window.location = destination;
}
}
_app.tsx
import App, {AppContext} from 'next/app'
import Router from "next/router"
import React from 'react'
import redirectTo from "../utils/redirect-to"
export default class MyApp extends App {
public static async getInitialProps({Component, ctx}: AppContext): Promise<{pageProps: {}}> {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (ctx.pathname === "" || ctx.pathname === "/_error") {
redirectTo("/hello-next-js", { res: ctx.res, status: 301 }); <== Redirect-To
return {pageProps};
}
return {pageProps};
}
render() {
const {Component, pageProps} = this.props;
return <Component {...pageProps}/>
}
}
@Nico 的回答解决了您使用 类.
时的问题
如果您正在使用函数,则不能使用 componentDidMount
。相反,您可以使用 React Hooks useEffect
.
import React, {useEffect} from 'react';
export default function App() {
const classes = useStyles();
useEffect(() => {
const {pathname} = Router
if(pathname == '/' ){
Router.push('/templates/mainpage1')
}
}
, []);
return (
null
)
}
2019 年 React introduced 钩子。比 类.
更快更高效
警告
首先,您应该评估是否需要 client-side 重定向(在 React 中),server-side 重定向(301 HTTP 响应)或 server-side 重定向 + 身份验证( 301 HTTP 响应但也有一些逻辑来检查身份验证).
这是我能写的最完整的答案。但是,在大多数情况下,您不需要任何这些。就像在任何 React 应用程序中所做的那样重定向。 首选 client-side 重定向。 只需使用 useEffect
+ router.push
,仅此而已。
Server-side 重定向很诱人,特别是当您想要“保护”私人页面时,但您应该评估您是否真的需要它们。通常,你不会。它们会带来意想不到的复杂性,例如管理身份验证令牌和刷新令牌。相反,您可能希望向您的体系结构添加一个网关服务器、一个反向代理或任何前端服务器来处理这些类型的检查。
请记住,Next.js 只是 React 应用程序,使用 Next.js 高级功能(如 SSR)的代价应在您的上下文中得到证明。
旧的下一个 9.4 答案
您好,这是一个适用于所有场景的示例组件:
Vulcan next starter withPrivate access
答案很庞大,如果我以某种方式违反了 SO 规则,我深表歉意,但我不想粘贴 180 行代码。如果您想同时支持 SSR 和静态导出,那么在 Next 中没有简单的模式来处理重定向。
以下场景各需要一个特定的模式:
- 服务器端呈现:如果允许我们呈现页面,如果不允许则 HTTP 重定向
- 静态渲染(server-side):我们不渲染任何东西,但我们仍然将页面包含在构建中
- 客户端呈现,在静态导出之后:我们检查客户端是否已授权用户,以及是否重定向。在此检查期间或如果我们正在重定向,我们不显示任何内容(或加载程序)。
- 使用 next/router 进行客户端重定向后的客户端呈现:相同的行为。
- SSR 后的客户端渲染:我们使用getInitialProps 传递的props 来判断用户是否被允许,直接在第一次渲染时。只是快了一点,避免了空白闪现。
在写的时候(Next 9.4),你必须使用getInitialProps
,而不是getServerSideProps
,否则你就失去了做next export
的能力。
下一个 9.5 更新
正如@Arthur 在评论中所述,9.5 还包括设置 redirects in next.config.js 的可能性。
我还不清楚此功能的局限性,但它们似乎是全局重定向,例如当您需要移动页面或只允许在有限的时间内访问时。
因此,它们不打算处理例如身份验证,因为它们似乎无权访问请求上下文。再次确认。
下 10 个新文档更新
此解决方案特定于基于身份验证的重定向。
Authentication patterns are now documented
我不喜欢从 getServerSideProps
进行身份验证,因为在我看来已经太晚了,而且很难设置处理刷新令牌等高级模式。但这是官方的解决方案。
您可能还想根据 Vercel 仪表板的工作方式(在撰写本文时)检查 in this ticket 记录的方法,该方法可防止未经身份验证的内容闪现
下一个 10.2 header 和基于 cookie 的重写更新
下一个 10.2 引入了 Rewrites 基于 header 和 cookie。
这是根据身份验证 cookie 或 header.
重定向 server-side 的好方法
但是,请记住,这不是 安全 重定向。 用户可以更改他们的请求 header一个虚假的令牌。您仍然需要网关、反向代理或前置服务器来实际检查令牌有效性并正确设置 headers.
编辑:注意 URL 不会改变。重写将 URL 指向应用程序的现有页面,而不更改 URL => 它允许您拥有“虚拟”URLs。
示例用例:假设您有一个已翻译的页面 src/contact.tsx
,并设置了国际化重定向。您可以通过将 /de/kontact
重写为 /de/contact
.
来翻译页面名称本身(“联系人”)
下一个 12 更新
现在 middlewares 在 server-side 重定向上给你 full-control。
但是,请再次记住,大多数时候 client-side 重定向和检查就足够了。
旧答案(有效,但会出现混乱的静态渲染)
Semi-official 例子
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. 在 server-side 渲染时重定向(aSSR 期间 oid 闪烁)
这是最常见的情况。你想在此时重定向以避免初始页面在第一次加载时闪烁。
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");
}
}
我无法避免在静态模式下闪烁初始页面添加这一点,因为你不能在静态构建期间重定向,但它似乎比通常的方法更好。我会在取得进展时尝试编辑。
Relevant issue, which sadly ends up with a client only answer
我已经在我的 Next.JS
应用程序中实现了这个功能,方法是定义一个根页面,这会重定向服务器端和客户端。这是根页面的代码:
import { useEffect } from "react";
import Router from "next/router";
const redirectTo = "/hello-nextjs";
const RootPage = () => {
useEffect(() => Router.push(redirectTo));
return null;
};
RootPage.getInitialProps = (ctx) => {
if (ctx.req) {
ctx.res.writeHead(302, { Location: redirectTo });
ctx.res.end();
}
};
export default RootPage;
这里有 2 个 copy-paste-level 示例:一个用于浏览器,一个用于服务器。
https://dev.to/justincy/client-side-and-server-side-redirection-in-next-js-3ile
假设您想从根目录 (/) 重定向到名为主页的页面:(/home)
在您的主索引文件中,粘贴以下内容:
客户端
import { useRouter } from 'next/router'
function RedirectPage() {
const router = useRouter()
// Make sure we're in the browser
if (typeof window !== 'undefined') {
router.push('/home')
}
}
export default RedirectPage
服务器端
import { useRouter } from 'next/router'
function RedirectPage({ ctx }) {
const router = useRouter()
// Make sure we're in the browser
if (typeof window !== 'undefined') {
router.push('/home');
return;
}
}
RedirectPage.getInitialProps = ctx => {
// We check for ctx.res to make sure we're on the server.
if (ctx.res) {
ctx.res.writeHead(302, { Location: '/home' });
ctx.res.end();
}
return { };
}
export default RedirectPage
如果您的目的是确保您的应用程序 运行 像 SPA 并且想要拦截用户粘贴到地址栏中的传入无效(或有效)路径名,那么这里有一个 fast/hacky 这样做的方法。
假设你的路径是,
enum ERoutes {
HOME = '/',
ABOUT = '/about',
CONTACT = '/contact'
}
如果您还没有自定义 _error
页面,请将其添加到其中:
import React from 'react';
import { NextPage } from 'next';
import { useDispatch } from 'react-redux';
import { useRouter } from 'next/router';
const Error: NextPage = () => {
const { asPath, push } = useRouter();
const dispatch = useDispatch();
React.useEffect(() => {
const routeValid = Object.values(ERoutes).includes(asPath);
if (routeValid) {
// do some stuff, such as assigning redux state to then render SPA content in your index page
} else {
// you can either continue to render this _error component, or redirect to your index page,
// where you may have your own error component that is displayed based on your app state.
// In my case, I always redirect to '/' (as you can see below, where I push('/'), but before doing so,
// I dispatch relevant redux actions based on the situation
}
// I redirect to root always, but you can redirect only if routeValid === true
push('/');
}, []);
return (
<div>Error because '{asPath}' does not exist</div>
);
};
export default Error;
适用于NextJS 9.5.0+
- 创建
next.config.js
文件
- 添加源和目标url(如果是外域可以设置永久重定向)
module.exports = {
async redirects() {
return [
{
source: '/team',
destination: '/about',
permanent: false,
},
{
source: "/blog",
destination:
"https://blog.dundermifflin.com",
permanent: true,
},
];
},
};
https://github.com/vercel/next.js/tree/canary/examples/redirects
Next.js 10+ 为我们提供了一些额外且优雅的重定向解决方案。
服务器端 - 你应该使用getServerSideProps
下面的例子假设我们有一些额外的会话要检查(但可以是
任何你想要的)。如果会话为空并且我们在服务器端
(context.res
),这意味着用户没有登录,我们应该
重定向到登录页面 (/login
).. 我们可以通过另一种方式传递 session
到 props
并重定向到 /dashboard
:
import { getSession } from 'next-auth/client';
export const getServerSideProps = async (context) => {
const session = await getSession(context);
if(context.res && !session) {
return {
redirect: {
permanent: false,
destination: '/login'
}
}
}
return {
props: { session },
redirect: {
permanent: false,
destination: '/dashboard'
}
}
}
客户端 - 你可以使用例如useRouter
hook:
import { useRouter } from 'next/router';
import { useSession } from 'next-auth/client';
const router = useRouter();
const [ session, loading ] = useSession();
if (typeof window !== 'undefined' && loading) return null;
if (typeof window !== 'undefined' && !session) {
router.push('/login');
}
router.push('/dashboard');
在 NextJs v9.5 及更高版本中,您可以在 next.config.js 文件中配置重定向和重写。
但是,如果您使用 trailingSlash: true
,请确保源路径以斜杠结尾以便正确匹配。
module.exports = {
trailingSlash: true,
async redirects() {
return [
{
source: '/old/:slug/', // Notice the slash at the end
destination: '/new/:slug',
permanent: false,
},
]
},
}
您还需要考虑其他可能影响路由的插件和配置,例如 next-images。
文档:https://nextjs.org/docs/api-reference/next.config.js/redirects
这是避免 URLs is malformed. Please use only absolute URLs
错误的中间件解决方案。
此外,使用路径对象可能是处理重定向的更简洁的方法。
// pages/_middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(req: NextRequest) {
const { pathname, origin } = req.nextUrl;
const paths: { [key: string]: string } = {
'/admin/appearance': `${origin}/admin/appearance/theme`,
};
const rePath = paths[pathname];
if (rePath) return NextResponse.redirect(rePath);
else return NextResponse.next();
}
♂️ useEffect
将重定向但立即跳回当前页面
✅ useLayoutEffect
很有魅力:
const router = useRouter();
useLayoutEffect(() => {
router.isFallback && router.replace("/course");
}, [router]);
ℹ️ 我对 useEffect 使用了上面相同的代码。
♂️如果有人能解释为什么,先谢谢了!
我是 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>
更新:Next.js >= 12
现在您可以使用 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 开始,您可以使用 getServerSideProps
中的 redirect
键执行 服务器端重定向 (客户端重定向见下文)或 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
在 next.js
中,您可以在页面加载后 重定向 使用 Router
例如:
import Router from 'next/router'
componentDidMount(){
const {pathname} = Router
if(pathname == '/' ){
Router.push('/hello-nextjs')
}
}
或使用 Hooks :
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
重定向甚至更好地使用组件的条件渲染时,通常不是 good/elegant 进行客户端重定向的方法。
我已经用上面的所有例子创建了一个简单的回购 here。
共有三种方法。
1.Redirect 事件或函数:
import Router from 'next/router';
<button type="button" onClick={() => Router.push('/myroute')} />
2.Redirect 带钩子:
import Router , {useRouter} from 'next/router';
const router = useRouter()
<button type="button" onClick={() => router.push('/myroute')} />
3.Redirect 与 Link:
基于 Nextjs 文档,<a>
标签在 link 中是必需的,例如在新标签页中打开!
import Link from 'next/link';
<Link href="/myroute">
<a>myroute</a>
</Link>
服务器端路由还有一些其他选项 asPath
。在所有描述的方法中,您可以添加 asPath 以重定向客户端和服务器端。
redirect-to.ts
import Router from "next/router";
export default function redirectTo(
destination: any,
{ res, status }: any = {}
): void {
if (res) {
res.writeHead(status || 302, { Location: destination });
res.end();
} else if (destination[0] === "/" && destination[1] !== "/") {
Router.push(destination);
} else {
window.location = destination;
}
}
_app.tsx
import App, {AppContext} from 'next/app'
import Router from "next/router"
import React from 'react'
import redirectTo from "../utils/redirect-to"
export default class MyApp extends App {
public static async getInitialProps({Component, ctx}: AppContext): Promise<{pageProps: {}}> {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (ctx.pathname === "" || ctx.pathname === "/_error") {
redirectTo("/hello-next-js", { res: ctx.res, status: 301 }); <== Redirect-To
return {pageProps};
}
return {pageProps};
}
render() {
const {Component, pageProps} = this.props;
return <Component {...pageProps}/>
}
}
@Nico 的回答解决了您使用 类.
时的问题如果您正在使用函数,则不能使用 componentDidMount
。相反,您可以使用 React Hooks useEffect
.
import React, {useEffect} from 'react';
export default function App() {
const classes = useStyles();
useEffect(() => {
const {pathname} = Router
if(pathname == '/' ){
Router.push('/templates/mainpage1')
}
}
, []);
return (
null
)
}
2019 年 React introduced 钩子。比 类.
更快更高效警告
首先,您应该评估是否需要 client-side 重定向(在 React 中),server-side 重定向(301 HTTP 响应)或 server-side 重定向 + 身份验证( 301 HTTP 响应但也有一些逻辑来检查身份验证).
这是我能写的最完整的答案。但是,在大多数情况下,您不需要任何这些。就像在任何 React 应用程序中所做的那样重定向。 首选 client-side 重定向。 只需使用 useEffect
+ router.push
,仅此而已。
Server-side 重定向很诱人,特别是当您想要“保护”私人页面时,但您应该评估您是否真的需要它们。通常,你不会。它们会带来意想不到的复杂性,例如管理身份验证令牌和刷新令牌。相反,您可能希望向您的体系结构添加一个网关服务器、一个反向代理或任何前端服务器来处理这些类型的检查。
请记住,Next.js 只是 React 应用程序,使用 Next.js 高级功能(如 SSR)的代价应在您的上下文中得到证明。
旧的下一个 9.4 答案
您好,这是一个适用于所有场景的示例组件:
Vulcan next starter withPrivate access
答案很庞大,如果我以某种方式违反了 SO 规则,我深表歉意,但我不想粘贴 180 行代码。如果您想同时支持 SSR 和静态导出,那么在 Next 中没有简单的模式来处理重定向。
以下场景各需要一个特定的模式:
- 服务器端呈现:如果允许我们呈现页面,如果不允许则 HTTP 重定向
- 静态渲染(server-side):我们不渲染任何东西,但我们仍然将页面包含在构建中
- 客户端呈现,在静态导出之后:我们检查客户端是否已授权用户,以及是否重定向。在此检查期间或如果我们正在重定向,我们不显示任何内容(或加载程序)。
- 使用 next/router 进行客户端重定向后的客户端呈现:相同的行为。
- SSR 后的客户端渲染:我们使用getInitialProps 传递的props 来判断用户是否被允许,直接在第一次渲染时。只是快了一点,避免了空白闪现。
在写的时候(Next 9.4),你必须使用getInitialProps
,而不是getServerSideProps
,否则你就失去了做next export
的能力。
下一个 9.5 更新
正如@Arthur 在评论中所述,9.5 还包括设置 redirects in next.config.js 的可能性。 我还不清楚此功能的局限性,但它们似乎是全局重定向,例如当您需要移动页面或只允许在有限的时间内访问时。 因此,它们不打算处理例如身份验证,因为它们似乎无权访问请求上下文。再次确认。
下 10 个新文档更新
此解决方案特定于基于身份验证的重定向。
Authentication patterns are now documented
我不喜欢从 getServerSideProps
进行身份验证,因为在我看来已经太晚了,而且很难设置处理刷新令牌等高级模式。但这是官方的解决方案。
您可能还想根据 Vercel 仪表板的工作方式(在撰写本文时)检查 in this ticket 记录的方法,该方法可防止未经身份验证的内容闪现
下一个 10.2 header 和基于 cookie 的重写更新
下一个 10.2 引入了 Rewrites 基于 header 和 cookie。 这是根据身份验证 cookie 或 header.
重定向 server-side 的好方法但是,请记住,这不是 安全 重定向。 用户可以更改他们的请求 header一个虚假的令牌。您仍然需要网关、反向代理或前置服务器来实际检查令牌有效性并正确设置 headers.
编辑:注意 URL 不会改变。重写将 URL 指向应用程序的现有页面,而不更改 URL => 它允许您拥有“虚拟”URLs。
示例用例:假设您有一个已翻译的页面 src/contact.tsx
,并设置了国际化重定向。您可以通过将 /de/kontact
重写为 /de/contact
.
下一个 12 更新
现在 middlewares 在 server-side 重定向上给你 full-control。
但是,请再次记住,大多数时候 client-side 重定向和检查就足够了。
旧答案(有效,但会出现混乱的静态渲染)
Semi-official 例子
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. 在 server-side 渲染时重定向(aSSR 期间 oid 闪烁)
这是最常见的情况。你想在此时重定向以避免初始页面在第一次加载时闪烁。
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");
}
}
我无法避免在静态模式下闪烁初始页面添加这一点,因为你不能在静态构建期间重定向,但它似乎比通常的方法更好。我会在取得进展时尝试编辑。
Relevant issue, which sadly ends up with a client only answer
我已经在我的 Next.JS
应用程序中实现了这个功能,方法是定义一个根页面,这会重定向服务器端和客户端。这是根页面的代码:
import { useEffect } from "react";
import Router from "next/router";
const redirectTo = "/hello-nextjs";
const RootPage = () => {
useEffect(() => Router.push(redirectTo));
return null;
};
RootPage.getInitialProps = (ctx) => {
if (ctx.req) {
ctx.res.writeHead(302, { Location: redirectTo });
ctx.res.end();
}
};
export default RootPage;
这里有 2 个 copy-paste-level 示例:一个用于浏览器,一个用于服务器。
https://dev.to/justincy/client-side-and-server-side-redirection-in-next-js-3ile
假设您想从根目录 (/) 重定向到名为主页的页面:(/home)
在您的主索引文件中,粘贴以下内容:
客户端
import { useRouter } from 'next/router'
function RedirectPage() {
const router = useRouter()
// Make sure we're in the browser
if (typeof window !== 'undefined') {
router.push('/home')
}
}
export default RedirectPage
服务器端
import { useRouter } from 'next/router'
function RedirectPage({ ctx }) {
const router = useRouter()
// Make sure we're in the browser
if (typeof window !== 'undefined') {
router.push('/home');
return;
}
}
RedirectPage.getInitialProps = ctx => {
// We check for ctx.res to make sure we're on the server.
if (ctx.res) {
ctx.res.writeHead(302, { Location: '/home' });
ctx.res.end();
}
return { };
}
export default RedirectPage
如果您的目的是确保您的应用程序 运行 像 SPA 并且想要拦截用户粘贴到地址栏中的传入无效(或有效)路径名,那么这里有一个 fast/hacky 这样做的方法。
假设你的路径是,
enum ERoutes {
HOME = '/',
ABOUT = '/about',
CONTACT = '/contact'
}
如果您还没有自定义 _error
页面,请将其添加到其中:
import React from 'react';
import { NextPage } from 'next';
import { useDispatch } from 'react-redux';
import { useRouter } from 'next/router';
const Error: NextPage = () => {
const { asPath, push } = useRouter();
const dispatch = useDispatch();
React.useEffect(() => {
const routeValid = Object.values(ERoutes).includes(asPath);
if (routeValid) {
// do some stuff, such as assigning redux state to then render SPA content in your index page
} else {
// you can either continue to render this _error component, or redirect to your index page,
// where you may have your own error component that is displayed based on your app state.
// In my case, I always redirect to '/' (as you can see below, where I push('/'), but before doing so,
// I dispatch relevant redux actions based on the situation
}
// I redirect to root always, but you can redirect only if routeValid === true
push('/');
}, []);
return (
<div>Error because '{asPath}' does not exist</div>
);
};
export default Error;
适用于NextJS 9.5.0+
- 创建
next.config.js
文件 - 添加源和目标url(如果是外域可以设置永久重定向)
module.exports = {
async redirects() {
return [
{
source: '/team',
destination: '/about',
permanent: false,
},
{
source: "/blog",
destination:
"https://blog.dundermifflin.com",
permanent: true,
},
];
},
};
https://github.com/vercel/next.js/tree/canary/examples/redirects
Next.js 10+ 为我们提供了一些额外且优雅的重定向解决方案。
服务器端 - 你应该使用
getServerSideProps
下面的例子假设我们有一些额外的会话要检查(但可以是 任何你想要的)。如果会话为空并且我们在服务器端 (
context.res
),这意味着用户没有登录,我们应该 重定向到登录页面 (/login
).. 我们可以通过另一种方式传递session
到props
并重定向到/dashboard
:import { getSession } from 'next-auth/client'; export const getServerSideProps = async (context) => { const session = await getSession(context); if(context.res && !session) { return { redirect: { permanent: false, destination: '/login' } } } return { props: { session }, redirect: { permanent: false, destination: '/dashboard' } } }
客户端 - 你可以使用例如
useRouter
hook:import { useRouter } from 'next/router'; import { useSession } from 'next-auth/client'; const router = useRouter(); const [ session, loading ] = useSession(); if (typeof window !== 'undefined' && loading) return null; if (typeof window !== 'undefined' && !session) { router.push('/login'); } router.push('/dashboard');
在 NextJs v9.5 及更高版本中,您可以在 next.config.js 文件中配置重定向和重写。
但是,如果您使用 trailingSlash: true
,请确保源路径以斜杠结尾以便正确匹配。
module.exports = {
trailingSlash: true,
async redirects() {
return [
{
source: '/old/:slug/', // Notice the slash at the end
destination: '/new/:slug',
permanent: false,
},
]
},
}
您还需要考虑其他可能影响路由的插件和配置,例如 next-images。
文档:https://nextjs.org/docs/api-reference/next.config.js/redirects
这是避免 URLs is malformed. Please use only absolute URLs
错误的中间件解决方案。
此外,使用路径对象可能是处理重定向的更简洁的方法。
// pages/_middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(req: NextRequest) {
const { pathname, origin } = req.nextUrl;
const paths: { [key: string]: string } = {
'/admin/appearance': `${origin}/admin/appearance/theme`,
};
const rePath = paths[pathname];
if (rePath) return NextResponse.redirect(rePath);
else return NextResponse.next();
}
♂️ useEffect
将重定向但立即跳回当前页面
✅ useLayoutEffect
很有魅力:
const router = useRouter();
useLayoutEffect(() => {
router.isFallback && router.replace("/course");
}, [router]);
ℹ️ 我对 useEffect 使用了上面相同的代码。
♂️如果有人能解释为什么,先谢谢了!