React 路由守卫:前端安全与控制的魔法钥匙 🗝️

React 路由守卫初体验 🔍

想象一下:你写了个光鲜亮丽的后台管理系统,刚上线就被产品经理一顿吐槽 ------"游客都能直接访问管理员页面?这要是被黑客盯上,我可要背锅了!" 😱

这时候你才猛然想起:对啊!路由跳转的时候压根没做任何限制。用户只要知道 URL,就能随意访问任何页面,这可不太行。

路由守卫就是解决这个问题的 "保安大叔"------ 在用户访问某个页面之前,先拦下来问问:"你有通行证吗?"" 你有这个区域的权限吗?" 确认没问题了,才放你进去。

在 React 项目里,路由守卫几乎是必备功能。无论是防止未登录用户访问个人中心,还是阻止普通用户进入管理员后台,都离不开它。今天咱们就用最接地气的方式,把 React 路由守卫彻底讲明白!

什么是路由守卫 🤔

路由守卫的概念

路由守卫(Route Guards)本质上就是路由跳转的 "安检门" 。当用户从一个页面跳转到另一个页面时,它会先跳出来执行一些检查逻辑,再决定是放行(允许跳转)还是拦截(重定向到其他页面)。

你可以把它理解成小区门口的保安:

  • 外卖小哥想进小区?先登记(验证身份)
  • 装修工人要上楼?得有业主开具的通行证(权限检查)
  • 陌生人硬闯?不好意思请回(拦截并重定向)

核心功能大揭秘

路由守卫能做的事情可不少,主要有这几类:

  • 身份验证:最常用的功能!比如检测用户是否登录,没登录就踢回登录页。就像电影院检票 ------ 没票?先去售票窗口!
  • 权限控制:就算登录了,也不是什么页面都能进。普通用户不能进管理员页面,这就需要权限控制。
  • 数据预加载:有些页面需要先加载数据才能正常显示(比如详情页需要先获取 ID 对应的信息),可以用守卫在跳转前加载好数据。
  • 路由重定向 :比如把访问/的用户自动转到/home,或者把没有权限的用户转到/unauthorized页面。
  • 页面访问控制:比如某些页面只允许特定角色访问,或者在维护期间禁止所有人访问。

有了这些功能,你的应用才能既安全又好用 ------ 不会让游客看到付费内容,也不会让用户在等待数据加载时看到空白页面。

React Router 中的实现方式 🛠️

React Router 本身并没有内置 "路由守卫" 这个 API,但我们可以通过它提供的工具自己实现。常用的有三种方式,咱们一个个来看:

1. 高阶组件(HOC)方式

高阶组件(HOC)就像一个 "包装机器",把组件塞进去,加上守卫逻辑,再吐出来一个新的、带守卫功能的组件。

jsx 复制代码
// PrivateRoute.jsx
import { Navigate, useLocation } from "react-router-dom";

// 这个组件就是我们的"守卫包装机"
const PrivateRoute = ({ children, isAuthenticated }) => {
  // 获取当前访问的位置(路径)
  const location = useLocation();

  // 如果没登录,就重定向到登录页
  if (!isAuthenticated) {
    // 这里的state:{from: location}很重要------记录用户原本想去哪
    // 等登录成功后,就能直接跳回这个页面,体验超棒!
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // 登录了就放行,显示原本要访问的内容
  return children;
};

// 使用方式:把需要保护的组件包起来
<Route
  path="/dashboard"
  element={
    <PrivateRoute isAuthenticated={isLoggedIn}>
      <Dashboard />
    </PrivateRoute>
  }
/>;

这种方式的优点是复用性强 ------ 写一次PrivateRoute,所有需要登录才能访问的页面都能用上。就像买了一个安检门,所有重要房间都能装。

2. 组件内守卫

有时候我们需要在组件内部做守卫检查(比如检查该用户是否有操作这个组件的权限),这时候就可以用组件内守卫。

jsx 复制代码
// Dashboard.jsx
import { useEffect } from "react";
import { useNavigate, useLocation } from "react-router-dom";

const Dashboard = () => {
  const navigate = useNavigate(); // 用于跳转路由
  const location = useLocation(); // 获取当前位置

  // 组件一加载就执行检查(依赖变化时也会执行)
  useEffect(() => {
    // 检查用户是否有权限访问dashboard
    if (!hasPermission("dashboard")) {
      navigate("/unauthorized", { replace: true });
      return; // 没权限就跳走,后面代码不执行
    }

    // 检查用户账号是否激活(比如是否验证邮箱)
    if (!isUserActive()) {
      navigate("/login", {
        state: { from: location }, // 同样记录原本想去的地方
        replace: true,
      });
    }
  }, [navigate, location]); // 依赖变化时重新检查

  // 所有检查通过,才显示内容
  return <div>Dashboard Content</div>;
};

这种方式的特点是针对性强------ 守卫逻辑和组件深度绑定,适合组件专属的检查逻辑。比如 Dashboard 组件需要检查 "仪表盘访问权限",Profile 组件需要检查 "个人信息修改权限",各自的守卫逻辑互不干扰。

3. 路由配置中的守卫

如果你的路由是集中配置的(比如用createBrowserRouter定义所有路由),可以直接在配置里写守卫逻辑。

jsx 复制代码
// router.jsx
import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />, // 布局组件(比如包含导航栏)
    children: [
      {
        path: "dashboard",
        // 直接在这里判断:登录了就显示Dashboard,否则跳登录页
        element: isAuthenticated ? <Dashboard /> : <Navigate to="/login" replace />
      },
      {
        path: "admin",
        // 检查是否有管理员角色:有就显示AdminPanel,否则跳无权限页
        element: hasAdminRole ? <AdminPanel /> : <Navigate to="/unauthorized" replace />
      },
    ],
  },
]);

这种方式的优点是直观------ 所有路由的守卫逻辑都集中在配置里,一眼就能看出哪个路由受什么条件保护。适合简单的守卫逻辑,如果逻辑复杂了,会让路由配置变得臃肿。

三种方式各有优缺点,实际开发中可以根据需求选择:简单的用配置守卫,通用的用 HOC 守卫,组件专属的用组件内守卫。

常见应用场景解析 🌰

知道了实现方式,咱们再看看实际开发中最常用的几个场景,把理论落地成代码:

1. 身份验证守卫

这是最常用的守卫 ------ 检查用户是否登录,没登录就不让进。咱们来写一个更完善的版本:

jsx 复制代码
// AuthGuard.jsx
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "../hooks/useAuth"; // 假设我们有个自定义的auth hook

const AuthGuard = ({ children, requiredRole = null }) => {
  // 从useAuth获取登录状态、用户信息、加载状态
  const { isAuthenticated, user, loading } = useAuth();
  const location = useLocation();

  // 加载中(比如正在检查token是否有效),显示加载提示
  if (loading) {
    return <div>Loading...</div>; // 这里可以换成好看的加载动画
  }

  // 没登录?去登录页
  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // 如果需要特定角色,且用户角色不匹配?去无权限页
  if (requiredRole && user.role !== requiredRole) {
    return <Navigate to="/unauthorized" replace />;
  }

  // 所有检查通过,放行!
  return children;
};

使用的时候也很简单,想保护哪个组件就包哪个:

jsx 复制代码
// 只需要登录就能访问
<AuthGuard>
  <Profile />
</AuthGuard>

// 需要管理员角色才能访问
<AuthGuard requiredRole="admin">
  <AdminPanel />
</AuthGuard>

这个守卫不仅能检查登录状态,还能检查用户角色,相当于一个 "多功能安检门"------ 既能查门票,又能查 VIP 证件。

2. 权限守卫

有时候光有角色还不够,需要更细粒度的权限控制。比如同样是 "编辑" 角色,有的能发布文章,有的只能草稿。这时候就需要权限守卫:

jsx 复制代码
// PermissionGuard.jsx
import { Navigate } from "react-router-dom";
import { usePermissions } from "../hooks/usePermissions"; // 自定义权限hook

const PermissionGuard = ({ children, requiredPermissions = [] }) => {
  // 获取权限检查工具和加载状态
  const { hasPermission, loading } = usePermissions();

  // 权限检查中,显示提示
  if (loading) {
    return <div>Checking permissions...</div>;
  }

  // 检查是否拥有所有必需的权限
  const hasAllPermissions = requiredPermissions.every((permission) =>
    hasPermission(permission)
  );

  // 少一个权限都不行!
  if (!hasAllPermissions) {
    return <Navigate to="/unauthorized" replace />;
  }

  // 权限足够,放行
  return children;
};

使用示例:

jsx 复制代码
// 需要"文章发布"权限才能访问
<PermissionGuard requiredPermissions={["article:publish"]}>
  <ArticlePublisher />
</PermissionGuard>

// 需要同时拥有"评论查看"和"评论删除"权限
<PermissionGuard requiredPermissions={["comment:view", "comment:delete"]}>
  <CommentManager />
</PermissionGuard>

这种细粒度的权限控制,在大型应用中特别重要。就像公司里,不是所有员工都能进档案室,就算能进,也不是所有人都能删文件 ------ 权限守卫就是做这个的。

3. 数据预加载守卫

有些页面必须依赖数据才能显示。比如商品详情页,必须先获取商品 ID 对应的信息,否则页面就是空的。这时候可以用数据预加载守卫:

jsx 复制代码
// DataGuard.jsx
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

const DataGuard = ({ children, dataLoader }) => {
  const [data, setData] = useState(null); // 存储加载的数据
  const [loading, setLoading] = useState(true); // 加载状态
  const [error, setError] = useState(null); // 错误状态
  const navigate = useNavigate();

  useEffect(() => {
    // 定义加载数据的函数
    const loadData = async () => {
      try {
        // 调用传入的dataLoader函数(具体加载什么数据由外部决定)
        const result = await dataLoader();
        setData(result); // 保存数据
      } catch (err) {
        setError(err); // 记录错误
        navigate("/error", { replace: true }); // 加载失败跳错误页
      } finally {
        setLoading(false); // 无论成功失败,都结束加载状态
      }
    };

    loadData(); // 执行加载
  }, [dataLoader, navigate]);

  // 加载中显示提示
  if (loading) {
    return <div>Loading data...</div>;
  }

  // 有错误就不显示内容了(已经跳走了)
  if (error) {
    return null;
  }

  // 数据加载成功,把数据传给子组件(如果需要)
  return children(data);
};

使用示例:

jsx 复制代码
// 商品详情页的路由
<Route
  path="/product/:id"
  element={
    <DataGuard
      // 传入数据加载函数(获取商品详情)
      dataLoader={async () => {
        const id = useParams().id; // 获取URL中的商品ID
        const response = await fetch(`/api/products/${id}`);
        if (!response.ok) throw new Error("Failed to load product");
        return response.json();
      }}
    >
      {/* 数据加载成功后,通过参数接收数据并显示 */}
      {(product) => <ProductDetail product={product} />}
    </DataGuard>
  }
/>

这个守卫就像 "内容预热器"------ 在用户看到页面之前,先把需要的内容准备好,避免用户看到空白或错误的页面。用户体验直接提升一个档次!

最佳实践与技巧 💡

掌握了基本用法,咱们再看看高手是怎么用路由守卫的 ------ 几个最佳实践能让你的代码更优雅、更好维护。

1. 统一的路由守卫管理

如果一个页面需要多个守卫(比如先检查登录,再检查权限,最后加载数据),一个个嵌套会很麻烦:

jsx 复制代码
// 嵌套太多,看着就头大
<AuthGuard>
  <PermissionGuard>
    <DataGuard>
      <Dashboard />
    </DataGuard>
  </PermissionGuard>
</AuthGuard>

这时候可以写一个 "守卫组合器",把多个守卫合并成一个:

jsx 复制代码
// guards/index.js
// 创建一个函数,接收守卫数组,返回一个组合后的守卫组件
export const createRouteGuard = (guards = []) => {
  return ({ children }) => {
    // 用reduce把多个守卫"串联"起来
    return guards.reduce((acc, guard) => {
      // 前一个守卫的输出作为后一个守卫的输入
      return guard(acc);
    }, children);
  };
};

// 使用示例:组合多个守卫
const ProtectedRoute = createRouteGuard([
  (children) => <AuthGuard>{children}</AuthGuard>,
  (children) => <PermissionGuard requiredPermissions={["dashboard:read"]}>{children}</PermissionGuard>,
  (children) => <DataGuard dataLoader={loadDashboardData}>{children}</DataGuard>
]);

// 用的时候直接包一层就行,清爽多了!
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />

这种方式就像 "守卫流水线"------ 用户先过登录检查,再过权限检查,最后等数据加载,一步步来,逻辑清晰又好维护。

2. 路由元信息的巧用

如果你的路由很多,每个都写守卫逻辑会很重复。可以用 "路由元信息"(meta)来简化 ------ 把守卫需要的条件存在路由配置里,守卫自动读取这些条件:

jsx 复制代码
// routes.jsx
// 路由配置里加一个meta字段,存守卫需要的信息
const routes = [
  {
    path: "/dashboard",
    element: <Dashboard />,
    meta: {
      requiresAuth: true, // 需要登录
      requiredRole: "user", // 需要user角色
      permissions: ["dashboard:read"], // 需要dashboard:read权限
      title: "Dashboard" // 还能顺便存页面标题,一举多得
    },
  },
  {
    path: "/admin",
    element: <AdminPanel />,
    meta: {
      requiresAuth: true,
      requiredRole: "admin", // 需要admin角色
      permissions: ["admin:access"],
      title: "Admin Panel"
    },
  }
];

// 路由守卫组件:自动读取meta信息做检查
const RouteGuard = ({ route, children }) => {
  const { meta } = route; // 获取当前路由的meta信息

  // 如果需要登录且未登录,跳登录页
  if (meta?.requiresAuth && !isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  // 如果需要特定角色且角色不匹配,跳无权限页
  if (meta?.requiredRole && user.role !== meta.requiredRole) {
    return <Navigate to="/unauthorized" replace />;
  }

  // 其他检查...

  return children;
};

这种方式相当于给每个路由贴了个 "通行证要求" 标签,守卫只要照着标签检查就行 ------ 不用为每个路由写单独的检查逻辑,大大减少重复代码。

3. 异步路由守卫的处理

很多时候,守卫需要执行异步操作(比如调用 API 检查权限)。这时候要特别注意处理异步状态,避免出现 "闪屏"(用户短暂看到不该看的内容)。

jsx 复制代码
// AsyncRouteGuard.jsx
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";

const AsyncRouteGuard = ({ children, guardFunction }) => {
  // 用null表示"还没检查完",true表示"可以访问",false表示"禁止访问"
  const [canAccess, setCanAccess] = useState(null);
  const navigate = useNavigate();

  useEffect(() => {
    // 异步检查函数
    const checkAccess = async () => {
      try {
        // 调用外部传入的异步守卫函数(比如API检查)
        const result = await guardFunction();
        setCanAccess(result); // 保存检查结果
      } catch (error) {
        // 出错了也禁止访问
        setCanAccess(false);
        navigate("/error", { replace: true });
      }
    };

    checkAccess(); // 执行检查
  }, [guardFunction, navigate]);

  // 还没检查完?显示加载提示(别让用户看到内容)
  if (canAccess === null) {
    return <div>Checking access...</div>;
  }

  // 检查不通过?不显示内容(已经跳走了)
  if (!canAccess) {
    return null;
  }

  // 检查通过,显示内容
  return children;
};

使用示例(检查用户是否有 VIP 权限):

jsx 复制代码
<AsyncRouteGuard
  guardFunction={async () => {
    // 调用API检查是否是VIP
    const response = await fetch("/api/user/is-vip");
    const data = await response.json();
    return data.isVip; // 返回true/false
  }}
>
  <VipContent />
</AsyncRouteGuard>

处理异步守卫的关键是:在结果出来之前,绝对不能显示受保护的内容 。就像安检还没结束,绝对不能让乘客进候车厅 ------ 异步守卫的canAccess === null状态就是这个作用。

实际项目中的应用案例 🏭

理论讲了这么多,咱们看看在实际项目中,这些守卫是怎么组合起来用的。

1. 登录状态检查(useAuth 钩子)

首先需要一个管理登录状态的工具,通常是一个自定义 hook:

jsx 复制代码
// hooks/useAuth.js
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";

export const useAuth = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false); // 是否登录
  const [user, setUser] = useState(null); // 用户信息
  const [loading, setLoading] = useState(true); // 加载状态
  const navigate = useNavigate();

  // 初始化时检查登录状态(比如刷新页面后)
  useEffect(() => {
    const checkAuth = async () => {
      try {
        // 从localStorage获取保存的token
        const token = localStorage.getItem("token");
        if (!token) {
          // 没token = 没登录
          setIsAuthenticated(false);
          return;
        }

        // 调用API验证token是否有效
        const response = await fetch("/api/auth/verify", {
          headers: { Authorization: `Bearer ${token}` },
        });

        if (response.ok) {
          // token有效,获取用户信息
          const userData = await response.json();
          setUser(userData);
          setIsAuthenticated(true);
        } else {
          // token无效,清除token
          localStorage.removeItem("token");
          setIsAuthenticated(false);
        }
      } catch (error) {
        // 网络错误等情况,也视为未登录
        setIsAuthenticated(false);
      } finally {
        // 无论结果如何,结束加载
        setLoading(false);
      }
    };

    checkAuth();
  }, []);

  // 登录函数(用户输入账号密码后调用)
  const login = async (credentials) => {
    try {
      const response = await fetch("/api/auth/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(credentials),
      });

      if (response.ok) {
        const { token, user } = await response.json();
        localStorage.setItem("token", token); // 保存token
        setUser(user);
        setIsAuthenticated(true);
        return true; // 登录成功
      } else {
        return false; // 登录失败
      }
    } catch (error) {
      return false;
    }
  };

  // 登出函数
  const logout = () => {
    localStorage.removeItem("token"); // 清除token
    setIsAuthenticated(false);
    setUser(null);
    navigate("/login"); // 跳回登录页
  };

  // 把需要的状态和方法返回出去
  return { isAuthenticated, user, loading, login, logout };
};

这个useAuth就像一个 "身份管理中心"------ 所有和登录相关的操作(检查状态、登录、登出)都由它负责。有了它,AuthGuard组件就能轻松获取登录状态了。

2. 完整路由配置示例

有了守卫组件和useAuth,就可以配置完整的路由了:

jsx 复制代码
// App.jsx
import { RouterProvider } from "react-router-dom";
import { router } from "./router";

const App = () => {
  // 用RouterProvider渲染路由
  return <RouterProvider router={router} />;
};

// router.jsx(核心路由配置)
import { createBrowserRouter } from "react-router-dom";
import { AuthGuard, PermissionGuard } from "./guards";
import Layout from "./Layout";
import Dashboard from "./pages/Dashboard";
import AdminPanel from "./pages/AdminPanel";
import Login from "./pages/Login";
import Unauthorized from "./pages/Unauthorized";

// 创建路由
const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />, // 共用的布局(导航栏+内容区)
    children: [
      // 首页(不需要守卫,所有人可访问)
      { path: "", element: <Home /> },

      // 仪表盘(需要登录+仪表盘访问权限)
      {
        path: "dashboard",
        element: (
          <AuthGuard>
            <PermissionGuard permissions={["dashboard:read"]}>
              <Dashboard />
            </PermissionGuard>
          </AuthGuard>
        ),
      },

      // 管理员面板(需要登录+管理员角色+管理员权限)
      {
        path: "admin",
        element: (
          <AuthGuard requiredRole="admin">
            <PermissionGuard permissions={["admin:access"]}>
              <AdminPanel />
            </PermissionGuard>
          </AuthGuard>
        ),
      },
    ],
  },

  // 登录页(不需要守卫,未登录用户访问)
  { path: "/login", element: <Login /> },

  // 无权限页(不需要守卫,被拒绝访问时跳转)
  { path: "/unauthorized", element: <Unauthorized /> },
]);

