第一步:利用cra脚手架 创建项目
文档地点:Create React App 中文文档
- npx create-react-app '你的项目名称'
复制代码 第二步:整理项目结构和删除多余代码
目标效果图:
在src目录下分别新建apis,assets,components,pages,router,store,utlis文件夹
原始项目目录结构只保留App.js,index.css,index.js这个三个文件
App.js:
index.css:
index.js:
拓展:安装scss
测试和利用:
App.js内容:
把index.css改为index.scss
index.js内容:
效果:
第三步:安装ui组件库-Ant Design
官网链接:在 create-react-app 中利用 - Ant Design
测试利用:
src/App.js:
- import { Button } from 'antd';
- function App() {
- return (
- <div>
- <Button type="primary">Button</Button>
- </div>
- );
- }
- export default App;
复制代码 效果图:
第四步:配置路由
中文文档:在开始之前 | React Router6 中文文档
第一步:安装
- npm install react-router-dom
复制代码 第二步:新建两个页面文件
在src/pages下新建两个文件Layout和Login
Layout/index.js:
- const Layout= ()=>{
- return(
- <div>
- 我是Layout
- </div>
- )
- }
- export default Layout
复制代码 Login/index.js:
- const Login= ()=>{
- return(
- <div>
- 我是Login
- </div>
- )
- }
- export default Login
复制代码 第三步:新建路由实例
src/router下新建index.js
- import Layout from "../pages/Layout";
- import Login from "../pages/Login";
- import { createBrowserRouter } from "react-router-dom";
- // 配置路由
- const router = createBrowserRouter([
- {
- path: '/',
- element: <Layout></Layout>
- },
- {
- path: '/login',
- element: <Login></Login>
- }
- ])
- export default router
复制代码 第四步:利用挂载实例
在src/index.js种导入
- import React from 'react';
- import ReactDOM from 'react-dom/client';
- import './index.scss';
- import router from './router';
- import { RouterProvider } from 'react-router-dom';
- const root = ReactDOM.createRoot(document.getElementById('root'));
- root.render(
- <React.StrictMode>
- <RouterProvider router={router}></RouterProvider>
- </React.StrictMode>
- );
复制代码 效果图:
第五步:封装request
第一步:修改src/pages/Login/index.js文件
- import { Card, Button, Form, Input, message } from 'antd';
- import './index.scss'
- import { useDispatch } from 'react-redux';
- import { fetchLogin } from '../../store/modules/user';
- import { useNavigate } from 'react-router-dom';
- const Login = () => {
- const navigate= useNavigate()
- // 实例的账号密码
- // 13800000002
- // 246810
- const dispatch = useDispatch()
- const onFinish =async (values) => {
- await dispatch(fetchLogin(values))
- // 跳转
- navigate('/')
- // 提示
- message.success('登录成功')
- console.log('Success:', values);
- };
- const onFinishFailed = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
- return (
- <div>
- <Card className='card'>
- <Form
- name="basic"
- labelCol={{
- span: 8,
- }}
- wrapperCol={{
- span: 16,
- }}
- style={{
- maxWidth: 600,
- }}
- initialValues={{
- remember: true,
- }}
- onFinish={onFinish}
- onFinishFailed={onFinishFailed}
- autoComplete="off"
- >
- <Form.Item
- label="Username"
- name="mobile"
- rules={[
- {
- required: true,
- message: 'Please input your username!',
- },
- ]}
- >
- <Input />
- </Form.Item>
- <Form.Item
- label="Password"
- name="code"
- rules={[
- {
- required: true,
- message: 'Please input your password!',
- },
- ]}
- >
- <Input.Password />
- </Form.Item>
- <Form.Item
- wrapperCol={{
- offset: 8,
- span: 16,
- }}
- >
- <Button type="primary" htmlType="submit">
- Submit
- </Button>
- </Form.Item>
- </Form>
- </Card>
- </div>
- )
- }
- export default Login
复制代码 在src/pages/Login下添加index.scss
- .card{
- display: flex;
- justify-content: center;
- position: fixed;
- top: 100px;
- left:35%;
- width: 500px;
- }
复制代码 第二步:封装request
中文文档:起步 | Axios中文文档 | Axios中文网
安装axios
在src/utlis下新建request.js和index.js
request.js:
- // axios封装
- // 1 根域名配置
- // 2 超时时间
- // 3 请求拦截器
- // 4 响应拦截器
- import axios from "axios";
- import { getToken } from "./token";
- const request = axios.create({
- baseURL:'http://geek.itheima.net/v1_0',
- timeout:5000
- })
- // 添加请求拦截器
- request.interceptors.request.use(function (config) {
- // 在发送请求之前做些什么
- const token= getToken()
- if(token){
- config.headers.Authorization='Bearer '+token
- }
- return config;
- }, function (error) {
- // 对请求错误做些什么
- return Promise.reject(error);
- });
- // 添加响应拦截器
- request.interceptors.response.use(function (response) {
- // 2xx 范围内的状态码都会触发该函数。
- // 对响应数据做点什么
- return response.data;
- }, function (error) {
- // 超出 2xx 范围的状态码都会触发该函数。
- // 对响应错误做点什么
- return Promise.reject(error);
- });
- export {request}
复制代码 index.js:
- // 统一中转工具函数导出
- import { request } from "./request";
- import { setToken,getToken,removeToken } from "./token";
- export {
- request,
- setToken,
- getToken,
- removeToken
- }
复制代码 第六步:利用redux封装token
第一步安装react-redux/@reduxjs/toolkit
中文文档:入门 Redux | Redux 中文官网
- npm install @reduxjs/toolkit react-redux
复制代码 在src/store新建modules/user.js和index.js
src/store/index.js:
- // 组合子模块
- import { configureStore } from "@reduxjs/toolkit";
- import userStore from "./modules/user";
- const store = configureStore({
- reducer:{
- userStore
- }
- })
- export default store
复制代码 src/store/modules/user.js:
- // 放置和用户相关的
- import { createSlice } from "@reduxjs/toolkit";
- import { request,setToken as _setToken,getToken } from "../../utils";
- const userSlice= createSlice({
- name:'user',
- initialState:{
- // token持久化
- token:getToken()||'',
- },
- reducers:{
- setToken:(state,action)=>{
- state.token=action.payload
- // token持久化
- _setToken(action.payload)
- }
- }
- })
- // 解构
- const {setToken} = userSlice.actions
- // 获取reducer函数
- const userStore= userSlice.reducer
- // 封装一部方法 完成登录 获取token
- const fetchLogin=(loginForm)=>{
- return async (dispatch)=>{
- // 1 发送异步请求
- const res = await request.post('/authorizations',loginForm)
- // 2 提交同步修改方法
- dispatch(setToken(res.data.token))
- }
- }
- export {fetchLogin,setToken}
- export default userStore
复制代码 最后在src/index.js添加store
- import React from 'react';
- import ReactDOM from 'react-dom/client';
- import './index.scss';
- import router from './router';
- import { RouterProvider } from 'react-router-dom';
- import { Provider } from 'react-redux';
- import store from './store';
- const root = ReactDOM.createRoot(document.getElementById('root'));
- root.render(
- <React.StrictMode>
- <Provider store={store}>
- <RouterProvider router={router}></RouterProvider>
- </Provider>
- </React.StrictMode>
- );
复制代码 然后在login举行调用
封装存,取,删token的方法
在src/utlis新建token.js
- const TOKENKEY='token'
- const setToken =(token)=>{
- localStorage.setItem(TOKENKEY,token)
- }
- const getToken=()=>{
- return localStorage.getItem(TOKENKEY)
- }
- const removeToken=()=>{
- localStorage.removeItem(TOKENKEY)
- }
- export {setToken,getToken,removeToken}
复制代码 在src/pages/Layout下举行测试
- // 测试toekn
- import { useEffect } from "react"
- import { request } from "../../utils"
- const Layout= ()=>{
- useEffect(()=>{
- request.get('/user/profile')
- },[])
- return(
- <div>
- 我是Layout
- </div>
- )
- }
- export default Layout
复制代码 第七步:根据token举行路由权限判断
在src/components下新建Auth.js
- // 封装高阶组件
- // 有token正常访问 无token跳转login
- import { getToken } from "../utils"
- import { Navigate } from "react-router-dom"
- const Auth = ({children}) => {
- const token = getToken()
- if(token){
- return <>{children}</>
- }else{
- return <Navigate to={'/login'} replace></Navigate>
- }
- }
- export default Auth
复制代码 然后在router/index.js修改
- import Layout from "../pages/Layout";
- import Login from "../pages/Login";
- import { createBrowserRouter } from "react-router-dom";
- import Auth from "../components/Auth";
- import Home from "../pages/Home";
- import Article from "../pages/Article";
- import Publish from "../pages/Publish";
- // 配置路由
- const router = createBrowserRouter([
- {
- path: '/',
- element: <Auth><Layout></Layout></Auth>,
- children:[
- {
- index:true,
- element:<Home></Home>
- },
- {
- path:'article',
- element:<Article></Article>
- }, {
- path:'publish',
- element:<Publish></Publish>
- }
- ]
- },
- {
- path: '/login',
- element: <Login></Login>
- }
- ])
- export default router
复制代码 新建src/pages下的Home,Article,Publish文件
Home.js:
- const Home = ()=>{
- return(
- <div>我是Home</div>
- )
- }
- export default Home
复制代码 下面两个文件类推!!!
第八步:封装Laout样式组件
覆盖src/pages/Layout/index.js
- import React, { useEffect, useState } from 'react';
- import {
- MenuFoldOutlined,
- MenuUnfoldOutlined,
- UploadOutlined,
- UserOutlined,
- VideoCameraOutlined,
- } from '@ant-design/icons';
- import { Button, Layout, Menu, theme, Popconfirm } from 'antd';
- import { Outlet, useLocation, useNavigate } from 'react-router-dom';
- import { useDispatch, useSelector } from 'react-redux';
- import { clearUserInfo, fetchUserInfo } from '../../store/modules/user';
- const { Header, Sider, Content } = Layout;
- const App = () => {
- const navigste = useNavigate()
- const dispatch = useDispatch()
- const [collapsed, setCollapsed] = useState(false);
- const {
- token: { colorBgContainer, borderRadiusLG },
- } = theme.useToken();
- // 侧边跳转
- const MenuClick = (key) => {
- navigste(key)
- }
- useEffect(() => {
- dispatch(fetchUserInfo())
- }, [dispatch])
- const userInfo = useSelector(state => state.userStore.userInfo)
- // 退出按钮
- const confirm = () => {
- dispatch(clearUserInfo())
- navigste('/login')
- }
- // 获取路由参数 初始化侧边高亮
- const location = useLocation()
- return (
- <Layout style={{ height: '100vh' }}>
- <Sider trigger={null} collapsible collapsed={collapsed}>
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '10px' }}>
- <img src='https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg' style={{ width: '50%', height: '50px' }} alt=''></img>
- {!collapsed &&
- <div style={{ color: '#fff', width: '50%' }}>哈哈哈</div>
- }
- </div>
- <Menu
- onClick={({ key }) => MenuClick(key)}
- theme="dark"
- mode="inline"
- defaultSelectedKeys={[location.pathname]}
- items={[
- {
- key: '/',
- icon: <UserOutlined />,
- label: '首页',
- },
- {
- key: '/article',
- icon: <VideoCameraOutlined />,
- label: '文章',
- },
- {
- key: '/publish',
- icon: <UploadOutlined />,
- label: '发布',
- },
- ]}
- />
- </Sider>
- <Layout>
- <Header
- style={{
- padding: '0 24px 0 0',
- background: colorBgContainer,
- display: 'flex',
- justifyContent: 'space-between'
- }}
- >
- <Button
- type="text"
- icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
- onClick={() => setCollapsed(!collapsed)}
- style={{
- fontSize: '16px',
- width: 64,
- height: 64,
- }}
- />
- <div style={{ display: 'flex', alignItems: 'center', fontSize: '16px' }}>
- <span>{userInfo.name}</span>
- <UserOutlined style={{ marginLeft: '10px' }} />
- <Popconfirm
- title="退出"
- description="确认退出吗?"
- onConfirm={confirm}
- okText="Yes"
- cancelText="No"
- >
- <span style={{ cursor: "pointer" }}>退出</span>
- </Popconfirm>
- </div>
- </Header>
- <Content
- style={{
- margin: '24px 16px',
- padding: 24,
- minHeight: 280,
- background: colorBgContainer,
- borderRadius: borderRadiusLG,
- }}
- >
- {/* 二级路由出口 */}
- <Outlet></Outlet>
- </Content>
- </Layout>
- </Layout>
- );
- };
- export default App;
复制代码 封装退出方法
src/store/modules/user.js
- // 放置和用户相关的
- import { createSlice } from "@reduxjs/toolkit";
- import { request,setToken as _setToken,getToken, removeToken } from "../../utils";
- const userSlice= createSlice({
- name:'user',
- initialState:{
- // token持久化
- token:getToken()||'',
- userInfo:{}
- },
- reducers:{
- setToken:(state,action)=>{
- state.token=action.payload
- // token持久化
- _setToken(action.payload)
- },
- setUserInfo:(state,action)=>{
- state.userInfo=action.payload
- },
- clearUserInfo:(state)=>{
- state.token=''
- state.userInfo={}
- removeToken()
- }
- }
- })
- // 解构
- const {setToken,setUserInfo,clearUserInfo} = userSlice.actions
- // 获取reducer函数
- const userStore= userSlice.reducer
- // 封装异步方法 完成登录 获取token
- const fetchLogin=(loginForm)=>{
- return async (dispatch)=>{
- // 1 发送异步请求
- const res = await request.post('/authorizations',loginForm)
- // 2 提交同步修改方法
- dispatch(setToken(res.data.token))
- }
- }
- // 获取个人信息
- const fetchUserInfo=()=>{
- return async (dispatch)=>{
- // 1 发送异步请求
- const res = await request.get('/user/profile')
- // 2 提交同步修改方法
- dispatch(setUserInfo(res.data))
- }
- }
- export {fetchLogin,setToken,fetchUserInfo,clearUserInfo}
- export default userStore
复制代码 第九步:处置处罚token失效
src/utlis/request.js
- // axios封装
- // 1 根域名配置
- // 2 超时时间
- // 3 请求拦截器
- // 4 响应拦截器
- import axios from "axios";
- import { getToken, removeToken } from "./token";
- import router from "../router";
- const request = axios.create({
- baseURL:'http://geek.itheima.net/v1_0',
- timeout:5000
- })
- // 添加请求拦截器
- request.interceptors.request.use(function (config) {
- // 在发送请求之前做些什么
- const token= getToken()
- if(token){
- config.headers.Authorization='Bearer '+token
- }
- return config;
- }, function (error) {
- // 对请求错误做些什么
- return Promise.reject(error);
- });
- // 添加响应拦截器
- request.interceptors.response.use(function (response) {
- // 2xx 范围内的状态码都会触发该函数。
- // 对响应数据做点什么
- return response.data;
- }, function (error) {
- // 超出 2xx 范围的状态码都会触发该函数。
- // 对响应错误做点什么
- // 处理token失效
- if(error.response.status===401){
- removeToken()
- router.navigate('/login')
- window.location.reload()
- }
- return Promise.reject(error);
- });
- export {request}
复制代码 第十步:封装Api请求
src/apis/user.js
- import { request } from "../utils";
- const loginApi=(data)=>{
- return request({
- url:'/authorizations',
- method:'post',
- data
- })
- }
- const userInfoApi=()=>{
- return request({
- url:'/user/profile',
- method:'get',
- })
- }
- export {loginApi,userInfoApi}
复制代码 修改src/store/modules/user.js
- // 放置和用户相关的
- import { createSlice } from "@reduxjs/toolkit";
- import {setToken as _setToken,getToken, removeToken } from "../../utils";
- import { loginApi, userInfoApi } from "../../apis/user";
- const userSlice= createSlice({
- name:'user',
- initialState:{
- // token持久化
- token:getToken()||'',
- userInfo:{}
- },
- reducers:{
- setToken:(state,action)=>{
- state.token=action.payload
- // token持久化
- _setToken(action.payload)
- },
- setUserInfo:(state,action)=>{
- state.userInfo=action.payload
- },
- clearUserInfo:(state)=>{
- state.token=''
- state.userInfo={}
- removeToken()
- }
- }
- })
- // 解构
- const {setToken,setUserInfo,clearUserInfo} = userSlice.actions
- // 获取reducer函数
- const userStore= userSlice.reducer
- // 封装异步方法 完成登录 获取token
- const fetchLogin=(loginForm)=>{
- return async (dispatch)=>{
- // 1 发送异步请求
- const res = await loginApi(loginForm)
- // 2 提交同步修改方法
- dispatch(setToken(res.data.token))
- }
- }
- // 获取个人信息
- const fetchUserInfo=()=>{
- return async (dispatch)=>{
- // 1 发送异步请求
- const res = await userInfoApi()
- // 2 提交同步修改方法
- dispatch(setUserInfo(res.data))
- }
- }
- export {fetchLogin,setToken,fetchUserInfo,clearUserInfo}
- export default userStore
复制代码 拓展:利用图表
Echarts文档:Examples - Apache ECharts
在src/pages/新建一个Echarts文件来放置我们的echrats
src/pages/Echarts/index.js:
- import * as echarts from 'echarts';
- import { useEffect, useRef } from 'react';
- const Echarts = () => {
- const chartRef= useRef(null)
- useEffect(() => {
- // 保证dom是可用的
- // 1 获取要渲染的图标节点
- const chartDom = chartRef.current
- // 2 初始化生成echarts实例
- const myChart = echarts.init(chartDom);
- // 3 配置参数
- const option = {
- xAxis: {
- type: 'category',
- data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- },
- yAxis: {
- type: 'value'
- },
- series: [
- {
- data: [120, 200, 150, 80, 70, 110, 130],
- type: 'bar'
- }
- ]
- };
- // 4 使用参数完成渲染
- option && myChart.setOption(option);
- }, [])
- return (
- <div>
- <div ref={chartRef} style={{width:'600px',height:'600px'}}></div>
- </div>
- )
- }
- export default Echarts
复制代码 在src/router/index.js添加路由
- import Layout from "../pages/Layout";
- import Login from "../pages/Login";
- import { createBrowserRouter } from "react-router-dom";
- import Auth from "../components/Auth";
- import Home from "../pages/Home";
- import Article from "../pages/Article";
- import Publish from "../pages/Publish";
- import Echarts from "../pages/Echarts";
- // 配置路由
- const router = createBrowserRouter([
- {
- path: '/',
- element: <Auth><Layout></Layout></Auth>,
- children: [
- {
- index: true,
- element: <Home></Home>
- },
- {
- path: 'article',
- element: <Article></Article>
- }, {
- path: 'publish',
- element: <Publish></Publish>
- },
- {
- path:'echarts',
- element:<Echarts></Echarts>
- }
- ]
- },
- {
- path: '/login',
- element: <Login></Login>
- }
- ])
- export default router
复制代码 在src/pages/Layout/index.js追加路由
- import React, { useEffect, useState } from 'react';
- import {
- MenuFoldOutlined,
- MenuUnfoldOutlined,
- UploadOutlined,
- UserOutlined,
- VideoCameraOutlined,
- } from '@ant-design/icons';
- import { Button, Layout, Menu, theme, Popconfirm } from 'antd';
- import { Outlet, useLocation, useNavigate } from 'react-router-dom';
- import { useDispatch, useSelector } from 'react-redux';
- import { clearUserInfo, fetchUserInfo } from '../../store/modules/user';
- const { Header, Sider, Content } = Layout;
- const App = () => {
- const navigste = useNavigate()
- const dispatch = useDispatch()
- const [collapsed, setCollapsed] = useState(false);
- const {
- token: { colorBgContainer, borderRadiusLG },
- } = theme.useToken();
- // 侧边跳转
- const MenuClick = (key) => {
- navigste(key)
- }
- useEffect(() => {
- dispatch(fetchUserInfo())
- }, [dispatch])
- const userInfo = useSelector(state => state.userStore.userInfo)
- // 退出按钮
- const confirm = () => {
- dispatch(clearUserInfo())
- navigste('/login')
- }
- // 获取路由参数 初始化侧边高亮
- const location = useLocation()
- return (
- <Layout style={{ height: '100vh' }}>
- <Sider trigger={null} collapsible collapsed={collapsed}>
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '10px' }}>
- <img src='https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg' style={{ width: '50%', height: '50px' }} alt=''></img>
- {!collapsed &&
- <div style={{ color: '#fff', width: '50%' }}>哈哈哈</div>
- }
- </div>
- <Menu
- onClick={({ key }) => MenuClick(key)}
- theme="dark"
- mode="inline"
- defaultSelectedKeys={[location.pathname]}
- items={[
- {
- key: '/',
- icon: <UserOutlined />,
- label: '首页',
- },
- {
- key: '/article',
- icon: <VideoCameraOutlined />,
- label: '文章',
- },
- {
- key: '/publish',
- icon: <UploadOutlined />,
- label: '发布',
- },
- {
- key: '/echarts',
- icon: <UploadOutlined />,
- label: '图标',
- },
- ]}
- />
- </Sider>
- <Layout>
- <Header
- style={{
- padding: '0 24px 0 0',
- background: colorBgContainer,
- display: 'flex',
- justifyContent: 'space-between'
- }}
- >
- <Button
- type="text"
- icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
- onClick={() => setCollapsed(!collapsed)}
- style={{
- fontSize: '16px',
- width: 64,
- height: 64,
- }}
- />
- <div style={{ display: 'flex', alignItems: 'center', fontSize: '16px' }}>
- <span>{userInfo.name}</span>
- <UserOutlined style={{ marginLeft: '10px' }} />
- <Popconfirm
- title="退出"
- description="确认退出吗?"
- onConfirm={confirm}
- okText="Yes"
- cancelText="No"
- >
- <span style={{ cursor: "pointer" }}>退出</span>
- </Popconfirm>
- </div>
- </Header>
- <Content
- style={{
- margin: '24px 16px',
- padding: 24,
- minHeight: 280,
- background: colorBgContainer,
- borderRadius: borderRadiusLG,
- }}
- >
- {/* 二级路由出口 */}
- <Outlet></Outlet>
- </Content>
- </Layout>
- </Layout>
- );
- };
- export default App;
复制代码 效果图:
拓展:利用富文本编辑器
这里介绍的富文本叫ReactQuill,利用比较简单,用的也比较多,实用于大多数场景!
npm地点:react-quill - npm
- npm install react-quill --save
复制代码 在src/pages/Publish/index.js:
- import ReactQuill from 'react-quill';
- import 'react-quill/dist/quill.snow.css';
- // 以上为导入资源
- import { useState } from 'react';
- const Publish = () => {
- // value 就是富文本的值
- const [value, setValue] = useState('');
- console.log(value);
- return (
- <div>
- <ReactQuill style={{ width: '500px', height: '200px' }} theme="snow" value={value} onChange={setValue} >
- </ReactQuill>
- </div>
- )
- }
- export default Publish
复制代码 效果图:
到这里恭喜你完成了一个简单版本的react后台模板
示例代码地点:react-demo-pc: 训练利用的一个react后台模板,主要熟悉接洽ui组件库,redux,router,axios
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |