使用react实现后台管理系统项目

一.开发React必须依赖三个库

    1.react:包含react所必须的核心代码

    2.react-dom:react渲染在不同平台所需要的核心代码

    3.babel:将jsx转换成React代码的工具

二.React的依赖引入

    1.方式一:直接CDN引入

    2.方式二:下载后,添加本地依赖

        1).在html中引入
            a.react.js
            b.react-dom.js
            c.babel.min.js

    3.方式三:通过npm管理(后续脚手架再使用)

三.定义组件化

1.react有两种方式定义组件

    1).采用class类来实现

    2).采用hook(函数)的方式来实现(定义组件首字母要大写,必须要继承React.Component)

    3).函数组件(function)

2.setState内部会做两件事情: 1.将state中的值改掉 2.自动执行render函数渲染页面

3.输出und,原因使用babel来去编译的话,意味着在严格模式下编译,那么在严格模式下得函数内部this指向und,

所以this也是指向und

4.组件化方法

    1).编写React的script代码中,必须添加 type="text/babel",作用是可以让babel解析jsx的语法

    2).使用ReactDOM.createRoot创建一个根节点用变量接收,把内容渲染到root上

        const root = ReactDOM.createRoot(document.querySelector('#root'))

    3).定义组件

        const App

    4).要渲染的根组件

        root.render()

    5).然后再定义class类(定义的组件名必须要大写),继承React.Component

        class App extends React.Component {}

    6).接着步骤5,里面有一个构造函数(子类继承父类)相当于data在构造函数中的state属性,this.state = {定义的数据}


        constructor(){super() this.state = {msg: 'Hello World'}}

    7).接着步骤6,render是渲染函数,相当于vue中的template(模板)
        render() {
                return (
                        


                            

{this.state.msg}


                        

                )
            }

this.setState 来更新数据S


四.react项目文件认识

1.node_modules所有的依赖

2.src代码区域

    a.根组件

    b.index.js运行项目的主入口

3.package.json包管理文件

五.JSX(语法糖)

    1.JSX的使用

        1).当变量是Number ,String,Array类型时,可以直接显示。

        2).当变量是null,undefined,boolean时,内容为空。

            若希望显示,则需要转成字符串;

            转换方式:toString,和“”空字符串拼接,String(变量)等方式;

        3).对象类型不能作为子元素(not valid as a React child)

    2.规范

         1) 需要使用()包含,代表一个整体

               2) jsx只能有一个根节点

                3) 可以使用单标签和双标签,单标签需要/结束

    3.jsx绑定属性

         1).绑定普通的属性,直接使用{}绑定
                        百度一下

                2).绑定class属性,需要使用className而不是用class来说声明,class被占用了
                       

我是p标签里的孩子

                3).绑定动态样式
                       

我是p标签里的孩子的孩子

                4).采用数组的方式绑定动态样式active,由于数组会默认以逗号连接, 因此绑定class属性需要调用join方法, 让它们以空格拼接 
                       

我是p标签里的孩子的孩子的孩子

            5).绑定内联样式
                       

啦啦


                       

扩扩

六.React事件绑定
    1.在react应用中,事件名都是用小驼峰格式进行书写,例如onclick要改写成onClick

、七.react条件渲染

    1.if 语句:如果有数据就显示组件,如果没有数据就不显示任何内容。posts 为需要渲染的列表

    2.三元运算符

    3.&&运算符:如果条件为真,则逻辑 && 运算符之后的表达式将是输出。如果条件为假,React 会忽略并跳过表达式.

八.react列表渲染

方式一:react vite创建npm create vite@latest

方式二:react脚手架创建

1.安装脚手架工具

    npm install create-react-app -g

2.检测项目

    create-react-app --version

3.创建项目

    create-react-app 项目名称(不能大写)

4.运行命令 npm start

5.注意:如果npm的镜像换成国内的镜像的话,会报一个会话超时的错误,原因是react的依赖需要访问国外的网络,但是我们的
npm镜像是国内的,所以会出现超时的错误,需要把npm镜像换回国外的镜像
npm config set registry https://registry.npmjs.org,再重新创建项目

九.生命周期

