React笔记

1. 创建react项目

// 1. 全局安装create-react-app
npm i create-react-app -g
// 2. 查看create-react-app是否安装成功
create-react-app -v
// 3. 创建react项目, 项目名自定义即可,下面以react-demo为例
npx create-react-app react-demo
// 4. 进入新创建的文件目录下
cd react-demo
// 5. 运行项目
npm strat

使用vite创建React项目(推荐)

// 1. 全局安装vite
npm i vite -g
// 2. 用vite创建React项目
npm create vite
// 3. 选项如下图,第三步根据需要选择js或ts
// 4. 进入新创建的文件目录下
cd react-demo
// 5. 运行项目
npm run dev
用vite创建React项目的选项

2. React基本语法

React使用JSX语法,JSX是一种JavaScript的语法扩展
语法:在大括号{}中直接插入可识别的内容

  1. 直接在大括号中使用字符串
  2. 直接在大括号中使用js变量
  3. 直接在大括号中插入函数方法
  4. 直接在大括号中js对象
const hello = "我是变量";

function print() {
  return "你好,react";
}

function App() {
  return (
    <div>
      {/* 1. 直接在{}中插入字符串 */}
      <div>{"hello react"}</div>
      {/* 2. 直接在{}中插入js变量 */}
      <div>{hello}</div>
      {/* 3. 直接在{}中插入函数或方法 */}
      <div>{print}</div>
      {/* 内置函数 */}
      <div>{new Date().getDate}</div>
      {/* 4. 直接在{}中插入js对象 */}
      <div style={{ color: "red" }}>{"hello react"}</div>
    </div>
  );
}
export default App

3. React列表渲染

  • map 循环数组结构,return结构
  • 注意点:循环时需要再标签上加上一个独一无二的key属性,可以是string和number
  • key的作用是:react内部使用,提升渲染性能
const list = [
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
  { id: 3, name: "王五" },
];

function App() {
  return (
      <div>
      <ul>
        {list.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

4. React条件渲染

  • 简单的条件判断可以使用三元运算符?;
  • 情况较多的条件可以定义一个函数,使用if-else或者switch-case判断后返回对应的标签
const flag = true;

const type = 0;
function getType() {
  if (type === 0) {
    return <div>类型为0</div>;
  } else if (type === 1) {
    return <div>类型为1</div>;
  } else {
    return <div>类型为其他</div>;
  }
}

function App() {
  return (
    <div>
      {/* &&语法,flag为true才显示 */}
      {flag && <div>flag为true时显示</div>}
      {/* 三元运算符 */}
      {flag ? <div>flag为true时显示</div> : <div>flag为false时显示</div>}
      {/* 多中情况,使用函数 */}
      {getType()}
    </div>
  );
}

export default App;

5. React事件绑定

function App() {
  // 基础事件绑定方式
  function click1() {
    console.log("基础事件绑定方式");
  }

  // 获取e参数
  function click2(e) {
    console.log("获取e参数", e);
  }

  // 获取自定义参数
  function click3(name) {
    console.log("获取自定义参数", name);
  }

  // 获取自定义参数和e参数
  function click4(name,e) {
    console.log("获取自定义参数和e参数",name,e);
  }
  return (
    <div>
      <button onClick={click1}>基础事件绑定方式</button>
      <br />
      <button onClick={click2}>获取e参数</button>
      <br />
      <button onClick={() => click3("张三")}>获取自定义参数</button>
      <br />
      <button onClick={(e) => click4("李四", e)}>获取自定义参数和e参数</button>
      <br />
    </div>
  );
}

export default App;

6. React组件

React组件就是以首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件只需要把组件当成标签书写

// 定义组件
function Son() {
  return <div>我是子组件</div>;
}

function App() {
  return (
    <div>
      我是父组件
      {/* 自闭和写法 */}
      <Son />
      {/* 成对标签写法 */}
      <Son></Son>
    </div>
  );
}

export default App;

7. useState状态变量

  • useState是一个React Hook函数,允许向组件添加一个状态变量,从而控制影响组件的渲染结果,即数据驱动视图,类似Vue3中的ref和reactive
  • 修改useState定义的值,只能使用setNum传入一个全新的值,改变旧值
import { useState } from "react";

function App() {
  const [num, setNum] = useState(0);
  const [data, setData] = useState({
    name: "Tom",
  });

  return (
    <div>
      {num}
      <button onClick={() => setNum(num + 1)}>加法</button>
      <hr />

      <div>{data.name}</div>
      <button
        onClick={() =>
          setData({
            ...data,
            name: "jock",
          })
        } 
      >
        改变姓名
      </button>
    </div>
  );
}

export default App;

8. React样式

  • 比较推荐给标签绑定className的样式写法
import './app.css'

const divStyle = {
  color: 'blue',
  fontSize:'25px'
}

function App() {
  return (
    <div>
      {/* 行内样式,必须在{}中包裹一个对象。不推荐这种写法 */}
      <div style={{ color: "red", fontSize: "30px" }}>这是一个div</div>
      {/* 定义一个样式对象,在style中传入一个对象 */}
      <div style={divStyle}>这是一个div</div>
      {/* 通过class类名添加样式,这种写法需要把样式单独写在一个css文件中,推荐写法 */}
      <div className="div-txt">这是一个div</div>
    </div>
  );
}

export default App;

9. 表单数据双向绑定

  • React中没有像Vue中的v-model指令实现数据的双向绑定
  • 可以用原生的方式实现
// 导入声明状态的方法
import { useState } from "react";

function App() {
  // 定义一个要给表单绑定的值和修改表单的方法
  const [text, setText] = useState("");
  return (
    <div>
      {/* 1. 通过value属性绑定input的状态 */}
      {/* 2. 绑定onChange事件,通过事件参数e获取输入框中最新的值 */}
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
    </div>
  );
}

export default App;

10. React中获取DOM

  • 与vue3中的ref写法类似
// 导入声明获取dom的方法
import { useRef } from "react";

function App() {
  // 定义一个用useRef生成的对象,绑定到dom元素标签上
  const buttonRef = useRef(null);
  return (
    <div>元素
      {/* 在dom可用时,通过生成的useRef对象的current来返回dom */}
      <button ref={buttonRef} onClick={() => console.log(buttonRef.current)}>
        获取按钮的dom元素
      </button>
    </div>
  );
}

export default App;

11. React组件通信

11.0. props类型或默认值设置

通过 组件名.propTypes来判断类型,通过安装npm i prop-types第三方库proptypes实现类型或非空校验
通过 组件名.defaultProps来设置默认值,注意:该方式目前主要用在类组件中,函数组件中会提示报错,函数组件中使用参数设置默认值的方法实现

import React from 'react'
// 函数组件设置默认值,通过参数设置
function Son({ val,txt='默认值' }) {
    return (
        <div>我是子组件
            <p>父组件传递的值:{val}</p>
            <p>默认值:{txt}</p>
        </div>
    )
}
// Son.propTypes,限制props传值的类型
Son.propTypes = {
    val: (props) => {
        if (typeof props.val !== 'string') {
            throw new Error('val 必须是一个字符串')
        }
    }
}

// Son.defaultProps,用在类组件中,可设置props的默认值,函数组件中控制台会报错
// Son.defaultProps = {
//  val: '我是默认值',
//  txt: '父组件没传,我就显示'
// }

export default function App() {
    const val = '你好'
    return (
        <>
            <div>我是父组件</div>
            <hr />
            <Son val={val} />
        </>
    )
}

第三方库proptypes的使用

import React from 'react'
// 导入校验props类型的库
import propTypes from 'prop-types'

// 函数组件设置默认值,通过参数设置
function Son(props) {
    return (
        <div>我是子组件
            <p>父组件传递的值:{props.val}</p>
        </div>
    )
}
// Son.propTypes,限制props传值的类型
Son.propTypes = {
    // 判断是否是字符串
    /**
     * propTypes可判断的类型:array、bool、func、number、object、string、symbol
     * propTypes.any.isRequired  表示传一个不为空的任意类型,any可替换为上面支持的类型
     * propTypes.oneOfType([propTypes.number,propTypes.string])  表示只能传数字和字符串类型的数据
     * propTypes.oneOf([1,10])  表示只能传数字1或10
     * propTypes.arrayOf(propTypes.number)或propTypes.objectOf(propTypes.number)  表示必须传一个都是数字类型的数组或属性为数字的对象
     * propTypes.shape({})  用于检测对象每一个属性的类型
     * (props, propName, componentName) => {// 复杂的类型判断} 在属性prop的类型检测中,属性值是一个函数,
     * 在这里props是包含prop的props对象,propName是prop的属性名,componentName是props所在的组件名称,函数的返回值是一个Error对象
     */
    val: propTypes.string.isRequired
    // val: propTypes.oneOfType([propTypes.number,propTypes.string])
    // val: propTypes.oneOf([1,10])
    // val: propTypes.arrayOf(propTypes.number)
    // val: propTypes.shape({
    //  name:propTypes.string,
    //  age:propTypes.number
    // })
    // val: (props, propName, componentName) => {
    //  // 复杂的类型判断
    // }
}


export default function App() {
    const val = 11
    return (
        <>
            <div>我是父组件</div>
            <hr />
            <Son val={val} />
        </>
    )
}

11.1. 父子组件通信

  • 父组件传递数据,子组件标签上绑定属性
  • 子组件接收数据,props的参数中获取到父组件传递的数据
  • props可以传递任意类型数据,包括数字、字符串、布尔值、数组、对象、函数、JSX
  • props是只读对象,不能直接修改传递的值,只能由父组件进行修改
  • 定义在子组件标签内部的内容,通过props.children可以获取到
// 父子组件通信

// 子组件
function Son(props) {
  return (
    <div>
      {props.text}
      <span style={{ color: "red" }}>{props.children}</span>
    </div>
  );
}

// 父组件
function App() {
  const text = "我是父组件传递的值";
  return (
    <div>
      <div>我是父组件</div>
      <Son text={text}>
        <span>子组件内部的内容</span>
      </Son>
    </div>
  );
}

export default App;

11.2. 子组件给父组件传值

  • 子传父可以通过父组件给子组件传递一个方法,子组件触发父组件传递的方法,并把想要传递的数据以参数的形式绑定在触发的父组件方法上,父组件接收即可
// 子组件给父组件传值

// 子组件
function Son(props) {
  const text = "我是子组件传递的值";
  return (
    <div>
      {/* 3. 子组件触发父组件定义的方法,把想要传递的数据,以参数的形式,传递给父组件 */}
      <button onClick={() => props.getValue(text)}>给父组件传值</button>
    </div>
  );
}

// 父组件
function App() {
  const [text, setText] = useState("");
  // 1. 父组件定义一个方法,把该方法绑定到子组件上
  function getSonMsg(value) {
    // 4. 子组件触发该方法时,接收传递过来的参数,并保存下来
    setText(value);
  }
  return (
    <div>
      <div>我是父组件</div>
      <div>{text}</div>
      {/* 2. 给子组件绑定父组件的方法 */}
      <Son getValue={getSonMsg} />
    </div>
  );
}

export default App;

11.3. 兄弟组件通信

  • 可以把父组件作为一个中间桥接,先通过11.2中的方法,把子组件A的值传给父组件,父组件接收后,在传给子组件B
// 兄弟组件传值

// 子组件A
function A(props) {
  const text = "传递给兄弟组件B的数据";
  return (
    <div>
      <div>我是子组件A</div>
      {/* 3. 子组件A触发父组件定义的方法,把想要传递的数据,以参数的形式,传递给父组件 */}
      <button onClick={() => props.getValue(text)}>给兄弟组件传值</button>
    </div>
  );
}

// 子组件B
function B(props) {
  return (
    <div>
      <div>我是子组件B</div>
      {/* 6. 接收来自子组件A的数据 -> 经过父组件 -> 最后到达子组件B */}
      <div> {props.text}</div>
    </div>
  );
}

// 父组件
function App() {
  const [text, setText] = useState("");
  // 1. 父组件定义一个方法,把该方法绑定到子组件A上
  function getSonMsg(value) {
    // 4. 子组件A触发该方法时,接收传递过来的参数,并保存下来
    setText(value);
  }
  return (
    <div>
      <div>我是父组件</div>
      <div>{text}</div>
      {/* 2. 给子组件A绑定父组件的方法 */}
      <A getValue={getSonMsg} />
      {/* 5. 父组件把接收到子组件A中的数据传递给子组件B */}
      <B text={text} />
    </div>
  );
}

export default App;

11.4. 爷组件传孙组件或父组件传子组件

  • 这个的方法类似于Vue3中的依赖注入,provide和inject
// 爷组件传孙组件或父组件传子组件

// 导入钩子函数
import { createContext, useContext } from "react";

// 1. 创建一个Context,一般首字母大写
const TextCtx = createContext();

// 子组件A
function A() {
  return (
    <div>
      <div>我是子组件A</div>
      <B />
    </div>
  );
}

// 孙组件B
function B() {
  // 3. 通过钩子函数useContext获取爷组件通过TextCtx.Provider传递过来的数据
  const text = useContext(TextCtx);
  return (
    <div>
      <div>我是孙组件B</div>
      {/* 4. 渲染爷组件传递过来的数据 */}
      <div>{text}</div>
    </div>
  );
}

// 爷组件
function App() {
  const text = "我是爷爷组件的数据";
  return (
    <div>
      {/* 2. 把需要接收数据的组件用TextCtx.Provider标签包裹起来,并绑定value属性 */}
      <TextCtx.Provider value={text}>
        <div>我是爷组件</div>
        <A />
      </TextCtx.Provider>
    </div>
  );
}

export default App;

12. useEffect副作用函数

使用useEffect,可以直接在函数组件内处理生命周期事件。 根据React class 的生命周期函数,可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

import {useEffect,useState} from 'react'

function App() {
    // useEffect副作用函数执行时机,也就是该函数的第二个参数不同的配置
    // 1. 没有依赖,也就是不传第二个参数,执行时机是:初始+数据更新
    useEffect(()=>{
        conosle.log('初始+数据更新')
    })
    
    // 2. 第二个参数传空数组,执行时机是:初始
    useEffect(()=>{
        // 类似于vue中的mounded函数,可以发起请求和操作dom
        conosle.log('初始')
    },[])
    
    // 3. 第二个参数传特定依赖项,执行时机是:初始+依赖项变化时
    const [count,setCount] = useState(0)
    useEffect(()=>{
        // 类似于vue中的watch监听
        conosle.log('初始+依赖项变化时')
    },[count])
    
  return (
    <div>
            <div>{count}</div>
            <button onClick={()=>setCount(count+1)}>加法</button>
    </div>
  );
}
export default App

清除副作用函数

import {useEffect, useState} from 'react';

function Son() {
    useEffect(() => {
        const timer = setInterval(() => {
            console.log('定时器执行中')
        })
        // 清除副作用函数,在卸载时执行
        return () => clearInterval(timer)
    }, []);

    return <div>我是子组件</div>;
}

function App() {
    const [show, setShow] = useState(true)
    return (
        <div>你好 react
            {show && <Son/>}
            <div>
                <button onClick={() => setShow(!show)}>隐藏子组件</button>
            </div>
        </div>
    )
}

export default App

13. useMemo和useCallback

  • useMemo是计算属性,用于缓存属性,类似于Vue中的computed
  • useCallback用于缓存方法
import React, { useCallback, useMemo, useState } from 'react'

export default function Home() {
    const [num, setNum] = useState(0)
    const text = '  你好'
    const add = () => {
        setNum(num + 1)
    }

    // 计算属性useMemo
    // 该hook的第二个参数是一个数组
    // 第二个参数,其中包含了需要监视变化的变量
    // 当数组中的属性发生变化时,useMemo 会重新执行计算函数,返回新的计算结果。
    // 如果数组中的属性没有发生变化,则直接使用缓存的计算结果。
    const newNum = useMemo(() => {
        console.log('useMemo');
        return num + text
    },[num])

    // useCallback, useMemo,useEffect第二个参数的用法都一样
    const add1 = useCallback(()=>{
        setNum(num + 1)
    })
    return (
        <div>
            <h2>{num}</h2>
            <button onClick={add}>加1</button>
            <button onClick={add1}>加1</button>
            <hr />
            <h2>{newNum}</h2>
        </div>
    )
}

14. 封装自定义Hook

封装Hook,目的是为了复用

  1. 定义一个use开头的函数
  2. 把逻辑定义到use的函数中,并以对象或数组的形式向外界return出去,供外界使用的属性或方法
  3. 在需要使用的地方,解构出自定义Hook中return出来的属性或方法,才可以使用
import {useState} from 'react';

// 封装自定义Hook,作用是可以多次复用
function useToggle() {
    const [show, setShow] = useState(true)
    const onToggle = () => setShow(!show)

    return {
        show,
        onToggle
    }
}

function App() {
    const {show, onToggle} = useToggle();
    return (
        <div>你好 react
            {show && <div>显示与隐藏</div>}
            <div>
                <button onClick={onToggle}>隐藏子组件</button>
            </div>
        </div>
    )
}

export default App

15. Redux状态管理

Redux 是一个小型的独立 JS 库。 但是,它通常与其他几个包一起使用:

React-Redux

Redux 可以集成到任何的 UI 框架中,其中最常见的是 React 。React-Redux 是我们的官方包,它可以让 React 组件访问 state 片段和 dispatch actions 更新 store,从而同 Redux 集成起来。

Redux Toolkit

Redux Toolkit 是我们推荐的编写 Redux 逻辑的方法。 它包含我们认为对于构建 Redux 应用程序必不可少的包和函数。 Redux Toolkit 构建是我们建议的最佳实践中,简化了大多数 Redux 任务,预防了常见错误,并使编写 Redux 应用程序变得更加容易。

Redux DevTools 扩展

Redux DevTools 扩展 可以显示 Redux 存储中状态随时间变化的历史记录。这允许你有效地调试应用程序,包括使用强大的技术,如“时间旅行调试”。

  • Redux是React最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架外运行
  • @reduxjs/toolkit是Redux官方推荐的工具库,是对 Redux 的二次封装,它提供了一些便捷的API和工具,帮助开发者更快速地编写Redux代码
  • react-redux是Redux官方提供的React绑定库,它提供了一些React Hooks和组件,用于在React组件中访问Redux store和派发Action

15.1 同步代码

  1. 安装工具库
// 安装工具库
npm i @reduxjs/toolkit react-redux
  1. 在src文件夹创建store文件夹,在企业开发中,不同的功能会单独创建一个文件,统一放在modules目录中,最后整合到index文件中,统一导出即可。目录如下


    image.png
  2. 创建countStore.js文件

import { createSlice } from '@reduxjs/toolkit'

const countsStore = createSlice({
    // 名称
    name: 'counts',
    // 初始化state,用于存储数据状态
    initialState: {
        count: 0
    },
    // 修改状态的方法,支持直接修改,同步方法
    reducers: {
        increment(state) {
            state.count++
        },
        decrement(state) {
            state.count--
        },
        // 通过actions.payload获取传递来的参数
        addToNumber(state, actions) {
            state.count += actions.payload
        }
    }
})

// 从仓库countsStore的actions中解构出对应的方法
const { increment, decrement, addToNumber } = countsStore.actions

// 从仓库countsStore的reducer中获取reducers
const reducers = countsStore.reducer

// 按需导出方法
export { increment, decrement, addToNumber }

// 默认导出reducers
export default reducers
  1. index.js文件
import {configureStore} from '@reduxjs/toolkit'

// 导入子模块的reducer
import countsReducer from './modules/countStore'

const store = configureStore({
    reducer:{
        counts:countsReducer
    }
})

// 导出store,供所有组件使用
export default store
  1. 为React注入store
    react-redux负责把Redux和React连接起来,内置Provider组件通过store参数把创建好的store实例导入到入口文件中

入口文件index.js或main.jsx文件

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

// 导入store
import store from './store'
import {Provider} from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>,
)
  1. 在React组件中使用store中的数据和方法
  • 使用react-redux中的useSelector方法,可以使用store中的数据
  • 使用react-redux中的useDispatch方法,可以调用store中的方法
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement,addToNumber } from "./store/modules/countStore.js";

function App() {
    const { count } = useSelector(state => state.counts);
    const dispatch = useDispatch();
    return (
        <div>
            <button style={{ marginRight: '10px' }} onClick={() => dispatch(decrement())}>减法</button>
            <span>{count}</span>
            <button style={{ marginLeft: '10px' }} onClick={() => dispatch(increment())}>加法</button>
            <button style={{ marginLeft: '20px' }} onClick={() => dispatch(addToNumber(20))}>每次加20</button>
        </div>
    )
}

export default App

15.2 异步代码

  1. 安装工具库
// 安装工具库
npm i axios
  1. 在src文件夹创建store文件夹,在企业开发中,不同的功能会单独创建一个文件,统一放在modules目录中,最后整合到index文件中,统一导出即可。目录如下


    image.png
  1. 创建txtListStore.js文件
import { createSlice } from "@reduxjs/toolkit";
import axios from 'axios'

const txtListStore = createSlice({
    name: 'txtList',
    initialState: {
        txtList: []
    },
    reducers: {
        // 定义同步方法,修改txtList的值
        getTxtList(state, actions) {
            state.txtList.unshift(actions.payload)
        }
    }
})

const { getTxtList } = txtListStore.actions

// 定义异步方法
const reqGetTxtList = () => {
    return async (dispatch) => {
        const { data: { content } } = await axios.get('https://api.uomg.com/api/rand.qinghua')
        
        // 调用同步方法,把获取的异步数据,传送过来,并修改state的状态
        dispatch(getTxtList(content))
    }
}
// 从仓库txtListStore的reducer中获取reducers
const reducers = txtListStore.reducer

// 导出异步方法
export { reqGetTxtList }

// 默认导出reducers
export default reducers
  1. index.js文件
import { configureStore } from '@reduxjs/toolkit'

// 导入子模块的reducer
import countsReducer from './modules/countStore'
import txtListReducer from './modules/txtListStore'


const store = configureStore({
    reducer: {
        counts: countsReducer,
        txtList: txtListReducer
    }
})

// 导出store,供所有组件使用
export default store
  1. 为React注入store
    react-redux负责把Redux和React连接起来,内置Provider组件通过store参数把创建好的store实例导入到入口文件中

入口文件index.js或main.jsx文件

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

// 导入store
import store from './store'
import {Provider} from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>,
)
  1. 在React组件中使用store中的数据和方法
  • 使用react-redux中的useSelector方法,可以使用store中的数据
  • 使用react-redux中的useDispatch方法,可以调用store中的方法
  • 可选择使用useEffect方法,在页面初始化时,调用异步方法,获取数据
import { useSelector, useDispatch } from "react-redux";
import { reqGetTxtList } from "./store/modules/txtListStore.js";
import { useEffect } from "react";

function App() {
    const dispatch = useDispatch();
    const { txtList } = useSelector(state => state.txtList)
    // 使用useEffect,在页面初始化时,调用异步请求,获取数据
    useEffect(() => {
        dispatch(reqGetTxtList())
    }, [reqGetTxtList])

    return (
        <div>
            <h3>土味情话列表</h3>
            <button style={{ marginLeft: '20px' }} onClick={() => dispatch(reqGetTxtList())}>获取情话</button>
            <ul>
                {txtList.map((item,index)=>(<li key={index}>{item}</li>))}
            </ul>
        </div>
    )
}

export default App

16. React-router路由

16.1 安装路由插件

npm i react-router-dom

16.2 常用路由标签components

  • <BrowserRouter> 浏览器模式,不带#的路径
  • <HashRouter> 哈希模式,带#的路径
  • <Routes>和<Route>路由出口,需要配合使用,Routes必须包裹着Route,Route中的caseSensitive可以设置路径是否区分大小写
  • <Link>路由链接,不高亮
  • <NavLink>路由链接,高亮
  • <Navigate>重定向,必须设置to,有push和replace两种跳转模式,默认push
  • <Outlet>嵌套路由的出口

16.3 基本使用,下面配置只是其中一种方式,并不是最优方式

16.3.1 在src目录下创建routes/index.js文件夹


路由表routes/index.js文件配置如下

import Home from "../pages/home";
import Library from "../pages/library";
import App from "../App";

import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'home', element: <Home /> },
            { path: 'library', element: <Library /> },
        ]
    }
])

export default router

16.3.2 在入口文件index.js或main.js文件中添加路由表配置

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import { RouterProvider } from 'react-router-dom'
// 导入路由表
import router from './routes'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
    <RouterProvider router={router}>
        <App />
    </RouterProvider>
)

16.3.3 调整App文件

import React from 'react'
import { Outlet, NavLink } from 'react-router-dom'

const linkStyle = {
    display: 'block',
    marginTop: '20px',
    border: '1px soild black'
}
const navStyle = {
    width: '200px',
    height: '100vh',
    algin: 'center',
    backgroundColor: '#c9c9c9'
}

export default function App() {
    return (
        <div style={{ display: 'flex' }}>
            <div style={navStyle}>
                <NavLink style={linkStyle} to='/home'>Home</NavLink>
                <NavLink style={linkStyle} to='/library'>Library</NavLink>
            </div>
            <Outlet />
        </div >
    )
}

16.4 三种路由传参及接收参数

使用const navigate = useNavigate()的navigate方法,实现编程式路由导航

也可参考更详细的配置:摘抄路径为 https://juejin.cn/post/7203156544878542904

  1. 动态路由传参
    • 路由表中的路径格式需要配置为/home/:xx/:xx
    • 在useParams中结构出传递的参数
  2. search传参
    • 路由路径不用额外配置
    • 在useSearchParams中结构出两个方法来获取参数和修改参数
  3. state传参
    • 第二个参数是一个配置对象,{replace:true|false,state:{}}
    • 在useLocation中结构出state对象,state对象就是传递的参数
    • replace用来是否可以返回上页历史
    • state用来键值对传参

App.js文件

import React from 'react'
import { Outlet, useNavigate } from 'react-router-dom'

const navStyle = {
    width: '200px',
    height: '100vh',
    algin: 'center',
    lineHeight: '30px',
    backgroundColor: '#c9c9c9'
}

export default function App() {
    // 定义编程式的hook
    const navigate = useNavigate()

    // 动态路由传参,路由表的路径格式为/home/:xx/:xx
    // 在useParams中结构出传递的参数
    const goHome = () => {
        navigate('/home/123/张三')
    }
    // search传参,路由路径不用额外配置
    // 在useSearchParams中结构出两个方法来获取参数和修改参数
    const goAbout = () => {
        navigate('/about/?id=999&name=王五')
    }
    // state传参,第二个参数是一个配置对象,{replace:true|false,state:{}}
    // replace用来是否可以返回上页历史
    // state用来键值对传参
    const goLibrary = () => {
        navigate('/library', { state: { id: '01', bookName: '三毛流浪记' } })
    }
    return (
        <div style={{ display: 'flex' }}>
            <div style={navStyle}>
                <button onClick={goHome}>跳转到Home,动态传参</button>
                <button onClick={goAbout}>跳转到About,Search传参</button>
                <button onClick={goLibrary}>跳转到Library,state传参</button>
            </div>
            <Outlet />
        </div >
    )
}

routes/index.js路由表配置文件

// 导入路由组件
import Home from "../pages/home";
import Library from "../pages/library";
import About from "../pages/about";
import App from "../App";

// 404路由组件
import NotFound from "../pages/notFound";

// 导入路由相关方法
import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'home/:id/:name', element: <Home /> },
            { path: 'library', element: <Library /> },
            { path: 'about', element: <About /> },
            { path: '*', element: <NotFound /> }
        ]
    }
])

export default router

home.js路由文件

import React from 'react'
import { useParams } from 'react-router-dom'

export default function Home() {
    // 通过动态路由传递的参数,可以在useParams中结构
    const { id, name } = useParams()
    return (<div>
        <h1>Home主页</h1>
        <hr />
        <h4>路由动态传参,并接收参数</h4>
        <h6>id: {id}</h6>
        <h6>姓名: {name}</h6>
    </div>)
}

about.js路由文件

import React from 'react'
import { useSearchParams } from 'react-router-dom'

export default function About() {
    // 通过search路由传递的参数,可以在useSearchParams中结构出两个方法
    const [searchParams, setSearchParams] = useSearchParams()
    // 调用searchParams的get方法,获取对应的参数
    const id = searchParams.get('id')
    const name = searchParams.get('name')

    return (<div>
        <h1>About关于</h1>
        <hr />
        <h4>search传参,并接收参数</h4>
        <h6>id: {id}</h6>
        <h6>姓名: {name}</h6>
        {/* setSearchParams可以响应式的修改search传递的参数 */}
        <button onClick={()=>setSearchParams({id:'333',name:'小明'})}>修改search传递的参数</button>
    </div>)
}

library.js路由文件

import React from 'react'
import { useLocation } from 'react-router-dom'

export default function Library() {
    // 通过state路由传递的参数,可以在useLocation中结构出state对象
    const { state } = useLocation()
    // state对象就是通过对象传参的内容
    const { id, bookName } = state
    return (<div>
        <h1>Library图书馆</h1>
        <hr />
        <h4>state路由传递的参数,并接收参数</h4>
        <h6>id: {id}</h6>
        <h6>图书名称: {bookName}</h6>
    </div>)
}

16.5 利用React高阶组件,重定向路由

  • 如果没有登录,通过高阶组件,重定向到登录页面

高阶组件withLogin.js文件

import React from 'react'
import { Navigate } from 'react-router-dom'

// 企业项目中,是否登录标识一般从redux中获取
const isLogin = false
// Comp接收一个组件
const WithLogin = (Comp) => {

    // 如已经登录,则放行
    if (isLogin) return Comp

    // 没登录时,重定向到登录页面
    return <Navigate to='/login' replace />
    
}

export default WithLogin

routes/index.js

// 导入路由组件
import Home from "../pages/home";
import Library from "../pages/library";
import About from "../pages/about";
import Login from "../pages/login";
import App from "../App";

// 导入控制登录鉴权的高阶组件
import WithLogin from "../component/withLogin";

// 404路由组件
import NotFound from "../pages/notFound";

// 导入路由相关方法
import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'login', element: <Login /> },
            { path: 'home/:id/:name', element: <Home /> },
            { path: 'library', element: (WithLogin(<Library />)) },
            { path: 'about', element: <About /> },
            { path: '*', element: <NotFound /> }
        ]
    }
])

export default router

17. React高阶组件

  • 高阶组件(HOC)是 React 中用于重用组件逻辑的高级技术。 HOC 本身不是 React API 的一部分。 它们是从 React 构思本质中浮现出来的一种模式。
  • 高阶组件是一个函数,能够接受一个组件并返回一个新的组件
  • React的高阶组件类似于Vue中的混入mixins
  • 用途:登录鉴权操作、增强props、扩展组件公共方法...

例:增强props的用法

  1. 创建hoc文件夹,创建withNum.js文件
    withNum.js文件
import React, { useState } from 'react'

// 增强props的高阶组件
export default function withNum(Conm) {
    return (props) => {
        // 定义state
        const [num, setNum] = useState(0)
        // 定义加法函数
        const add = () => {
            setNum(num + 1)
        }
        // {...props} 表示把组件原本的props还传给组件
        return <Conm num={num} add={add} {...props} />
    }
}
  1. 把需要使用高阶组件的普通组件,传给高阶组件
    addNum.js文件
import React from 'react'
import withNum from '../hocs/withNum'

function ClickCounter(props) {
    return (
        <div>
            {/* 父组件传递的值 */}
            <h1>{props.text}</h1>
            {/* 通过props接收到高阶组件中定义的属性和方法 */}
            <p>{props.num}</p>
            <button onClick={props.add}>加法</button>
        </div>
    )
}

export default withNum(ClickCounter)
  1. 把addNum组件导入到需要使用的位置
import React from 'react'
import ClickCounter from './addNum'

export default function Home() {
    const text = 'Hello React'
    return (<div>
        <ClickCounter text={text} />
    </div>)
}

18. 类组件中的生命周期

  • 类组件中分为三个阶段:挂载、更新、卸载
image.png

常用生命周期钩子

  1. 挂载阶段
钩子函数 触发时机 作用
constructor 创建组件时,最先执行,初始化的时候只执行一次 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
render 每次组件渲染都会触发 渲染UI(注意: 不能在里面调用setState() )
componentDidMount 组件挂载(完成DOM渲染)后执行,初始化的时候执行一次,在浏览器更新视图之前调用 1. 发送网络请求 2.DOM操作
  1. 更新阶段
钩子函数 触发时机 作用
getDerivedStateFromProps(nextProps,preState) 在初始化和更新时都会被调用 用于根据属性的变化计算并返回新的状态的生命周期方法,适用于 state 的值在任何时候都取决于 props 的情况
shouldComponentUpdate(nextProps, nextState) 在组件更新之前被调用 可以控制组件是否进行更新, 返回 true 时组件更新, 返回 false 则不更新。在这个阶段可以拿到上一个状态Dom元素的坐标、大小等相关信息
componentWillUpdate 在DOM更新之前触发 用于处理更新前的一些操作
render 每次组件渲染都会触发 渲染UI(注意: 不能在里面调用setState() )
componentDidUpdate DOM和状态更新后触发,参数包括上一个状态的值 可用于比较新旧状态并进行相应的操作
  1. 卸载阶段
钩子函数 触发时机 作用
componentWillUnmount 组件卸载(从页面中消失) 执行清理工作(比如:清理定时器等)

19. React的性能优化

请参考这篇文章:https://blog.csdn.net/m0_65121454/article/details/132366339

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容