Next的路由处理

Next.js是如何实现路由的?

1.文件系统路由

Next.js采用文件系统路由,pages目录下的每个React组件文件都代表一个路由。

例如:

  • pages/index.js -> /
  • pages/a.js -> /a
  • pages/b/index.js->/b
  • pages/b/c.js->/b/c

文件路由的处理方式比较直观和文件结构相挂钩,这样处理开发者可以通过文件结构了解路由结构,提高代码的可维护性和易读性。

一些组件我们并不期望被访问到,需要做好做好权限处理。

当我们需要频繁修改路由时,就要求我们频繁修改文件结构,这点可能并不友好。

2.动态路由

Next.js 支持通过文件名 [param].js 创建动态路由。例如,要创建一个可以匹配任何 ID 的动态页面,你可以创建 pages/[id].js

javascript 复制代码
import React from 'react';  
import { useRouter } from 'next/router';  
  
export default function Post() {  
  const router = useRouter();  
  const { id } = router.query;  
  
  return <div>Post ID: {id}</div>;  
}

访问http://localhost:3001/any-id

但是如果我们访问pages/a,还是会访问a.js,而非得到Post ID:a。由此可以得出结论,文件系统路由的优先级在动态路由之上。

Next如何进行路由导航?

1.使用next/link组件

next/link 组件允许你在 Next.js 应用中进行客户端路由导航,而不需要刷新页面,下面我们创建一个Nav组件,并且在index中引用它。

javascript 复制代码
import React from 'react';  
import Link from 'next/link';  
  
export default () => {  
  return (  
    <div>  
      <Link href="/">  
        Home
      </Link>
      <br/>  
      <Link href="/a">  
        a
      </Link>  
      <br/>
      <Link href="/b/c">  
        b/c
      </Link>  
    </div>  
  );  
}
javascript 复制代码
import React from 'react';
import Nav from '../components/Nav/nav.jsx';
​
export default () => {
    return <>
        <Nav />
    </>
}

点击即可实现跳转

2.使用useRouterHook

useRouter Hook 可以在任何组件中访问路由相关的对象和方法,例如查询参数、路径名,以及进行编程式导航:

javascript 复制代码
import { useRouter } from 'next/router';
import React from 'react';  
​
export default () => {  
  const router = useRouter();
  const goTo = (path) => {
    router.push(`/${path}`);
  }
  return <>
      <span onClick={() => { goTo('a') }}>a</span>
      <br/>
      <span onClick={() => { goTo('b/c') }}>b/c</span>
      <br/>
      <span onClick={() => { goTo('b') }}>b</span>
  </>
}

Next如何使用路由守卫?

前面展示的路由访问例子,只要是文件系统下的组件,都可以直接通过路径访问到。但是有些时候我们希望拦截住一些路由进行重定向,或者放过一些路由。

比如,未登陆的用户,在访问index时,我们希望将之拦截,重定向到login界面。

这里我们建立login页面。

javascript 复制代码
import React from 'react';
export default () => {
    return <>login Page</>;
}

1.使用getServerSideProps在服务端重定向

我们先介绍一下getServerSideProps这个next.js提供的API:

getServerSideProps 是 Next.js 提供的一个 API,用于在服务器端渲染页面之前获取数据。这个 API 允许你在每次请求页面时从服务器端获取最新的数据,并将这些数据传递给页面组件。

参数

getServerSideProps 接收一个上下文对象(context)作为参数,该对象包含以下属性:

  1. params : 一个包含动态路由参数的对象。如果页面使用了动态路由(例如 [id].js),则这些参数将包含在 params 中。
  2. req : Next.js 的 req 对象,它代表 HTTP 请求。你可以通过这个对象访问请求头、查询参数、cookies 等。
  3. res : Next.js 的 res 对象,它代表 HTTP 响应。虽然你通常不需要直接操作这个对象(因为 Next.js 会自动处理响应),但在某些情况下,你可能需要用它来设置 cookies 或响应头。
  4. query: 一个包含 URL 查询参数的对象。这允许你轻松访问 URL 中的查询字符串参数。
  5. resolvedUrl: 一个字符串,表示页面的完整 URL(包括查询参数)。

返回值

getServerSideProps 应该返回一个对象,该对象可以包含以下属性:

  1. props: 一个对象,包含你想要传递给页面组件的属性(数据)。这些属性将在服务器端渲染时被注入到页面组件中。
  2. notFound : 一个布尔值,表示页面是否应该返回 404 状态码。如果设置为 true,Next.js 将自动渲染 404 页面。
  3. redirect : 一个对象,用于执行服务器端重定向。这个对象应该包含两个属性:destination(重定向的目标 URL)和 permanent(一个布尔值,表示重定向是否是永久性的,默认为 false)。

所以getServerSideProps 可以在页面组件加载之前运行服务器端的逻辑,根据条件进行重定向。

由于我们没有登陆cookies,所以每次访问index的时候都会被重定向到login页面。

javascript 复制代码
// pages/index.js  
import React from 'react';
import Nav from '../components/Nav/nav.jsx';
  
export const getServerSideProps = async ({ req, res }) => {  
  // 检查用户是否登录,例如通过 cookies 或 headers  
  const isAuthenticated = req.cookies.authToken; // 只是一个示例  
  
  if (!isAuthenticated) {  
    // 重定向到登录页面  
    return {  
      redirect: {  
        destination: '/login',  
        permanent: false, // 通常设置为 false,除非你想进行永久重定向  
      },  
    };  
  }  
  
  return { props: {} };  
}; 
​
export default () => {
    return <Nav/>
}

2.使用 useEffectrouter 在客户端进行重定向

在客户端,可以使用 React 的 useEffect 钩子和 Next.js 提供的 useRouter 钩子来实现路由守卫。

首先,我们创建一个名为useAuth的权限hooks

javascript 复制代码
// hooks/useAuth.js
export const useAuth = () => {
    return {
        isLogin: false, // 这里暂时默认未登陆
    }
}

创建一个protect页面,当我们访问protect页面时,就会被重定向到login页面。当isLogin被改为true时,就正常渲染。

javascript 复制代码
// pages/protect.js  
import { useEffect } from 'react';  
import { useRouter } from 'next/router';  
import { useAuth } from '../hooks/useAuth'; // 假设你有一个自定义的 auth hook  
  
const ProtectPage = () => {  
  const router = useRouter();  
  const { isLogin } = useAuth(); // 假设 useAuth 返回一个 isLogin 状态  
  
  useEffect(() => {  
    if (!isLogin) {  
      // 如果用户未认证,重定向到登录页面  
      router.push('/login');  
    }  
  }, [isLogin, router]);  
  
  return <div>This is a protect page.</div>;  
};  
  
export default ProtectPage;

3.创建一个高阶组件(HOC)

实际上,如果每个需要权限认证的页面都写一遍重定向逻辑是很麻烦的。这里我们就可以通过HOC简化统一这个逻辑。

这里我们创建一个HOC,用于检查是否有权限。

javascript 复制代码
// hoc/withAuthRouter.jsx
import { useEffect } from 'react';  
import { useRouter } from 'next/router';  
import { useAuth } from '../hooks/useAuth'; // 假设你有一个自定义的 auth hook  
  
const withAuthRouter = (WrappedComponent) => {
    return (props) => {
        const router = useRouter();  
        const { isLogin } = useAuth(); // 假设 useAuth 返回一个 isLogin 状态  
        useEffect(() => {  
            if (!isLogin) {  
            // 如果用户未认证,重定向到登录页面  
            router.push('/login');  
            }  
        }, [isLogin, router]);
        return <WrappedComponent {...props}/>
    }
};  
  
export default withAuthRouter;

我们再创建一个protectB页面,包裹上HOC。

javascript 复制代码
// pages/protectB.js
import React from 'react';
import withAuthRouter from "../hoc/withAuthRouter";
​
export default withAuthRouter(() => {
    return <>I'm protectB</>;
});

这种方式很适合,少数几个页面有统一的路由逻辑的情况使用。但是HOC最好不要滥用,不然后期代码会难以维护。

4.在入口文件处统一拦截

最后这种方式就是将拦截处理统一在入口文件_app.js当中,在next.js的页面访问逻辑当中,会先访问_app.js再访问index.js

_app.js是应用级的,而index.js是页面级的,所以通常将处理放在_app.js当中。

下面的代码中,我们对不在白名单内的页面进行了重定向处理。

javascript 复制代码
// _app.js
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
​
export default(context) => {
    const { Component, pageProps } = context;
    const router = useRouter();
    useEffect(() => {
        if(!WHITE_LIST.includes(router.pathname)) router.push('login');
    }, []);
    return <Component {...pageProps}/>
};
​
// 白名单,只能访问到以下页面
const WHITE_LIST = [
    '/a',
    '/b/c',
    '/login',
    '/protect',
]

上面这样做的好处在于可以进行全局处理,将路由的操作进行统一处理,代码更易于维护。

但是有些较为局部的处理,也集中到这里的话,会使代码变得臃肿,难以维护。所以还需要根据情况去选择合适的处理方式。

相关推荐
夜斗(dou)3 分钟前
谷歌开发者工具 - 网络篇
前端·网络·chrome devtools
常常不爱学习9 分钟前
CSS盒子模型(溢出隐藏,块级元素和行级元素的居中对齐,元素样式重置)
前端·css
风抽过的烟头10 分钟前
Python提取字符串中的json,时间,特定字符
前端·python·json
SomeB1oody27 分钟前
【Rust自学】6.3. 控制流运算符-match
开发语言·前端·rust
m0_748256781 小时前
【Django自学】Django入门:如何使用django开发一个web项目(非常详细)
前端·django·sqlite
林小白的日常1 小时前
uniapp中wx.getFuzzyLocation报错如何解决
前端·javascript·uni-app
傻小胖1 小时前
React 脚手架配置代理完整指南
前端·react.js·前端框架
EterNity_TiMe_2 小时前
【论文复现】农作物病害分类(Web端实现)
前端·人工智能·python·机器学习·分类·数据挖掘
余生H2 小时前
深入理解HTML页面加载解析和渲染过程(一)
前端·html·渲染
吴敬悦2 小时前
领导:按规范提交代码conventionalcommit
前端·程序员·前端工程化