官网:React Native 中文网 · 使用React来编写原生应用的框架
npx create-react-app my-app
npm start
npm eject 暴露项目优先提交代码
git add .
git commit -m “搭建项目“
4.yarn add node-sass --dev 和 yarn add less less-loader --dev
5.修改配置config/webpack 打包文件 在75行左右
添加代码
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
//配置 less
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'icss',
},
},
'less-loader'
),
sideEffects: true,
},
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
},
'less-loader'
),
},
其实就把上面sass配置代码复制一遍,改成less。按照以上操作后,项目已支持Less。
6.接下来安装Stylus
yarn add stylus stylus-loader --dev
//stylus
const stylusRegex = /\.styl$/;
const stylusModuleRegex = /\.module\.styl$/;
安装完成后,按照上节介绍的支持Less的方法,修改config/webpack.config.js:
//配置 stylus
{
test: stylusRegex,
exclude: stylusModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'icss',
},
},
'stylus-loader'
),
sideEffects: true,
},
{
test:stylusModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
},
'stylus-loader'
),
},
6.设置路径别名(避免使用相对路径的麻烦)
检索:alias
//config/webpack.config.js
//设置绝对路径
'@': path.join(__dirname, '..', 'src')
若使用绝对路径,在pakage.json配置
"homepage": "./",
"name": "react-demo",
"version": "0.1.0",
"private": true,
"homepage": "./",
├─ /config <-- webpack配置目录
├─ /node_modules
├─ /public
| ├─ favicon.ico <-- 网页图标
| └─ index.html <-- HTML页模板
├─ /scripts <-- node编译脚本
├─ /src
| ├─ /api <-- api目录
| | └─ index.js <-- api库
| ├─ /common <-- 全局公用目录
| | ├─ /fonts <-- 字体文件目录
| | ├─ /images <-- 图片文件目录
| | ├─ /js <-- 公用js文件目录
| | └─ /styles <-- 公用样式文件目录
| | | ├─ frame.styl <-- 全部公用样式(import本目录其他全部styl)
| | | ├─ reset.styl <-- 清零样式
| | | └─ global.styl <-- 全局公用样式
| ├─ /components <-- 公共模块组件目录
| | ├─ /header <-- 头部导航模块
| | | ├─ index.js <-- header主文件
| | | └─ header.styl <-- header样式文件
| | └─ ... <-- 其他模块
| ├─ /pages <-- 页面组件目录
| | ├─ /home <-- home页目录
| | | ├─ index.js <-- home主文件
| | | └─ home.styl <-- home样式文件
| | ├─ /login <-- login页目录
| | | ├─ index.js <-- login主文件
| | | └─ login.styl <-- login样式文件
| | └─ ... <-- 其他页面
| ├─ /route <-- 路由配置目录
| ├─ /store <-- Redux配置目录
| ├─ globalConfig.js <-- 全局配置文件
| ├─ index.js <-- 项目入口文件
| ├─.gitignore
| ├─ package.json
| ├─ README.md
| └─ yarn.lock
1.设置styles样式(我使用的是webstrom 记得安装插件stylus)
global样式
html, body, #root
height: 100%
/*清浮动*/
.clearfix:after
content: "."
display: block
height: 0
clear: both
visibility: hidden
.clearfix
display:block
frame导入样式
@import "./global.styl"
@import "./reset.styl"
在index.js 入口文件中导入文件预处理样式
import React from 'react';
import ReactDOM from 'react-dom/client';
import '@/common/styles/frame.styl'
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
官网:Ant Design - 一套企业级 UI 设计语言和 React 组件库
1.安装
yarn add antd
2.设置Antd为中文语言
import React from 'react';
import ReactDOM from 'react-dom/client';
// 全局样式
import '@/common/styles/frame.styl'
// 引入Ant Design中文语言包
import zhCN from 'antd/locale/zh_CN'
import App from './App';
import {ConfigProvider} from "antd";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
官网:React Router 主页 | React Router7 中文文档
1.安装(备注:安装时需要根据自己的node版本选择版本,默认是安装最新的,最新的需要node环境是20的)
备注:yarn add react-router-dom 只是针对项目内注册是组件跳转使用
yarn add react-router-dom
useNavigate()
作用:编程式导航,返回
const navigate = useNavigate();
navigate("/path", { state: { data } }); // 支持相对路径和状态传递
useParams()
作用:获取动态路由参数(如)
/user/:id
useSearchParams()
作用:获取和操作 URL 查询参数
Jsconst [searchParams, setSearchParams] = useSearchParams();
const id = searchParams.get("id");
useLocation()
作用:获取当前路由的location
对象(包含pathname、search、state)
2.在router/index.js 文件下添加路由信息
import { createHashRouter, Navigate } from 'react-router-dom'
import Home from "../pages/home";
import Login from "../pages/login";
export const routes = createHashRouter([
{
path: '/',
element: ,
children: []
},
{
path: '/login',
element: ,
children: []
},
{
path: '/home',
element: ,
},
{
path: '*',
element:
}
]);
3.在src/index.js 下引入路由,并删除App.js文件
import React from 'react';
import ReactDOM from 'react-dom/client';
// 全局样式
import '@/common/styles/frame.styl'
// 引入Ant Design中文语言包
import zhCN from 'antd/locale/zh_CN'
// 引入路由配置
import {ConfigProvider} from "antd";
import {RouterProvider} from "react-router-dom";
import {routes} from "../src/router/index";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
4.实现跳转
在login界面进行引入路由
import {useNavigate} from "react-router-dom";
使用完整示例
import { Button, Input } from 'antd'
import imgLogo from './logo.png'
import './login.styl'
import {useNavigate} from "react-router-dom";
function Login() {
const navigate = useNavigate()
return (
)
}
export default Login
5.安装非组件内跳转路由,以上跳转,只是针对在组件内进行跳转,一下安装是非React组件内跳转
yarn add [email protected]
6.安装完成后在src/router/hisRouter.js 写一个goto方法
import { createHashHistory } from 'history'
let history = createHashHistory()
export const goto = (path) => {
history.push(path)
}
7.在src/pages/home/index.js里调用goto方法
import {Button, theme} from "antd";
import {goto} from "../../router/hisRouter";
import {Content} from "antd/es/layout/layout";
function admin(){
// 获取Design Token
const token = theme.useToken().token || {};
const contentStyle = {
textAlign: 'center',
minHeight: '100%',
lineHeight: '120px',
color: '#fff',
backgroundColor: token.colorBgContainer,
};
return (
admin
)
}
export default admin
1.安装图标
yarn add @ant-design/icons
2.在src/components/extraIcons/index.js文件添加代码
import Icon from '@ant-design/icons'
//特别注意
//https://www.iconfont.cn/
//检查svg代码中是否有class以及与颜色相关的fill、stroke等属性,如有,必须连带属性一起删除。
//确保标签中有fill="currentColor",否则图标的颜色将不能改变。
//确保标签中width和height属性的值为1em,否则图标的大小将不能改变。
const SunSvg = () => (
)
const MoonSvg = () => (
// 这里粘贴“月亮”图标的SVG代码
)
const ThemeSvg = () => (
// 这里粘贴“主题色”图标的SVG代码
)
export const SunOutlined = (props) =>
export const MoonOutlined = (props) =>
export const ThemeOutlined = (props) =>
3.在Header组件下编写代码index.js 和 header.stly
import { Button, Card } from 'antd'
import { MoonOutlined, ThemeOutlined } from '@/components/extraIcons'
import './header.styl'
function Header() {
return (
Header
} shape="circle">
} shape="circle">
)
}
export default Header
.M-header
position: relative
z-index: 999
border-radius: 0
overflow hidden
.ant-card-body
padding: 16px 24px
height: 62px
line-height: 32px
.header-wrapper
display: flex
.logo-con
display: flex
font-size: 30px
font-weight: bold
.opt-con
display: flex
flex: 1
justify-content: flex-end
gap: 20px
4.代码测试,在home 界面里面引入使用
import Header from "../../components/header";
import { useNavigate } from 'react-router-dom'
import { Button } from 'antd'
import { goto } from '../../api/index'
import './home.styl'
import Header from "../../components/header";
function Home() {
// 创建路由钩子
const navigate = useNavigate()
return (
Home Page
)
}
export default Home
1.在header/index.js 下添加代码
import { Button, Card } from 'antd'
import { MoonOutlined, ThemeOutlined } from '@/components/extraIcons'
import './header.styl'
function Header(props) {
//接收父组件传的值
const {title,info} =props
if (info){
info()
}
return (
Header{title }
} shape="circle">
} shape="circle">
)
}
export default Header
2.在home/index.js 里面添加代码,并测试运行代码
{console.log("接受了数据")}} />
1.创建二级路由的框架页面
src/pages/entry/index.js 和entry.styl
import { Outlet } from 'react-router-dom'
import Header from '../../components/header'
import './entry.styl'
function Entry() {
return (
)
}
export default Entry
.M-entry
display: flex
flex-direction: column
height: 100%
.main-container
position: relative
flex: 1
2.在src/pages下添加一个admin 参考对比界面
3.配置路由页面完整测试代码
import { createHashRouter, Navigate } from 'react-router-dom'
import Home from "../pages/home";
import Login from "../pages/login";
import Admin from "../pages/admin";
import Entry from "../pages/entry";
export const routes = createHashRouter([
{
path: '/login',
element: ,
},
{
index: true,
element: ,
},
{
path: '/entry/*',
element: ,
children: [
{ path: 'home', element: },
{ path: 'admin', element: },
{ index: true, element: },
{ path: '*', element: }
]
},
{
path: '*',
element:
}
]);
Redux 中文文档
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux Toolkit (也称为 “RTK” ) 是我们官方推荐的编写 Redux 逻辑的方法。@reduxjs/toolkit
包封装了核心的 redux
包,包含我们认为构建 Redux 应用所必须的 API 方法和常用依赖。 Redux Toolkit 集成了我们建议的最佳实践,简化了大部分 Redux 任务,阻止了常见错误,并让编写 Redux 应用程序变得更容易。
1.安装
yarn add @reduxjs/toolkit react-redux
2.在全局配置文件src/globalConfig.js里面配置信息(用来配置主题)
export const globalConfig = {
//初始化主题
initTheme: {
// 初始为亮色主题
dark: false,
// 初始主题色
// 与customColorPrimarys数组中的某个值对应
// null表示默认使用Ant Design默认主题色或customColorPrimarys第一种主题色方案
colorPrimary: null,
},
// 供用户选择的主题色,如不提供该功能,则设为空数组
customColorPrimarys: [
'#1677ff',
'#f5222d',
'#fa8c16',
'#722ed1',
'#13c2c2',
'#52c41a',
],
// localStroge用户主题信息标识
SESSION_LOGIN_THEME: 'userTheme',
// localStroge用户登录信息标识
SESSION_LOGIN_INFO: 'userLoginInfo',
}
3.创建用于主题换肤的store分库,src/store/slices/theme.js
import { createSlice } from '@reduxjs/toolkit'
import { globalConfig } from '../../globalConfig'
// 先从localStorage里获取主题配置
const sessionTheme = JSON.parse(window.localStorage.getItem(globalConfig.SESSION_LOGIN_THEME))
// 如果localStorage里没有主题配置,则使用globalConfig里的初始化配置
const initTheme = sessionTheme?sessionTheme: globalConfig.initTheme
export const themeSlice = createSlice({
// store分库名称
name: 'theme',
// store分库初始值
initialState:{
dark: initTheme.dark,
colorPrimary: initTheme.colorPrimary
},
reducers: {
// redux方法:设置亮色/暗色主题
setDark: (state, action) => {
// 修改了store分库里dark的值(用于让全项目动态生效)
state.dark = action.payload
// 更新localStorage的主题配置(用于长久保存主题配置)
window.localStorage.setItem(globalConfig.SESSION_LOGIN_THEME, JSON.stringify(state))
},
// redux方法:设置主题色
setColorPrimary: (state, action) => {
// 修改了store分库里colorPrimary的值(用于让全项目动态生效)
state.colorPrimary = action.payload
// 更新localStorage的主题配置(用于长久保存主题配置)
window.localStorage.setItem(globalConfig.SESSION_LOGIN_THEME, JSON.stringify(state))
},
},
})
// 将setDark和setColorPrimary方法抛出
export const { setDark } = themeSlice.actions
export const { setColorPrimary } = themeSlice.actions
export default themeSlice.reducer
4.创建store总库src/index.js
import {configureStore} from "@reduxjs/toolkit";
import {themeSlice} from "./slices/theme";
export const store = configureStore({
reducer: {
//主题换肤分库
theme: themeSlice.reducer,
}
})
5.引入store库,src/index.js
//引入store
import {store} from "../src/store/index";
import {Provider} from "react-redux";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
);
6.在header头里面配置主题信息和调用store方法,修改后的header/index.js
import { Button, Card } from 'antd'
//导入图标
import { MoonOutlined, ThemeOutlined,SunOutlined } from '../extraIcons/index'
import './header.styl'
import { useSelector, useDispatch } from 'react-redux'
import { setDark } from '../../store/slices/theme'
//导入store主题抛出的方法
function Header({title,info}) {
//如果传info方法就执行调用,这里是模拟演示,实际使用时,info方法是从父组件传入的
//父组件传入的info方法是在父组件中定义的,父组件中定义的info方法是在父组件中定义的,父组件中定义的info方法是在父组件中定义的
if (info){info()}
//获取store中theme的dispatch方法
const dispatch = useDispatch()
//获取store中theme的状态
const {dark} = useSelector((state)=>state.theme)
//图标主题切换
const darkChange=()=>{
return dark ?
}
shape="circle"
onClick={()=>{dispatch(setDark(false))}}>
:
}
shape="circle"
onClick={()=>{dispatch(setDark(true))}}>
}
//返回头部
return (
Header{title }
{darkChange()}
} shape="circle">
)
}
export default Header
7.修改src/entry/index.js页面设置主题的切换完整示例
import {Outlet, useLocation} from 'react-router-dom'
import Header from '../../components/header'
import './entry.styl'
import { useSelector, useDispatch } from 'react-redux'
//获取Ant Design的主题、
import { ConfigProvider, theme } from 'antd'
//darkAlgorithm为暗色主题,defaultAlgorithm为亮色(默认)主题
const {darkAlgorithm, defaultAlgorithm} = theme;
function Entry() {
const mate = useLocation();
//获取store中theme的状态
const globalTheme = useSelector((state) => state.theme);
const antdTheme = {
algorithm: globalTheme.dark ? darkAlgorithm : defaultAlgorithm,
}
return (
)
}
export default Entry
备注:在以上的主题切换中,页面中的“admin Page”始终是白色,并没有跟随换肤。这是因为它并没有包裹在Antd的组件中。而Header组件能够换肤是因为其外层用了Antd的
组件。所以在开发过程中,建议尽量使用Antd组件。
8.以上的换肤是针对使用Antd组件的换肤,可能也会遇到自行开发的组件也要换肤,非Ant Design组件的主题换肤。src/pages/admin/index.js
import {Button, theme} from "antd";
import {goto} from "../../api";
import {Content} from "antd/es/layout/layout";
function admin(){
// 获取Design Token
const token = theme.useToken().token || {};
const contentStyle = {
textAlign: 'center',
minHeight: '100%',
lineHeight: '120px',
color: '#fff',
backgroundColor: token.colorBgContainer,
};
return (
admin
)
}
export default admin
备注:把文字色设为了token.colorText,即当前Antd文本色,因此会跟随主题进行换肤。同理,如果想让自定义组件的背景色换肤,可以使用token.colorBgContainer;边框色换肤,可以使用token.colorBorder;使用当前Antd主题色,可以使用token.colorPrimary。
定制主题 - Ant Design
9.创建主题色选择对话框组件,新建src/components/themeModal/index.js
import { Modal } from 'antd'
import { useSelector, useDispatch } from 'react-redux'
import { CheckCircleFilled } from '@ant-design/icons'
import { setColorPrimary } from '@/store/slices/theme'
import { globalConfig } from '@/globalConfig'
import './themeModal.styl'
function ThemeModal({ open = false, onClose = (val) => {} }) { // 为 open 和 onClose 添加默认值
const dispatch = useDispatch()
// 从 store 中获取 theme
const theme = useSelector(state => state.theme)
//获取主题色列表
const themeColorList = globalConfig.customColorPrimarys || [];
// 渲染主题色列表
const renderThemeColorList = () => {
return themeColorList.map((item, index) => {
return (
{dispatch(setColorPrimary(item));onClose(false)}}>
{
theme.colorPrimary === item && (
)
}
)
})
}
return (
onClose(false)} // 确保 onClose 存在时调用
maskClosable={false}
footer={null} // 添加 footer 为 null,避免默认按钮干扰
>
{renderThemeColorList()}
)
}
export default ThemeModal
10.修改header/index.js 代码
const [isModalOpen, setIsModalOpen] = useState(false) // 添加状态管理
Header{title}
{darkChange()}
}
shape="circle"
onClick={() => setIsModalOpen(true)} // 绑定点击事件
/>
setIsModalOpen(false)} // 关闭弹窗
/>
11.设置修改后的主题颜色,修改src/pages/entry/index.js
//获取自定义的颜色
const customColor = globalTheme.colorPrimary || "";
//如果自定义的颜色存在,就将其设置为主题色示例
if (customColor){
antdTheme.token = {
colorPrimary: customColor,
}
}
1.安装dotenv-cli插件
yarn add dotenv-cli -g
2.在根目录下新建.env.dev
# 开发环境
#代理前缀
REACT_APP_BASE = '/api'
#接口前缀
REACT_APP_BASE_URL = 'http://localhost:8089'
#websocket前缀
REACT_APP_BASE_WSS = 'wss://localhost:8089'
同样在根目录下新建.env.pro
# 生产环境
#代理前缀
REACT_APP_BASE = '/api'
#接口前缀
REACT_APP_BASE_URL = 'http://localhost:8089'
#websocket前缀
REACT_APP_BASE_WSS = 'wss://localhost:8089'
3.配置package.json文件
"scripts": {
"dev": "dotenv -e .local.single.env -e .env.dev node scripts/start.js ",
"build": "dotenv -e .local.single.env -e .env.pro node scripts/build.js ",
"test": "node scripts/test.js",
"preview": "dotenv -e .local.single.env -e .env.pro node scripts/start.js "
},
1.安装axios
yarn add axios
2.构建请求封装src/api/Api.js 构建请求体封装
import axios from 'axios';
// 从环境变量中获取baseUrl和base
const {REACT_APP_BASE_URL,REACT_APP_BASE} = process.env;
// 创建axios实例
const instance = axios.create({
baseURL: REACT_APP_BASE_URL + REACT_APP_BASE,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
instance.interceptors.request.use(config => {
const token = localStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
return Promise.reject(error);
});
// 响应拦截器
instance.interceptors.response.use(response => {
if (response.status === 200) {
return response.data;
}
return Promise.reject(response);
}, error => {
return Promise.reject(error);
});
/**
* get参数转换
*/
const queryChangeFun = result => {
let queryString = Object.keys(result)
.map(key => `${key}=${result[key]}`)
.join('&');
return queryString.length >= 2 ? '?' + queryString : '';
};
// 封装通用请求方法
const askApi = (method = 'get', url = "", params = {}, query = {}, headers = {}) => {
const config = {
method: method.toLowerCase(), // 统一转换为小写
url,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
...headers // 合并自定义headers
}
};
// 根据请求方法处理参数
if (method.toLowerCase() === 'get') {
// config.params = query; // get请求使用params
config.url = url + queryChangeFun(query);
} else {
config.data = params; // 其他请求使用data
}
return instance(config)
};
export default askApi;
2.在src/interface/userApi.js创建一个模拟用户请求接口的封装
/**
* 用户相关接口请求
*/
import askApi from "../api/Api";
/**
* 登录接口
*/
export const loginApi = (param) => askApi('post', '/login',param,{},{});
3.在登录界面掉用接口,示例
function Login() {
const navigate = useNavigate();
const loginFun = () => {
loginApi({
username: 'admin',
password: 'admin'
}).then(res => {
console.log(res)
navigate("/entry")
}).catch(err => {
navigate("/login")
})
}
return (
)
}
作用:生成随机数据,拦截 Ajax 请求
1.安装:
yarn add mockjs
2.新建文件src/mock/index.js
import Mock from 'mockjs'
const {REACT_APP_BASE_URL,REACT_APP_BASE} =process.env;
const url = REACT_APP_BASE_URL+REACT_APP_BASE
// 设置延迟时间
Mock.setup({
timeout: '200-400'
});
if (process.env.NODE_ENV === 'development') {
Mock.mock(url+'/login','post', function (val) {
console.log(val)
return {
code: 200,
msg: '登录成功',
data: {
loginUid: 1000,
username: 'admin',
password: 'admin@123456',
token: "dnwihweh0w0183971030183971030",
},
}
})
}
3.在src/index.js 下引入方法,正式发布需要移除
//引入mock数据
import './mock/index'
4.优化登录界面和调用接口
安装第三方背景插件,可跳过: yarn add @splinetool/react-spline @splinetool/runtime
import {Button, Checkbox, Form, Input, message} from 'antd'
import './login.styl'
import {useNavigate} from "react-router-dom";
import {loginApi} from "../../interface/userApi";
import {useMsg} from "../../common/utils/toolUtil";
import Spline from "@splinetool/react-spline";
function Login() {
const {isMsg,msgTitle} = useMsg();
const navigate = useNavigate();
const onFinish = values => {
loginApi(values).then(res => {
isMsg(res)
if (res.code === 200) {
localStorage.setItem('token', res.data.token);
localStorage.setItem('userName', res.data.userName);
localStorage.setItem('userId', res.data.userId);
navigate("/entry")
}
}).catch(err => {
localStorage.removeItem('token');
})
};
return (
{msgTitle}
测试管理系统
还没有账户?
)
}
export default Login
5.封装两个工具类方法/src/comon/utils/toolUtil
/**
* 工具类
*/
import { message } from 'antd';
/**
* 判空
*/
export const isEmpty = (val) => {
if (val === undefined) return true;
if (val === null) return true;
if (val === '') return true;
if (val.length === 0) return true;
if (typeof val === 'object') {
return Object.keys(val).length === 0;
}
if(typeof val === 'number') {
return val === 0;
}
if (typeof val === 'string') {
return val === '0';
}
if (typeof val === 'boolean') {
return val;
}
if (typeof val === 'undefined'){
return false;
}
}
/**
* 消息提示
*/
export const useMsg = () => {
const [messageApi, contextHolder] = message.useMessage();
const isMsg = ({ code, msg }) => {
let type = 'info';
if (code === 200) type = 'success';
else if (code >= 400 && code < 500) type = 'warning';
else if (code >= 500) type = 'error';
messageApi[type](msg);
};
let msgTitle = contextHolder
return { isMsg, msgTitle };
};
1.在router/index.js 里面来个简单示例
// 白名单路径
const WHITE_LIST = ['/login']
// 路由守卫组件
function AuthRoute({ children }) {
const location = useLocation();
// 替换为实际的登录状态检查,例如从redux、context或localStorage获取
const isLogin = localStorage.getItem('token') !== null;
// 如果在白名单中直接放行
if (WHITE_LIST.includes(location.pathname)) {
return children;
}
// 不在白名单但已登录,放行
if (isLogin) {
return children;
}
// 否则重定向到登录页,并携带来源路径以便登录后跳转
return ;
}
完整示例:
import {createHashRouter, Navigate, useLocation} from 'react-router-dom'
import Home from "../pages/home";
import Login from "../pages/login";
import Admin from "../pages/admin";
import Entry from "../pages/entry";
// 白名单路径
const WHITE_LIST = ['/login']
export const routes = createHashRouter([
{
path: '/login',
element: ,
},
{
index: true,
element: ,
},
{
path: '/entry/*',
meta: { auth: true },
element: ,
children: [
{ path: 'home', element: },
{ path: 'admin', element: },
{ index: true, element: },
{ path: '*', element: }
]
},
{
path: '*',
element:
}
]);
// 路由守卫组件
function AuthRoute({ children }) {
const location = useLocation();
// 替换为实际的登录状态检查,例如从redux、context或localStorage获取
const isLogin = localStorage.getItem('token') !== null;
// 如果在白名单中直接放行
if (WHITE_LIST.includes(location.pathname)) {
return children;
}
// 不在白名单但已登录,放行
if (isLogin) {
return children;
}
// 否则重定向到登录页,并携带来源路径以便登录后跳转
return ;
}
1.安装(备注:有需求可设置,无需求跳过)
yarn add http-proxy-middleware@latest --save
2.在src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware')
const {REACT_APP_BASE_URL,REACT_APP_BASE}=process.env
/**
* 配置代理
*/
module.exports = function (app) {
app.use(
'/api-net',
createProxyMiddleware({
target: REACT_APP_BASE_URL+REACT_APP_BASE,
changeOrigin: true,
pathRewrite: {
'^/api-net': ''
},
})
)
}
true },
element: ,
children: [
{ path: ‘home’, element: },
{ path: ‘admin’, element: },
{ index: true, element: },
{ path: ‘', element: }
]
},
{
path: '’,
element:
}
]);
// 路由守卫组件
function AuthRoute({ children }) {
const location = useLocation();
// 替换为实际的登录状态检查,例如从redux、context或localStorage获取
const isLogin = localStorage.getItem(‘token’) !== null;
// 如果在白名单中直接放行
if (WHITE_LIST.includes(location.pathname)) {
return children;
}
// 不在白名单但已登录,放行
if (isLogin) {
return children;
}
// 否则重定向到登录页,并携带来源路径以便登录后跳转
return ;
}
# 十三,设置反向代理
1.安装(备注:有需求可设置,无需求跳过)
yarn add http-proxy-middleware@latest --save
2.在src/setupProxy.js
const { createProxyMiddleware } = require(‘http-proxy-middleware’)
const {REACT_APP_BASE_URL,REACT_APP_BASE}=process.env
/**