1.componentDidMount()挂载后,react向后台接口获取数据需要在此周期上运行

2.componentDidUpdate()更新后,指数据发生了变化

3.componentWillUnmount()卸载后

4.shouldComponentUpdate()state数据更新前会触发,此生命周期是返回一个true或者是false如果是true代表允许更新state数据,
 false是取消更新

十.React组件间的通信

    1.父组件传子组件:父组件通过 属性=传递的值 的形式来传递给子组件数据;子组件通过 this.props 参数获取父组件传递过来的数据;

    2.子组件传递父组件:在vue中是通过自定义事件来完成的;在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;

    3.插槽用法:props接收父组件传递的数据,children可以拿到父组件传递过来的标签

        const {children}=this.props//我是插槽传值

    4.context为了解决多层父子组件传值的问题(非父子之间传值)

        1)创建js文件,文件里面创建context对象,且赋值

            a.引入react

                import React from ‘react’

            b.创建context对象,且可以进行赋值,赋值是以对象的形式赋值的

                const ThemeContext=React。createContext({context:1})

            c.暴露出去

                export default ThemeContext

        2)在主组件中使用,使用方式是当做组件来使用

            a.引入ThemeContext.js文件

            b.然后当做组件来使用

        3)在需要的组件中进行挂载

            a.引入需要使用的js文件

                import ThemeeContext from ‘.../’

            b.给当前的类组件添加contextType属性

                Child.contextType=ThemeContext

            c.可以在render函数中直接使用this.context{this.context.age}

十一.ref获取dom三种方式

步骤构造函数创建ref语法:this.myRef名字=React.createRef()

绑定到元素上:{this.myRef名字}

获取DOM元素:this.myRef.current

  1.使用this.refs.属性获取节点

        console.log(this.refs.title1)

  2.使用createRef创建一个ref属性,使用时需要current来获取

        console.log(this.title2.current)

  3.在定义ref属性的时候使用箭头函数的方法来定义,参数就是节点

十二.不可变数据

不能直接使用setState来修改的数据,当你在state中定义的数据类型是引用数据类型的时候那么此数据就是一个不可变数据,不能直接操作,
需要先赋值给某个变量,在此变量进行操作,操作完之后使用setState来进行赋值,基本类似可不属于此范畴

十三.render中的state状态

两种定义数据的方式:

1.constructor里面定义数据 

特点:只能组件内部使用,数据是响应式,访问this.state.xxx

2.直接定义数据

特点:只能组件内部使用,数据不是响应式,视图不更新,访问this.xxx

十四.纯函数和高阶函数/高阶组件

纯函数:
    1.确定的输入,会有确定的输出

    2.不能产生副作用,修改原先的数据

高阶函数:
    1.函数参数是函数,或者返回值是函数

高阶组件(是一个函数):
    1.函数参数是组件,或者返回值是新组件的函数(是一个方法传递一个参数是组件,然后在返回一个组件,
变成新的组件)

十五.redux类似状态管理

    1.在src中views下新建文件夹redux

    2.下载初始化npm init

    3.在从redux下创建store文件夹

    4.从store文件夹新建index.js文件
    
    5.安装redux

        npm install redux --save 

    6.在index.js文件

        a.引入redux

            const {func} =require('prop-types')
            const{createStore} =require('redux')

        b.创建store实例

            const store=createStore(reducer)

        c.暴露函数

            module.exports=store 

        d.创建纯函数

            function reducer(){
                    return list
            }

    7.从redux下创建store文件夹,创建新的文件

        a.引入store

            const store=require('../store')

        b.使用getState获取到定义的数据

             console.log(store.getState())

十六.代理

1.安装:npm install --save-dev http-proxy-middleware

2.在src目录下创建一个setupProxy.js文件,在这个文件中配置

3.创建文件:src/setupProxy.js

const {createProxyMiddleware} =require('http-proxy-middleware')//按需导入
module.exports=function(app){
    app.use(
        '/api',
        createProxyMiddleware({
            target:'http://iwenwiki.com:3002',//真实地址,目标服务器
            changeOrigin:true,//运行跨越
            pathRewrite:{
                '/api':'',//路径重写
            }
        })
    )
}


