您的位置:首页 > 科技 > IT业 > 建立app_app开发比较好的公司_怎么查询最新网站_小说百度搜索风云榜

建立app_app开发比较好的公司_怎么查询最新网站_小说百度搜索风云榜

2025/4/7 22:43:35 来源:https://blog.csdn.net/qq_53722480/article/details/146947500  浏览:    关键词:建立app_app开发比较好的公司_怎么查询最新网站_小说百度搜索风云榜
建立app_app开发比较好的公司_怎么查询最新网站_小说百度搜索风云榜

React+Ant Design+router+axios安装完整教程

官网:React Native 中文网 · 使用React来编写原生应用的框架

一,安装

  1. npx create-react-app my-app

  2. npm start

  3. 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, #rootheight: 100%
/*清浮动*/
.clearfix:aftercontent: "."display: blockheight: 0clear: bothvisibility: hidden
.clearfixdisplay: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(<App />
);

三,安装Ant Design

官网: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(<ConfigProvider locale={zhCN}><App /></ConfigProvider>
);

四,安装路由

官网:React Router 主页 | React Router7 中文文档

1.安装(备注:安装时需要根据自己的node版本选择版本,默认是安装最新的,最新的需要node环境是20的)
备注:yarn add react-router-dom 只是针对项目内注册是组件跳转使用

yarn add react-router-dom

Hooks API

  1. useNavigate()

    作用:编程式导航,返回

    const navigate = useNavigate();
    navigate("/path", { state: { data } });  // 支持相对路径和状态传递
    
  2. useParams()

    作用:获取动态路由参数(如)

    /user/:id
    
  3. useSearchParams()

    作用:获取和操作 URL 查询参数

    Jsconst [searchParams, setSearchParams] = useSearchParams();
    const id = searchParams.get("id");
    
  4. 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:<Navigate to="/login" />,children: []},{path: '/login',element: <Login />,children: []},{path: '/home',element: <Home />,},{path: '*',element: <Navigate to="/login" />}
]);

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(<ConfigProvider locale={zhCN}><RouterProvider router={routes}></RouterProvider></ConfigProvider>
);

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 (<div className="content-body"><img src={imgLogo} alt="" className="logo" /><div className="ipt-con"><Input placeholder="账号" /></div><div className="ipt-con"><Input.Password placeholder="密码" /></div><div className="ipt-con"><Button type="primary" block={true} onClick={()=>{navigate("/home")}}>登录</Button></div></div>)
}
export default Login

5.安装非组件内跳转路由,以上跳转,只是针对在组件内进行跳转,一下安装是非React组件内跳转

yarn add history@4.10.1

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 Tokenconst token = theme.useToken().token || {};const contentStyle = {textAlign: 'center',minHeight: '100%',lineHeight: '120px',color: '#fff',backgroundColor: token.colorBgContainer,};return (<Content style={contentStyle}><div><span style={{color:token.colorText}}>admin</span><Button onClick={()=>{goto('/entry/home')}}>返回</Button></div></Content>)
}export default admin

五,创建自定义SVG图标Icon组件(无需求跳过)

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 = () => (<svg t="1743404892026"className="icon"viewBox="0 0 1024 1024"version="1.1"xmlns="http://www.w3.org/2000/svg"p-id="3408"width="1em"height="1em"fill="currentColor"><pathd="M344.189719 297.542353l-57.889397-57.889397-48.231443 48.232466 57.889397 57.889397L344.189719 297.542353zM254.129654 480.812217l-96.462886 0L157.666768 545.103411l96.462886 0L254.129654 480.812217zM543.518311 162.503932l-64.291194 0 0 93.214915 64.291194 0L543.518311 162.503932zM784.677572 287.885422l-48.231443-48.232466-57.89042 57.889397 45.031568 45.027474L784.677572 287.885422zM678.555709 728.42137l57.89042 57.841302 45.07557-44.982449-57.934423-57.885304L678.555709 728.42137zM768.614751 545.103411l96.464932 0 0-64.291194-96.464932 0L768.614751 545.103411zM511.397785 320.009018c-106.116747 0-192.926795 86.855073-192.926795 192.927818 0 106.113677 86.810048 192.923725 192.926795 192.923725 106.11777 0 192.923725-86.810048 192.923725-192.923725C704.32151 406.864091 617.515555 320.009018 511.397785 320.009018M479.227117 863.459791l64.291194 0 0-93.259941-64.291194 0L479.227117 863.459791zM238.068879 738.030205l48.231443 48.231443 57.889397-57.841302-44.982449-45.027474L238.068879 738.030205z"p-id="3409"></path></svg>
)const MoonSvg = () => (// 这里粘贴“月亮”图标的SVG代码
)const ThemeSvg = () => (// 这里粘贴“主题色”图标的SVG代码
)export const SunOutlined = (props) => <Icon component={SunSvg} {...props} />
export const MoonOutlined = (props) => <Icon component={MoonSvg} {...props} />
export const ThemeOutlined = (props) => <Icon component={ThemeSvg} {...props} />

3.在Header组件下编写代码index.js 和 header.stly

import { Button, Card } from 'antd'
import { MoonOutlined, ThemeOutlined } from '@/components/extraIcons'
import './header.styl'function Header() {return (<Card className="M-header"><div className="header-wrapper"><div className="logo-con">Header</div><div className="opt-con"><Button icon={<MoonOutlined />} shape="circle"></Button><Button icon={<ThemeOutlined />} shape="circle"></Button></div></div></Card>)
}export default Header
.M-headerposition: relativez-index: 999border-radius: 0overflow hidden.ant-card-bodypadding: 16px 24pxheight: 62pxline-height: 32px.header-wrapperdisplay: flex.logo-condisplay: flexfont-size: 30pxfont-weight: bold.opt-condisplay: flexflex: 1justify-content: flex-endgap: 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 (<div className="P-home"><Header /><h1>Home Page</h1><div className="ipt-con"><Button onClick={()=>{goto('/login')}}>组件外跳转</Button></div><div className="ipt-con"><Button type="primary" onClick={()=>{navigate('/login')}}>返回登录</Button></div></div>)
}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} =propsif (info){info()}return (<Card className="M-header"><div className="header-wrapper"><div className="logo-con">Header{title }</div><div className="opt-con"><Button icon={<MoonOutlined />} shape="circle"></Button><Button icon={<ThemeOutlined />} shape="circle"></Button></div></div></Card>)
}export default Header

2.在home/index.js 里面添加代码,并测试运行代码

<Header title='测试' info={()=>{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 (<div className="M-entry"><Header /><div className="main-container"><Outlet /></div></div>)
}
export default Entry
.M-entrydisplay: flexflex-direction: columnheight: 100%.main-containerposition: relativeflex: 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: <Login />,},{index: true,element: <Navigate to="/login" />,},{path: '/entry/*',element: <Entry />,children: [{ path: 'home', element: <Home /> },{ path: 'admin', element: <Admin /> },{ index: true, element: <Navigate to="home" /> },{ path: '*', element: <Navigate to="/404" /> }]},{path: '*',element: <Navigate to="/404" />}
]);

八,安装Redux及Redux Toolkit

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.initThemeexport 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.actionsexport 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(<Provider store={store}><ConfigProvider locale={zhCN}><RouterProvider router={routes}></RouterProvider></ConfigProvider></Provider>
);

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 ?<Button icon={<SunOutlined />}shape="circle"onClick={()=>{dispatch(setDark(false))}}></Button> :<Button icon={<MoonOutlined />}shape="circle"onClick={()=>{dispatch(setDark(true))}}></Button>}//返回头部return (<Card className="M-header"><div className="header-wrapper"><div className="logo-con">Header{title }</div><div className="opt-con">{darkChange()}<Button icon={<ThemeOutlined />} shape="circle"></Button></div></div></Card>)
}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 (<ConfigProvider theme={antdTheme}><div className="M-entry"><Header title={mate.pathname} /><div className="main-container"><Outlet /></div></div></ConfigProvider>)
}
export default Entry

备注:在以上的主题切换中,页面中的“admin Page”始终是白色,并没有跟随换肤。这是因为它并没有包裹在Antd的组件中。而Header组件能够换肤是因为其外层用了Antd的<Card>组件。所以在开发过程中,建议尽量使用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 Tokenconst token = theme.useToken().token || {};const contentStyle = {textAlign: 'center',minHeight: '100%',lineHeight: '120px',color: '#fff',backgroundColor: token.colorBgContainer,};return (<Content style={contentStyle}><div><span style={{color:token.colorText}}>admin</span><Button onClick={()=>{goto('/entry/home')}}>返回</Button></div></Content>)
}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 中获取 themeconst theme = useSelector(state => state.theme)//获取主题色列表const themeColorList = globalConfig.customColorPrimarys || [];// 渲染主题色列表const renderThemeColorList = () => {return  themeColorList.map((item, index) => {return (<div className="theme-color"style={{ backgroundColor: item.toString() }}key={index}onClick={() =>{dispatch(setColorPrimary(item));onClose(false)}}>{theme.colorPrimary === item && (<CheckCircleFilledstyle={{fontSize: 28,color: '#fff',}}/>)}</div>)})}return (<ModalclassName="M-themeModal"open={open}title="主题色"onCancel={() => onClose(false)} // 确保 onClose 存在时调用maskClosable={false}footer={null} // 添加 footer 为 null,避免默认按钮干扰><div className="colors-con">{renderThemeColorList()}</div></Modal>)
}export default ThemeModal

10.修改header/index.js 代码

const [isModalOpen, setIsModalOpen] = useState(false) // 添加状态管理
<Card className="M-header"><div className="header-wrapper"><div className="logo-con">Header{title}</div><div className="opt-con">{darkChange()}<Buttonicon={<ThemeOutlined />}shape="circle"onClick={() => setIsModalOpen(true)} // 绑定点击事件/></div></div><ThemeModalopen={isModalOpen}onClose={() => setIsModalOpen(false)} // 关闭弹窗/>
</Card>

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 "},

十,安装axios和请求封装

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请求使用paramsconfig.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 (<div className="content-body"><img src={imgLogo} alt="" className="logo" /><div className="ipt-con"><Input placeholder="账号" /></div><div className="ipt-con"><Input.Password placeholder="密码" /></div><div className="ipt-con"><Button type="primary" block={true} onClick={()=>{loginFun()}}>登录</Button></div></div>)
}

十一,安装Mock.js

​ 作用:生成随机数据,拦截 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 (<main><div className="spline"><Spline scene="https://prod.spline.design/zaHcDRWYBdPkoutI/scene.splinecode"/></div><div className="content-body"  >{msgTitle}<div className='card-body'><div className='title'><span className='svg'><svg t="1743563430303" className="icon" viewBox="0 0 1024 1024" version="1.1"xmlns="http://www.w3.org/2000/svg" p-id="5310" width="25" height="25"><pathd="M512 512m-91.264 0a91.264 91.264 0 1 0 182.528 0 91.264 91.264 0 1 0-182.528 0Z"fill="#000000" p-id="5311"></path><pathd="M256.341333 693.546667l-20.138666-5.12C86.101333 650.496 0 586.112 0 511.829333s86.101333-138.666667 236.202667-176.597333l20.138666-5.077333 5.674667 19.968a1003.946667 1003.946667 0 0 0 58.154667 152.661333l4.309333 9.088-4.309333 9.088a994.432 994.432 0 0 0-58.154667 152.661333l-5.674667 19.925334zM226.858667 381.866667c-114.090667 32.042667-184.106667 81.066667-184.106667 129.962666 0 48.853333 70.016 97.877333 184.106667 129.962667a1064.533333 1064.533333 0 0 1 50.432-129.962667A1056.085333 1056.085333 0 0 1 226.858667 381.866667z m540.8 311.68l-5.674667-20.010667a996.565333 996.565333 0 0 0-58.197333-152.618667l-4.309334-9.088 4.309334-9.088a999.253333 999.253333 0 0 0 58.197333-152.661333l5.674667-19.968 20.181333 5.077333c150.058667 37.930667 236.16 102.314667 236.16 176.64s-86.101333 138.666667-236.16 176.597334l-20.181333 5.12z m-20.949334-181.717334c20.48 44.330667 37.418667 87.893333 50.432 129.962667 114.133333-32.085333 184.106667-81.109333 184.106667-129.962667 0-48.896-70.016-97.877333-184.106667-129.962666a1057.621333 1057.621333 0 0 1-50.432 129.962666z"fill="#000000" p-id="5312"></path><pathd="M226.56 381.653333l-5.674667-19.925333C178.688 212.992 191.488 106.410667 256 69.205333c63.274667-36.522667 164.864 6.613333 271.317333 115.882667l14.506667 14.890667-14.506667 14.890666a1004.885333 1004.885333 0 0 0-103.338666 126.592l-5.76 8.234667-10.026667 0.853333a1009.365333 1009.365333 0 0 0-161.493333 26.026667l-20.138667 5.077333z m80.896-282.88c-11.434667 0-21.546667 2.474667-30.08 7.381334-42.410667 24.448-49.92 109.44-20.693333 224.128a1071.872 1071.872 0 0 1 137.941333-21.376 1060.138667 1060.138667 0 0 1 87.552-108.544c-66.56-64.810667-129.578667-101.589333-174.72-101.589334z m409.130667 868.778667c-0.042667 0-0.042667 0 0 0-60.8 0-138.88-45.781333-219.904-128.981333l-14.506667-14.890667 14.506667-14.890667a1003.946667 1003.946667 0 0 0 103.296-126.634666l5.76-8.234667 9.984-0.853333a1008.213333 1008.213333 0 0 0 161.578666-25.984l20.138667-5.077334 5.717333 19.968c42.112 148.650667 29.354667 255.274667-35.157333 292.437334a101.546667 101.546667 0 0 1-51.413333 13.141333z m-174.762667-144.256c66.56 64.810667 129.578667 101.589333 174.72 101.589333h0.042667c11.392 0 21.546667-2.474667 30.037333-7.381333 42.410667-24.448 49.962667-109.482667 20.693333-224.170667a1067.52 1067.52 0 0 1-137.984 21.376 1052.757333 1052.757333 0 0 1-87.509333 108.586667z"fill="#000000" p-id="5313"></path><pathd="M797.44 381.653333l-20.138667-5.077333a1001.770667 1001.770667 0 0 0-161.578666-26.026667l-9.984-0.853333-5.76-8.234667a998.997333 998.997333 0 0 0-103.296-126.592l-14.506667-14.890666 14.506667-14.890667C603.093333 75.861333 704.64 32.725333 768 69.205333c64.512 37.205333 77.312 143.786667 35.157333 292.48l-5.717333 19.968zM629.333333 308.906667c48.725333 4.437333 95.018667 11.648 137.984 21.376 29.269333-114.688 21.717333-199.68-20.693333-224.128-42.154667-24.362667-121.386667 12.970667-204.8 94.208A1060.224 1060.224 0 0 1 629.333333 308.906667zM307.456 967.552A101.546667 101.546667 0 0 1 256 954.410667c-64.512-37.162667-77.312-143.744-35.114667-292.437334l5.632-19.968 20.138667 5.077334c49.28 12.416 103.637333 21.162667 161.493333 25.984l10.026667 0.853333 5.717333 8.234667a1006.762667 1006.762667 0 0 0 103.338667 126.634666l14.506667 14.890667-14.506667 14.890667c-80.981333 83.2-159.061333 128.981333-219.776 128.981333z m-50.773333-274.218667c-29.269333 114.688-21.717333 199.722667 20.693333 224.170667 42.112 24.021333 121.301333-13.013333 204.8-94.208a1066.581333 1066.581333 0 0 1-87.552-108.586667 1065.642667 1065.642667 0 0 1-137.941333-21.376z"fill="#000000" p-id="5314"></path><pathd="M512 720.128c-35.114667 0-71.210667-1.536-107.349333-4.522667l-10.026667-0.853333-5.76-8.234667a1296.554667 1296.554667 0 0 1-57.6-90.538666 1295.104 1295.104 0 0 1-49.749333-95.061334l-4.266667-9.088 4.266667-9.088a1292.8 1292.8 0 0 1 49.749333-95.061333c17.664-30.549333 37.077333-61.013333 57.6-90.538667l5.76-8.234666 10.026667-0.853334a1270.826667 1270.826667 0 0 1 214.741333 0l9.984 0.853334 5.717333 8.234666a1280.256 1280.256 0 0 1 107.392 185.6l4.309334 9.088-4.309334 9.088a1262.933333 1262.933333 0 0 1-107.392 185.6l-5.717333 8.234667-9.984 0.853333c-36.138667 2.986667-72.277333 4.522667-107.392 4.522667z m-93.738667-46.250667c63.146667 4.736 124.330667 4.736 187.52 0a1237.589333 1237.589333 0 0 0 93.696-162.048 1219.626667 1219.626667 0 0 0-93.738666-162.048 1238.656 1238.656 0 0 0-187.477334 0 1215.018667 1215.018667 0 0 0-93.738666 162.048 1242.197333 1242.197333 0 0 0 93.738666 162.048z"fill="#000000" p-id="5315"></path></svg></span><span>测试管理系统</span></div><FormclassName='formBody'name="basic"layout="vertical"initialValues={{ remember: true }}onFinish={onFinish}autoComplete="off"size='large'><Form.Itemlabel="账户"name="userName"rules={[{ required: true, message: '账户不能为空!' }]}><Input /></Form.Item><Form.Itemlabel="密码"name="password"rules={[{ required: true, message: '密码不能为空!' }]}><Input.Password /></Form.Item><div className='noUserName'><span>还没有账户?</span></div><Form.Item><Button className='formBtn' type="primary" htmlType="submit">登录</Button></Form.Item></Form></div></div></main>)
}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 = contextHolderreturn { 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 <Navigate to="/login" state={{ from: location }} replace />;
}

完整示例:

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: <Login />,},{index: true,element: <Navigate to="/login" />,},{path: '/entry/*',meta: { auth: true },element: <AuthRoute><Entry /></AuthRoute> ,children: [{ path: 'home', element: <Home /> },{ path: 'admin', element: <Admin /> },{ index: true, element: <Navigate to="home" /> },{ path: '*', element: <Navigate to="/404" /> }]},{path: '*',element: <Navigate to="/404" />}
]);// 路由守卫组件
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 <Navigate to="/login" state={{ from: location }} replace />;
}

十三,设置反向代理

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 <Navigate to="/login" state={{ from: location }} replace />;

}


# 十三,设置反向代理1.安装(备注:有需求可设置,无需求跳过)yarn add http-proxy-middleware@latest --save2.在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’: ‘’
    },
    })
    )
    }

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com