您现在的位置是:网站首页> 编程资料编程资料
react最流行的生态替代antdpro搭建轻量级后台管理_React_
2023-05-24
239人已围观
简介 react最流行的生态替代antdpro搭建轻量级后台管理_React_
前言
你是否经历过公司的产品和 ui 要求左侧菜单栏要改成设计图上的样子? 苦恼 antd-pro 强绑定的 pro-layout 菜单栏不能自定义?你可以使用 umi,但是就要根据它的约定来开发,捆绑全家桶等等。手把手教你搭一个轻量级的后台模版,包括路由的权限、动态菜单等等。
为方便使用 antd 组件库,你可以改成任意你喜欢的。数据请求的管理使用 react-query,类似 useRequest,但是更加将大。样式使用 tailwindcss 加 styled-components,因为 antd v5 将使用 css in js。路由的权限和菜单管理使用 react-router-auth-plus。。。
项目初始化
# npm 7+ npm create vite spirit-admin -- --template react-ts
react-router-auth-plus (权限路由、动态菜单解决方案) 仓库地址文章地址
等等...
数据请求 + mock
配置 axios
设置拦截器,并在 main.ts 入口文件中引入这个文件,使其在全局生效
// src/axios.ts import axios, { AxiosError } from "axios"; import { history } from "./main"; // 设置 response 拦截器,状态码为 401 清除 token,并返回 login 页面。 axios.interceptors.response.use( function (response) { return response; }, function (error: AxiosError) { if (error.response?.status === 401) { localStorage.removeItem("token"); // 在 react 组件外使用路由方法, 使用方式会在之后路由配置时讲到 history.push("/login"); } return Promise.reject(error); } ); // 设置 request 拦截器,请求中的 headers 带上 token axios.interceptors.request.use(function (request) { request.headers = { authorization: localStorage.getItem("token") || "", }; return request; }); 配置 react-query
在 App 外层包裹 QueryClientProvider,设置默认选项,窗口重新聚焦时和失败时不重新请求。
// App.tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; export const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: false, retry: false, }, }, }); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( ); 我们只有两个请求,登录和获取当前用户,src 下新建 hooks 文件夹,再分别建 query、mutation 文件夹,query 是请求数据用的,mutation 是发起数据操作的请求用的。具体可以看 react-query 文档
获取当前用户接口
// src/hooks/query/useCurrentUserQuery.ts import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import { queryClient } from "../../main"; // useQuery 需要唯一的 key,react-query v4 是数组格式 const currentUserQueryKey = ["currentUser"]; // 查询当前用户,如果 localStorage 里没有 token,则不请求 export const useCurrentUserQuery = () => useQuery(currentUserQueryKey, () => axios.get("/api/me"), { enabled: !!localStorage.getItem("token"), }); // 可以在其它页面获取 useCurrentUserQuery 的数据 export const getCurrentUser = () => { const data: any = queryClient.getQueryData(currentUserQueryKey); return { username: data?.data.data.username, }; }; 登录接口
// src/hooks/mutation/useLoginMutation.ts import { useMutation } from "@tanstack/react-query"; import axios from "axios"; export const useLoginMutation = () => useMutation((data) => axios.post("/api/login", data)); mock
数据请求使用 react-query + axios, 因为只有两个请求,/login(登录) 和 /me(当前用户),直接使用 express 本地 mock 一下数据。新建 mock 文件夹,分别建立 index.js 和 users.js
// users.js 存放两种类型的用户 export const users = [ { username: "admin", password: "admin" }, { username: "employee", password: "employee" }, ]; // index.js 主文件 import express from "express"; import { users } from "./users.js"; const app = express(); const port = 3000; const router = express.Router(); // 登录接口,若成功返回 token,这里模拟 token 只有两种情况 router.post("/login", (req, res) => { setTimeout(() => { const username = req.body.username; const password = req.body.password; const user = users.find((user) => user.username === username); if (user && password === user.password) { res.status(200).json({ code: 0, token: user.username === "admin" ? "admin-token" : "employee-token", }); } else { res.status(200).json({ code: -1, message: "用户名或密码错误" }); } }, 2000); }); // 当前用户接口,请求时需在 headers 中带上 authorization,若不正确返回 401 状态码。根据用户类型返回权限和用户名 router.get("/me", (req, res) => { setTimeout(() => { const token = req.headers.authorization; if (!["admin-token", "employee-token"].includes(token)) { res.status(401).json({ code: -1, message: "请登录" }); } else { const auth = token === "admin-token" ? ["application", "setting"] : []; const username = token === "admin-token" ? "admin" : "employee"; res.status(200).json({ code: 0, data: { auth, username } }); } }, 2000); }); app.use(express.json()); // 接口前缀统一加上 /api app.use("/api", router); // 禁用 304 缓存 app.disable("etag"); app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); 在 package.json 中的 scripts 添加一条 mock 命令,需安装 nodemon,用来热更新 mock 文件的。npm run mock 启动 express 服务。
"scripts": { ... "mock": "nodemon mock/index.js" } 现在在项目中还不能使用,需要在 vite 中配置 proxy 代理
// vite.config.ts export default defineConfig({ plugins: [react()], server: { proxy: { "/api": { target: "http://localhost:3000", changeOrigin: true, }, }, }, }); 路由权限配置
路由和权限这块使用的方案是 react-router-auth-plus,具体介绍见上篇
路由文件
新建一个 router.tsx,引入页面文件,配置项目所用到的所有路由,配置上权限。这里我们扩展一下 AuthRouterObject 类型,自定义一些参数,例如左侧菜单的 icon、name 等。设置上 /account/center 和 /application 路由需要对应的权限。
import { AppstoreOutlined, HomeOutlined, UserOutlined, } from "@ant-design/icons"; import React from "react"; import { AuthRouterObject } from "react-router-auth-plus"; import { Navigate } from "react-router-dom"; import BasicLayout from "./layouts/BasicLayout"; import Application from "./pages/application"; import Home from "./pages/home"; import Login from "./pages/login"; import NotFound from "./pages/404"; import Setting from "./pages/account/setting"; import Center from "./pages/account/center"; export interface MetaRouterObject extends AuthRouterObject { name?: string; icon?: React.ReactNode; hideInMenu?: boolean; hideChildrenInMenu?: boolean; children?: MetaRouterObject[]; } // 只需在需要权限的路由配置 auth 即可 export const routers: MetaRouterObject[] = [ { path: "/", element: }, { path: "/login", element: }, { element: , children: [ { path: "/home", element: , name: "主页", icon: , }, { path: "/account", name: "个人", icon: , children: [ { path: "/account", element: , }, { path: "/account/center", name: "个人中心", element: , }, { path: "/account/setting", name: "个人设置", element: , // 权限 auth: ["setting"], }, ], }, { path: "/application", element: , // 权限 auth: ["application"], name: "应用", icon: , }, ], }, { path: "*", element: }, ]; main.tsx
使用 HistoryRouter,在组件外可以路由跳转,这样就可以在 axios 拦截器中引入 history 跳转路由了。
import { createBrowserHistory } from "history"; import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom"; export const history = createBrowserHistory({ window }); ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
相关内容
- vue中的vendor.js文件过大问题及解决_vue.js_
- Vue如何解决每次发版都要强刷清除浏览器缓存问题_vue.js_
- vue中使用keep-alive动态删除已缓存组件方式_vue.js_
- uniapp开发安卓App实现高德地图路线规划导航功能的全过程_javascript技巧_
- react 路由权限动态菜单方案配置react-router-auth-plus_React_
- Composition API思想封装NProgress示例详解_vue.js_
- 关于element中el-cascader的使用方式_vue.js_
- vue实现前端保持筛选条件到url并进行同步参数设计_vue.js_
- vant中如何修改用户的头像_vue.js_
- vue在标签中如何使用(data-XXX)自定义属性并获取_vue.js_
点击排行
本栏推荐