十七.路由

1.非路由组件:直接添加到父组件里子组件

2.路由组件:通过路由跳转的组件(点击谁就会有切换的效果)

3.路由使用:

    1).下载:npm install --save [email protected] -S

    2).引用 import { HashRouter,BrowserRouter ,Route,Switch,Redirect} from "react-router-dom";

    3).HashRouter/BrowserRouter路由包裹容器

        区别:HashRouter地址栏里带#
              BrowserRouter:地址栏里带/

     4).在需要使用路由的地方引入

        import { Route, Routes, Navigate, Link } from "react-router-dom";

    5).使用Routes包裹定义路由走向路由规则 
        path:匹配路径 element:加载指定
         }>的组件
        
4.Switch只能匹配一个路由规则

使用BrowserRouter
 
       
         
       

     
 

5.路由重定向(Navigate )

    }>

    默认路由,使用重定向的方式Navigate导航,后面跟着to属性

6.404页面(配置在路由的最后)

    }>

7.嵌套路由(路由嵌套路由)

       

        }>

         

8.使用Link中的to进行跳转

排行榜

    

Outlet相当于路由视图窗口,起到一个占位符作用

9.useNavigate跳转并传值

10.useParams作用:回当前匹配路由的`params`参数

11.useSearchParams  用于读取和修改当前位置的 URL 中的查询字符串

十八.异步组件

1.在index.js中引入import { Suspense } from 'react'

2.使用lading.......

}>把App组件包裹起来

使用React.lazy的方式来定义组件,则代表此组件是一个异步组件

3. 将组件改成异步组件使用lazy写法:const 名称 = React.lazy(()=>import('组件路径'))

十九.hook的使用

    1.useState处理之后数据返回的是一个数组

    数组的第一个元素就是定义数据

    数组的第二个元素就是操作数据的函数

   2.useEffect只有组件渲染(render)的操作,那么渲染(render)完之后都会触发hook
    函数 第一个参数是一个函数(普通函数,箭头函数) 第二个参数是一个数组,此hook函数所依赖的数据
    (只有依赖数据变化的时候函数才会调用)
    相当于是挂载后
    数据的初始化
    只在页面初始化的时候触发一次,不依赖任何数据


redux的四个文件
index.js(store文件夹的主入口)
constant.js常量类型(action.type)文件
reduer.js纯函数文件
actionCreator.js(action创建者)


Fragments
空容器不会被渲染Fragments允许你将值列表分组,而无需向DOM添加节点包括空标签容器,不被渲染

编程式导航
配置路由this.props
history:路由跳转方式api方法,可以实现路由跳转js
location
match路由配置信息 包含路由path 路由参数 路由的url地址
this.props.history.goBack返回上一层
this.props.history.push('/path路径)跳转进入某一个路径 有返回功能/path/参数
this.props.history.replace("/path路径")跳转进入某一个路径并关闭当前页面,没有返回功能

1.安装vite+react

2.安装Ant Design组件库

npm install antd --save

3.axios封装

import axios from 'axios'//引入axios
const instance = axios.create({
    baseURL: '/',//请求路径
    timeout: 5000,//请求超时
});
// 添加请求拦截器
instance.interceptors.request.use((config) => {
    // 在发送请求之前做些什么
    config.headers['token'] = sessionStorage.getItem('token')
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
instance.interceptors.response.use((response) => {
    // 对响应数据做点什么
    return response;
}, (error) => {
    // 对响应错误做点什么
    return Promise.reject(error);
});
export default instance//暴露函数

开启代理

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: { // 本地开发环境通过代理实现跨域,生产环境使用 nginx 转发
      // 正则表达式写法
      '^/api': {
        target: 'http://192.168.1.56:8081', // 后端服务实际地址
        changeOrigin: true, //开启代理
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

login页面(html)

import React from 'react';
import { Button, Form, Input, Card } from 'antd';//引入antd样式组件
import { LockOutlined, UserOutlined } from '@ant-design/icons';//引入antd小图标
import { useNavigate } from "react-router-dom";//路由重定向
import { login } from '../api/login';//引入api封装
import { useDispatch } from "react-redux";
import { logins } from "../store/modules/loginSlice";
const App = () => {
    const dispatch = useDispatch()//调用dispatch更新数据
    const navigate = useNavigate()//路由重定向
    const onFinish = (values) => {
        login({ username: values.username, password: values.password }).then(res => {
            if (res.data.errmsg == "成功") {
                // let tokenData=res.data.data.token
                dispatch(logins(res.data.data.token))//从后台获取到token
                navigate('/home')//跳转到主页
            }
        })
    };
    const onFinishFailed = (errorInfo) => {
        console.log('Failed:', errorInfo);
    };
    return (
        // onFinish	提交表单且数据验证成功后回调事件 initialValues	表单默认值,只有初始化以及重置时生效
        // onFinishFailed	提交表单且数据验证失败后回调事件
        
} /> } />
); }; export default App;

login页面(api)

import http from "../utils/http";
export function login(data) {
    return http.request({
        url:'/api/admin/auth/login',
        method:'post',
        data
    })
}

跳转的路由 

import { Navigate } from "react-router-dom";
import Home from "../view/Home";
import Login from "../view/Login";

const router = [
    {
        path: '/',//路由重定向
        element: 
    },
    {
        path: '/home',
        element: ,
    },
    {
        path: '/login',
        element: 
    }
]

export default router

login页面存储token的状态管理(index文件)

// 引入configureStore函数创建一个store实例
import { configureStore } from "@reduxjs/toolkit";
import loginRefucer from "./modules/loginSlice";//引用模块
const store = configureStore({
    //模块合并
    reducer: {
        tokens: loginRefucer,
    }
})
export default store

login页面存储token的状态管理(modules模块文件)

import { createSlice } from "@reduxjs/toolkit";
const tokenSlice = createSlice({
    name: 'tokens',//模块的名称,唯一标识
    initialState: {
        tokenData: ''//状态管理定义数据的地方
    },
    reducers: {
        logins(state, { payload }) {
            state.tokenData = payload//请求到的数据赋值给定义的空变量
            console.log(state.tokenData, 111);
        }
    }
})
export const { logins } = tokenSlice.actions//导出actions函数
export default tokenSlice.reducer//导出reducer

mian.js文件xiao

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import 'antd/dist/reset.css';//引入antd框架
import { HashRouter } from "react-router-dom";//引入HashRouter模式路由
import { Provider } from "react-redux";// 从react-redux库中引入Provider
import store from "./store"; // 引入store(状态管理)
ReactDOM.createRoot(document.getElementById('root')).render(
  
    {/* 通过属性store的方式将store值展开,这样Provider组件就能接收到store中的数据,其内部的组件也可以拿到store中的状态 */}
    
      
    
  ,
)

效果图如下:

使用react实现后台管理系统项目_第1张图片

 home页面

import React, { useEffect, useState } from 'react';
import { Layout, Input, Button, Pagination, notification, Table, Modal, Form, Menu, theme, Breadcrumb } from 'antd';//样式
import {
  SearchOutlined, ArrowDownOutlined, CheckOutlined, SmileOutlined, MenuFoldOutlined, MenuUnfoldOutlined,
  UploadOutlined, UserOutlined, VideoCameraOutlined,
} from '@ant-design/icons';//按钮图标样式
const { Header, Footer, Content, Sider } = Layout;//布局容器
const { TextArea } = Input;
import { lists, deletes, creates, updates } from '../api/home';//api封装

const App = () => {
  const columns = [
    {
      title: '问题ID',
      align: 'center',//居中
      dataIndex: 'id',
    },
    {
      title: '问题内容',
      align: 'center',
      dataIndex: 'answer',//列表数据
      render: (text, row, index) => {//render	生成复杂数据的渲染函数,参数分别为 text当前行的值,row当前行数据,index行索引
        return (
          
{/* 当前bool为false显示span标签里面的数据 input标签中value显示输入框的数据 onChange输入框内容变化时的回调*/} {row.bool ? { editsData.answer = e.currentTarget.value; setEdit({ ...editsData }) }} /> : {row.answer}}
) } }, { title: '问题回复', align: 'center', dataIndex: 'question', render: (text, row, index) => { return (
{row.bool ?