这个配置把前面讲的所有守卫都串起来了:

  • 未登录用户访问/dashboard?被AuthGuard拦下来,跳/login

  • 已登录但不是管理员的用户访问/admin?被AuthGuard(检查角色)拦下来,跳/unauthorized

  • 管理员但没有admin:access权限?被PermissionGuard拦下来,跳/unauthorized

  • 所有检查通过?顺利访问对应页面。

这样的路由配置,既安全又灵活,能应对大多数应用的需求。

注意事项与常见问题 ⚠️

路由守卫虽然好用,但如果用不好,可能会出现各种小问题。咱们来看看需要注意什么:

1. 避免无限重定向

这是最容易踩的坑!比如你给/login页面也加了AuthGuard,就会出现:

  • 未登录用户访问/dashboard → 被守卫跳/login

  • 访问/login时,因为未登录 → 又被守卫跳/login

  • 无限循环,页面崩溃!

解决办法很简单:登录页、注册页、无权限页这些 "公共跳转页",绝对不能加登录相关的守卫。就像医院的急诊通道,不能设置门禁 ------ 否则救护车都进不来了。

2. 性能优化要点

守卫里的逻辑会在路由跳转时执行,如果太复杂,会让跳转变慢。优化技巧:

  • 避免在守卫里做不必要的计算或 API 调用(比如重复检查同一个权限)。

  • useMemouseCallback缓存数据和函数(比如dataLoader函数可以缓存,避免每次渲染都创建新函数)。

  • 加载状态要简洁(别搞太复杂的动画,影响性能)。

简单说就是:守卫只做必要的检查,别在里面 "摸鱼" 做额外工作。

3. 用户体验与错误处理

用户被拦截时,一定要给明确的提示:

  • 加载时显示 "正在检查权限...",别让用户以为页面卡住了。

  • 被拒绝访问时,跳转到/unauthorized并显示 "您没有访问该页面的权限",别让用户一脸懵。

  • 数据加载失败时,显示 "加载失败,请重试",并提供重试按钮。

好的用户体验,就是让用户知道 "为什么被拦"、"该怎么办",而不是单纯地跳走。

4. 测试很重要

路由守卫涉及很多条件判断,一定要测试各种情况:

  • 未登录用户访问受保护页面 → 应该跳登录页。

  • 已登录用户访问受保护页面 → 应该正常显示。

  • 权限不足的用户访问 → 应该跳无权限页。

  • 刷新页面后,守卫是否还能正常工作 → (比如 token 存在时是否能保持登录状态)。

可以用 React Testing Library 写测试用例,也可以手动测试 ------ 总之别让守卫在生产环境掉链子。

总结 📝

路由守卫是 React 应用中控制页面访问的核心机制,就像应用的 "保安系统"------ 既能保护敏感内容,又能引导用户正确使用应用。

今天咱们从基础概念讲到实际应用,涵盖了:

  • 什么是路由守卫及其核心功能

  • 三种实现方式(HOC、组件内、路由配置)

  • 三大常见场景(身份验证、权限控制、数据预加载)

  • 最佳实践(守卫组合、元信息、异步处理)

  • 实际项目中的应用和注意事项

只要掌握了这些知识,你就能写出既安全又好用的路由守卫,让你的应用既不怕 "不速之客",又能给合法用户良好的体验。

最后送大家一句话:路由守卫的核心不是 "拦",而是 "引导"------ 拦对的人,引导对的人去对的地方。希望你写的守卫,既能守护应用安全,又能让用户感觉顺畅自然! 🚀

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax