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

时间:2019-09-30 19:01:27

标签: html reactjs routing react-router next.js

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

反应路由器中,我们执行以下操作:

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

11 个答案:

答案 0 :(得分:18)

下一个9.4个答案

嗨,这是在所有情况下都可以使用的示例组件:

Vulcan next starter withPrivate access

Example usage here

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

以下每种情况都需要特定的模式:

  • 服务器端呈现:如果允许,我们呈现页面,否则呈现HTTP重定向
  • 静态渲染(服务器端):我们什么也不渲染,但是我们仍将页面包含在构建版本中
  • 在静态导出后,
  • 客户端渲染:我们检查客户端是否为auth,以及是否重定向。在此检查或重定向过程中,我们什么也不显示(或显示加载程序)。
  • 使用next / router重定向客户端后的客户端呈现:相同行为。
  • Sli之后的
  • 客户端渲染:我们使用getInitialProps传递的prop来直接在第一次渲染时告诉用户是否被允许。只是快了一点,避免了空白的闪光。

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

下一个9.5更新

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

旧答案(可以,但是会有一个凌乱的静态渲染)

半官方示例

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调用实际上是获得auth令牌的调用,您可能希望将其封装到一个单独的函数中。

我会建议的内容

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

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

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");
    }
  }

我无法避免在静态模式下刷新初始页面,请添加这一点,因为您无法在静态构建过程中进行重定向,但它似乎比通常的方法要好。我会在取得进展时尝试进行编辑。

Full example is here

Relevant issue, which sadly ends up with a client only answer

New issue I've opened regarding redirecton

答案 1 :(得分:8)

有三种方法。

1。重定向事件或功能

import Router from 'next/router';

<button type="button" onClick={() => Router.push('/myroute')} />

2。使用挂钩重定向:

import Router , {useRouter}  from 'next/router';

const router = useRouter()

<button type="button" onClick={() => router.push('/myroute')} />

3。使用链接重定向:

基于Nextjs文档的链接中,<a>标签是必需的!

import Link from 'next/link';

<Link href="/myroute">
   <a>myroute</a>
</Link>

服务器端路由还有其他一些选项,asPath。在所有描述的方法中,您都可以添加asPath来重定向客户端和服务器端。

答案 2 :(得分:4)

@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挂钩。比类更快,更有效。

答案 3 :(得分:4)

这里有2个复制粘贴级别的示例:一个用于浏览器,一个用于服务器。

https://dev.to/justincy/client-side-and-server-side-redirection-in-next-js-3ile

假设您要从根(/)重定向到名为home:(/ 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

答案 4 :(得分:3)

我已经在我的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;

答案 5 :(得分:2)

NextJS 9.5.0+有效

  1. 创建next.config.js文件
  2. 添加源URL和目标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

答案 6 :(得分:2)

Next.js 10+ 为我们提供了一些额外的优雅解决方案来进行重定向。

  1. SERVER-SIDE - 你应该使用 getServerSideProps

    下面的例子假设我们有一些额外的会话要检查(但可以 任何你想要的)。如果会话为空并且我们在服务器端 (context.res),这意味着用户没有登录,我们应该 重定向到登录页面 (/login).. 另一种方式我们可以通过 sessionprops 并重定向到 /dashboard

    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'
        }
      }
    }
    
    
  2. CLIENT-SIDE - 例如,您可以使用 useRouter 钩子:

    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');
    

更多信息在这里:https://github.com/vercel/next.js/discussions/14890

答案 7 :(得分:2)

在 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

答案 8 :(得分:1)

next.js中,您可以使用Router进行重定向:

import Router from 'next/router'

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

答案 9 :(得分:1)

如果您要确保您的应用程序像SPA一样运行并且想要拦截用户粘贴到地址栏中的传入的无效(或有效)路径名,那么这是一种快速/简便的方法。 / p>

假设您的路径是

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;

答案 10 :(得分:-1)

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}/>
  }
}