前端 | React 学习笔记(基础篇)

302 阅读15分钟

前端 | React 学习笔记


image-20240327203820295.png

React 官方中文文档

1. 什么是React

React用于构建 Web 和原生交互界面的库,可以将数据渲染为HTML视图的开源JS

  React用来构建界面的JavaScript库,只提供了UI层面的解决方案,遵循组件设计模式,声明式编程规范和函数式编程概念。使用虚拟 DOM 来操作真实 DOM 来提高应用程序的效率。

2. React特点有哪些

  1. 采用组件化模式、声明式编程,提高开发效率以及组件的复用率。
  2. 在React Native 中可以使用React语法进行移动端开发。
  3. 使用虚拟DOM以及优秀的Diffing算法,尽量减少与真实DOM的交互。

3. React优点有哪些

  1. 提高了应用的性能
  2. 基于使用 JSX,代码的可读性更好

4. React 简单应用Hello, React

项目操作步骤:

image-20240421122806753.png

  • 编写Html页面,引入React 核心库、操作 DOM 的 react 扩展库、将 jsx 转为 js 的 babel 库
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>01_Hello React</title>
</head>
<body>
    <div id="test"></div>
    <script type="text/javascript" src="./js/16.9.0/react.development.js"></script>
    <script type="text/javascript" src="./js/16.9.0/react-dom.development.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script></script>
    <script type="text/babel">
        const visualDom = <h1>Hello,React</h1>
        ReactDOM.render(visualDom, document.querySelector("#test"))
    </script>
</body>
</html>

image-20240421123047810.png

  • 引入核心库等内容必不可少
  • script标签中的type=“text/babel”表示不编写原生的JavaScripttype="text/javascript"
  • ReactDOM.render(reactNode, domNode, callback?)参照官网方法 zh-hans.react.dev/reference/r…

项目执行结果:

  • 16.9.0版本的React执行结果与控制台输出

image-20240327212247880.png

  • 17.0.2版本的React执行结果与控制台输出

image-20240327212247880.png

  • 18.2.0版本的React执行结果与控制台输出

image-20240327213441659.png

image-20240327213415039.png

JSX(JavaScriptXML)

JSX(JavaScriptXML)是一种用于React项目的JavaScript语法扩展,并不是全新的编程语言,而是一个语法糖,使得编写React组件时的代码更加直观和易于维护。

  1. 定义虚拟DOM,不能使用双引号“”
  2. 标签中混入JS表达式的时候使用{}
  3. 样式的类名指定不能使用class,使用className
  4. 内敛样式要使用{{}}包裹,style={{color:'red'}}
  5. 不能有多个根标签,只能有一个根标签
  6. 标签必须闭合<div></div>,自闭合也行<input />
  7. 小写字母开头,就将标签转化为 html 同名元素,html 中无该标签对应的元素就报错
  8. 大写字母开头,react 就去渲染对应的组件,没有找到组件就报错

虚拟DOM

1. 认识虚拟DOM

虚拟DOM (Virtual DOM) 是一种轻量级的抽象,它允许我们在JavaScript中创建、更新和删除DOM元素。它是React等现代JavaScript框架的核心概念之一。

  • 虚拟DOM的工作原理是:当状态改变时,React会重新渲染整个DOM树,并比较新旧树的差异。然后,React只会更新实际DOM中发生变化的部分,而不是整个DOM树。

虚拟DOM的优点:

  1. 减少重绘的次数,提高性能。
  2. 跨平台,因为虚拟DOM可以在Web、服务器和本地应用中使用。
  3. 使用JSX,可以更容易地写出类似HTML的代码。

2. 使用JSX创建虚拟DOM

<script type="text/babel">
    const visualDom = <h1>Hello,React</h1>
    console.log(visualDom);
    ReactDOM.render(visualDom, document.querySelector("#test"))
</script>
  • 输出虚拟DOM,发现虚拟DOM是一个对象

image-20240327215338222.png

3. 使用JS创建虚拟DOM

<script type="text/babel">
    const visualDom2 =React.createElement('h1',{id:'title'},React.createElement('span',{},'hello,React'))
    console.log(visualDom2);
    ReactDOM.render(visualDom2, document.querySelector("#test"))
</script>

image-20240327215606001.png

  • JS写法在创建更多层级时,就会显得特别繁琐臃肿,不好维护。建议一直使用JSX的,符合书写习惯

Diff 算法

为了避免不必要的渲染,按需更新,虚拟DOM会采用Diff算法进行虚拟DOM节点比较,比较节点差异,从而确定需要更新的节点,再进行渲染。

image-20240421123614899.png

面试题:

动态表单的数据添加内容,在对应的源数据后的输入框输入内容

class Animal extends React.Component  {
​
    state = {
        formList: [{
            key: 1,
            name: '张三'
        }, {
            key: 2,
            name: '李四'
        }, ]
    }
​
    componentWillMount() {
        console.log("componentWillMount")
    }
​
    render() {
​
        const { formList } = this.state;
        console.log(formList)
        const addPerson = () => {
            this.setState({
                formList: [ ...[ { key:3, name: '王五' }], ...formList,]
            })
        }
​
        return <div>
            111
            <ul>
                {
                    formList.map((item, index)=>{
                        return <li key={index}>{item.name}<input /> </li>
                    })
                }
            </ul>
            <button onClick={ addPerson } >新增</button>
        </div>
    }
}

第一种结果:

image-20240421134406202.png

第二种结果:

image-20240421134458774.png

5.React组件

组件,React 内部创建组件的实例对象,调用 render() 得到虚拟DOM,并解析为真实的DOM,插入到指定元素内部。

注意:

  1. 组件名必须是首字母大写
  2. 虚拟DOM元素只能有一个根元素
  3. 虚拟DOM元素必须有结束标签 < />

1. 函数式组件

  • 通过定义函数,返回JSX的虚拟DOM的函数,称为函数式组件

2. 类组件

  • 通过继承(extends React.Component)定义的类,称为类组件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>02_函数组件与类组件</title>
</head>
<body>
    <div id="root"></div>
    
    <script type="text/javascript" src="./js/16.9.0/react.development.js"></script>
    <script type="text/javascript" src="./js/16.9.0/react-dom.development.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/babel">
        // 类组件
        class Animal extends React.Component  {
            render() {
                return <div>
                    动物园里有什么
                    <Dog />
                </div>
            }
        }
​
        // 函数组件
        function Dog() {
            return <div>
                狗:汪汪汪    
            </div>
        }
​
        ReactDOM.render( <Animal />, document.getElementById("root"));
    </script>
</body>
</html>

3. 受控组件

  • 受控组件:表单组件的输入组件随着输入并将内容存储到状态中(随时更新

    • onChange 回调方法,维护数据到状态中,类似VUE的双向绑定
<script type="text/babel">
    //创建组件
    class Login extends React.Component{
​
        //初始化状态
        state = {
            username:'', //用户名
            password:'' //密码
        }
​
        //保存用户名到状态中
        saveUsername = (event)=>{
            this.setState({username:event.target.value})
        }
​
        //保存密码到状态中
        savePassword = (event)=>{
            this.setState({password:event.target.value})
        }
​
        //表单提交的回调
        handleSubmit = (event)=>{
            event.preventDefault() //阻止表单提交
            const {username,password} = this.state
            alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
        }
​
        render(){
            return(
                <form onSubmit={this.handleSubmit}>
                    用户名:<input onChange={this.saveUsername} type="text" name="username"/>
                    密码:<input onChange={this.savePassword} type="password" name="password"/>
                    <button>登录</button>
                </form>
            )
        }
    }
    //渲染组件
    ReactDOM.render(<Login/>,document.getElementById('test'))
</script>

4. 非受控组件

  • 非受控组件:表单组件的输入组件的内容现用现取(即用即取

    • 通过form、ref等内容,由真实DOM获取数据,不维护到状态中
<script type="text/babel">
    //创建组件
    class Login extends React.Component{
        handleSubmit = (event)=>{
            event.preventDefault() //阻止表单提交
            const {username,password} = this
            alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
        }
        render(){
            return(
                <form onSubmit={this.handleSubmit}>
                    用户名:<input ref={c => this.username = c} type="text" name="username"/>
                    密码:<input ref={c => this.password = c} type="password" name="password"/>
                    <button>登录</button>
                </form>
            )
        }
    }
    //渲染组件
    ReactDOM.render(<Login/>,document.getElementById('test'))
</script>

5. 组件实例三大属性 state props refs

1. state

  • 组件的状态,也就是该组件所存储的数据
  • React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
定义 state

在类组件中定义statestate是对象({}

  • 在构造器中初始化state,(this.state={ version: '1.0.0'}
  • 在类中添加属性state来初始化,(state = { version: '1.0.0'}
使用 state
const { version } = this.state;
修改 state

在类组件中修改state,使用setState()方法,合并属性。

  • this.setState(partialState, [callback]);
    
    • partialState: 需要更新的状态的部分对象
    • callback: 更新完状态后的回调函数
  • 直接修改

this.setState({
    version: '1.1.0'
})
  • 依据原参数修改
// 传入一个函数,返回x需要修改成的对象,参数为当前的 state
this.setState(state => ({count: state.count+1});

2. props

  • state是组件自身的状态,而props则是外部传入的数据
定义 props
  • 通过在组件标签上传递值,在组件中就可以获取到所传递的值
  • 在构造器里的props参数里可以获取到 props
使用 props
const { name, age, sex } = this.props;
修改 props
  • 需要借助外部方法修改 props
检查 props
  • 凡是入参都会有校验,前端也不例外
  • 设置 propTypes定义props的规范,添加在类式组件的原型对象
  • 设置defaultProps定义props的默认值,添加在类式组件的原型对象
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>02_函数组件与类组件</title>
</head>
<body>
    <div id="root"></div>
    
    <script type="text/javascript" src="./js/16.9.0/react.development.js"></script>
    <script type="text/javascript" src="./js/16.9.0/react-dom.development.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/javascript" src="./js/prop-types.js"></script>
    <script type="text/babel">
        // 类组件
        class Animal extends React.Component  {
            render() {
                const { aniName, aniCount } = this.props;
                return <div>
                    动物园里有什么
                    <br/>
                    {`动物园里有 ${aniName},${aniCount}只`}
                </div>
            }
​
            static propTypes = {
                aniName: PropTypes.string.isRequired,
                aniCount: PropTypes.number
            }
​
            static defaultProps = {
                aniCount: 0
            }
​
        }
​
        // 函数组件
        function Dog() {
            return <div>
                狗:汪汪汪    
            </div>
        }
​
        ReactDOM.render( <Animal aniName='老虎'  />, document.getElementById("root"));
    </script>
</body>
</html>

注意:

  • 组件代码中增加propTypesdefaultProps

3. refs

  • Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。
何时使用Refs
  1. 管理焦点,文本选择或媒体播放。
  2. 触发强制动画。
  3. 集成第三方DOM库。

在React无法控制局面的时候就需要直接操作Refs了。

Refs有哪些使用方式
  1. 字符串形式的refs。(可能在以后的版本中弃用)

  2. 回调形式的refs。

    1. 组件实例的ref属性传递一个回调函数c => this.input1 = c(箭头函数简写),这样会在实例的属性中存储对DOM节点的引用,使用时可通过this.input1来使用
  3. 使用React.createRef()创建,并通过ref属性附加到React元素。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>02_函数组件与类组件</title>
</head>
<body>
    <div id="root"></div>
    
    <script type="text/javascript" src="./js/16.9.0/react.development.js"></script>
    <script type="text/javascript" src="./js/16.9.0/react-dom.development.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/javascript" src="./js/prop-types.js"></script>
    <script type="text/babel">
        // 类组件
        class Animal extends React.Component  {
​
            myRef = React.createRef() 
​
            showData=()=>{
                console.log(this.refs.input1)// 通过字符串形式拿到标签为input1的真实DOM
                console.log(this.refs.input1.value)
            }
​
            showDataFun =()=>{
                console.log(this.inputFun)// 通过回调函数方式拿到声明的实例属性 inputFun 拿到真实的DOM 
                console.log(this.inputFun.value)
            }
​
            showDataCreate =()=>{
                console.log(this.myRef) // 通过 cerateRef() 创建,拿到指定的 ref 的真实 DOM
                console.log(this.myRef.current.value)
            }
​
            render() {
                const { aniName, aniCount } = this.props;
                return <div>
                    动物园里有什么
                    <br/>
                    {`动物园里有 ${aniName},${aniCount}只`}
                    <div>
                        <input ref='input1' type="text" placeholder='点击按钮提示数据'/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>
                    </div>
                    <div>
                        <input ref={(currentNode)=>{this.inputFun=currentNode}} type="text" placeholder='点击按钮提示数据' />
                        <button onClick={this.showDataFun}>点我提示左侧数据</button>
                    </div>
                    <div>
                        <input ref={this.myRef} type="text" placeholder='点击按钮提示数据' />
                        <button onClick={this.showDataCreate}>点我提示左侧数据</button>
                    </div>
                </div>
            }
​
            static propTypes = {
                aniName: PropTypes.string.isRequired,
                aniCount: PropTypes.number
            }
​
            static defaultProps = {
                aniCount: 0
            }
​
        }
​
        // 函数组件
        function Dog() {
            return <div>
                狗:汪汪汪    
            </div>
        }
​
        ReactDOM.render( <Animal aniName='老虎'  />, document.getElementById("root"));
    </script>
</body>
</html>

image-20240328203413224.png

6. 事件处理

  1. React 使用的是自定义事件,而不是原生的 DOM 事件,通过onXxx属性指定时间处理函数
  2. React 的事件是通过事件委托方式处理的(为了更加的高效)
  3. 可以通过事件的 event.target获取发生的 DOM 元素对象,可以尽量减少 refs的使用
<input onChange={this.saveName} type="text" name="username" />
​
saveName = (event) => {
    this.setState({name: event.target.value});
}

6. 生命周期

  在 React 16、17与18中组件的生命周期有所不同,主要变化在于旧的生命周期被新的生命周期钩子函数所替代,并且新React版本中推荐使用函数组件的Hooks来替代组件的生命周期函数。

16 版本之前的生命周期

image-20240330191317181.png

image-20240330200028917.png

  1. 初始化阶段:由 ReactDOM.render() 触发,进行初次渲染

    • 检查组件中是否有默认的属性()、是否有属性校验()

    • constructor()

    • componentWillMount()

      • 组件即将挂载到页面上
    • render()

      • 渲染页面
    • componentDidMount()

      • 组件已经被挂载到页面中
  2. 更新阶段:由组件内部 this.setState() 或父组件render触发渲染

    • componentWillReceiveProps()

      • 父组件改变了props的值
    • shouldComponentUpdate()

      • state 被更改时触发,接收两个参数为 nextProps、nextStates
      • return false不更改,流程回到state被更改前
      • return true进行更改,state更改,流程继续
    • componentWillUpdate()

      • 执行conponentWillUpdate生命周期函数,告知组件即将开始进行更新
    • render()

    • componentDidUpdate()

      • 执行componentDidUpdate生命周期函数,告知组件更新并渲染完毕。
      • 此时更新过的组件已经渲染到页面中
  3. 卸载阶段:由ReactDOM.unmountComponentAtNode() 触发

    • componentWillUnmount()

      • 即将销毁组件
  4. 异常处理

    • componentDidCatch()

17 版本之后的生命周期

eg.17.0.2
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>04_旧版本生命周期</title>
</head>
<body>
    <div id="root"></div>
    
    <script type="text/javascript" src="./js/17.0.2/react.development.js"></script>
    <script type="text/javascript" src="./js/17.0.2/react-dom.development.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/javascript" src="./js/prop-types.js"></script>
    <script type="text/babel">
        class Count extends React.Component {
            constructor(props) {
                console.log("count-constructor")
                super(props)
                // 初始化状态
                this.state = {
                    count: 0
                }
            }
            // 将要挂载
            UNSAFE_componentWillMount() {
                console.log("UNSAFE_componentWillMount")
            }
            //挂载完毕
            componentDidMount() {
                console.log("componentDidMount")
            }
            // +1 按钮回调 
            add = () => {
                //获取原状态
                const { count } = this.state
                // 更新状态
                this.setState({ count: count + 1 })
            }
            // 卸载组件的回调
            death = () => {
                ReactDOM.unmountComponentAtNode(document.getElementById('root'))
            }
            // 组件将要卸载调用
            componentWillUnmount() {
                console.log("componentWillUnmount")
            }
            UNSAFE_componentWillReceiveProps (){
                console.log("UNSAFE_componentWillReceiveProps ")
            }
            // 控制组件更新的“阀门”
            shouldComponentUpdate() {
                console.log("shouldComponentUpdate")
                return true
            }
            // 组件将要更新的钩子
            UNSAFE_componentWillUpdate() {
                console.log("UNSAFE_componentWillUpdate")
            }
            // 组件更新完毕的钩子
            componentDidUpdate() {
                console.log("componentDidUpdate")
            }
            force = () => {
                this.forceUpdate()
            }
            render() {
                console.log("count-render")
                return (
                    <div>
                        <h1>当前求和为{this.state.count}</h1>
                        <button onClick={this.add}>点我+1</button>
                        <button onClick={this.death}>销毁</button>
                        <button onClick={this.force}>强制更新</button>
                    </div>
                )
            }
        }
​
        ReactDOM.render( <Count />, document.getElementById("root"));
    </script>
</body>
</html>
  • 初始阶段,仍使用componentWillMount()componentWillReceiveProps()componentWillUpdate()

image-20240330221619618.png

  • 初始阶段,使用新UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()

image-20240330221930702.png

  • 更新阶段

image-20240330221950871.png

image-20240330222008264.png

  • 卸载阶段

image-20240330222410325.png

18 版本的生命周期

eg.18.2.0
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>04_旧版本生命周期</title>
</head>
<body>
    <div id="root"></div>
    
    <script type="text/javascript" src="./js/18.2.0/react.development.min.js"></script>
    <script type="text/javascript" src="./js/18.2.0/react-dom.development.min.js"></script>
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/javascript" src="./js/prop-types.js"></script>
    <script type="text/babel">
        class Count extends React.Component {
            constructor(props) {
                console.log("count-constructor")
                super(props)
                // 初始化状态
                this.state = {
                    count: 0
                }
            }
            // 将要挂载
            UNSAFE_componentWillMount() {
                console.log("UNSAFE_componentWillMount")
            }
            //挂载完毕
            componentDidMount() {
                console.log("componentDidMount")
            }
            // +1 按钮回调 
            add = () => {
                //获取原状态
                const { count } = this.state
                // 更新状态
                this.setState({ count: count + 1 })
            }
            // 卸载组件的回调
            death = () => {
                // ReactDOM.unmountComponentAtNode(document.getElementById('root'))
                root.unmount();
            }
            // 组件将要卸载调用
            componentWillUnmount() {
                console.log("componentWillUnmount")
            }
            UNSAFE_componentWillReceiveProps (){
                console.log("UNSAFE_componentWillReceiveProps ")
            }
            // 控制组件更新的“阀门”
            shouldComponentUpdate() {
                console.log("shouldComponentUpdate")
                return true
            }
            // 组件将要更新的钩子
            UNSAFE_componentWillUpdate() {
                console.log("UNSAFE_componentWillUpdate")
            }
            // 组件更新完毕的钩子
            componentDidUpdate() {
                console.log("componentDidUpdate")
            }
            force = () => {
                this.forceUpdate()
            }
            render() {
                console.log("count-render")
                return (
                    <div>
                        <h1>当前求和为{this.state.count}</h1>
                        <button onClick={this.add}>点我+1</button>
                        <button onClick={this.death}>销毁</button>
                        <button onClick={this.force}>强制更新</button>
                    </div>
                )
            }
        }
​
        // ReactDOM.render( <React.StrictMode><Count /></React.StrictMode>, document.getElementById("root"));
        // ReactDOM.render( <Count />, document.getElementById("root"));
        const container = document.getElementById("root");
        const root = ReactDOM.createRoot(container);
        root.render(<Count />);
​
    </script>
</body>
</html>
  • 渲染无问题,按照内容修改

image-20240330222810925.png

image-20240330223033910.png

  • 卸载阶段问题与解决办法

image-20240330223143797.png

image-20240330223300731.png

7. 脚手架

1. 使用create-react-app创建react应用

脚手架介绍

  1. react提供了用于创建react项目的脚手架库:create-react-app
  2. 项目的整体架构为:react + webpack + es6 + eslint
  3. 包含了所有需要的配置,语法检查、JSX编译、devServer……

创建并启动项目

  • 安装 - 执行 - 启动
  1. 全局安装脚手架npm install -g create-react-app
  2. 使用命令创建项目create-react-app hello-react-01
  3. 进入项目目录并启动项目cd hello-react-01 && npm start

image-20240331121453900.png

image-20240331121546195.png

脚手架项目结构

  • public 静态资源文件夹
  • src 源码文件夹

2. VSCODE中react插件的安装

  • ES7 React/Redux/GraphQL/React-Native snippets

image-20240331122710249.png

3. 脚手架项目的应用

组件间数据传递

  • 在React中,组件间数据传递通常通过props(属性)和state(状态)来实现。
父传子

image-20240421135223094.png

子传父

image-20240421135307384.png

image-20240421135311755.png

消息订阅与发布

  1. 依赖库**PubSubJS**

  2. 下载npm install pubsub-js --save

  3. 使用

    • 引入 import PubSub from 'pubsub-js'
    • 订阅 PubSub.subscribe('add', function(data){})
    • 发布 PubSub.publish('add', data)

路由

单页面应用
  1. 单页Web应用(SPA,single page web application)
  2. 整个页面只有一个完整的页面
  3. 在页面只做局部更新
路由的理解
  1. 路由就是路径与组件的映射关系
  2. 后端路由:node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。
  3. 前端路由:浏览器的路径 path 改变时,路由的组件随着改变
路由使用react-router-dom
  1. 导航栏使用<Link to="/h1t">标题1</Link>
  2. 展示组件使用Route进行路径匹配<Route path="h1t" component={ H1title}/>
  3. 导航栏和展示组件需要在同一个Router中,可以是<BrowserRouter><HashRouter>
<BrowserRouter><HashRouter>的区别
  1. 底层原理不一样

    • <BrowserRouter> 使用H5的 history API,不兼容 IE9 及以下版本
    • <HashRouter> 使用的是 URL 的哈希值
  2. URL表现形式不一样

    • <BrowserRouter> 路径不带 #
    • <HashRouter> 路径带 #
  3. 刷新后对路由state参数的影响

    • <BrowserRouter> 无影响,state 保存在 history 对象中
    • <HashRouter> 刷新导致 state 参数丢失
路由组件与一般组件
  1. 写法不同

    • 路由组件:<Route path="/index" component={IndexPage} />
    • 一般组件:<Demo />
  2. 接收数据不同

    • 一般组件:依靠标签传递内容

    • 路由组件:默认有三个固定的属性

      • history:

        • go: f go(n)
        • goBack: f goBack()
        • goForward: f goForward()
        • push: f push(path, state)
        • replace: f replace(path, state)
      • location:

        • pathname: "/index"
        • search: ""
        • state: undefined
      • match:

        • params: {}
        • path: "/index"
        • url: "/index"
路由组件API
  • 通过<NavLink>可以实现路由切换的高亮显示,使用属性activeClassName可以指定高亮样式

  • 注册路由使用<Switch>包裹,使pathcomponent保持一对一的关系,并且可以提高路由匹配效率

  • 路由默认采用的是模糊匹配,可以开启严格匹配(路由上增加 exact={true}),一级路由开启严格匹配,可能会导致无法继续匹配二级路由

  • 使用<Redirect to="/index" />将页面重定向到指定的路由组件中

  • 嵌套路由

    • 注册子路由时,需要写上父路由的path
    • 路由的匹配是按照注册路由的顺序进行的
路由参数传递
  • params

    • 向路由组件传递参数

      • <Link to={``/home/message/${msg}``}>传递params参数</Link>
    • 路由组件的声明

      • <Route path="/home/message/:msg" />
    • 接收参数this.props.match.params;

  • search

    • 向路由组件传递参数

      • <Link to={``/home/message?msg=${msg}``}>传递search参数</Link>
    • 路由组件的声明(无需声明,正常注册即可)

      • <Route path="/home/message" />
    • 接收参数this.props.location.search

      • 参数传递与解析,需要借助querystring
      • 对象转urlencode编码qs.stringify(obj)
      • urlencode编码转对象qs.parse(str)
  • state 借助浏览器的history保存数据,刷新不丢失

    • 向路由组件传递参数

      • <Link to={{path: '/home/message', state: {msg: '消息1'}}>传递state参数</Link>

      • 路由组件的声明(无需声明,正常注册即可)

        • <Route path="/home/message" />
    • 接收参数this.props.location.state;

路由跳转的两种模式
  • push 放入栈顶,Link、NavLink 默认跳转模式
  • replace 替换栈顶 <Link replace />
编程式路由导航
  • 借助this.props.history对象的API,操作路由跳转、前进、后退
withRouter使用
  1. 让一般组件具备路由组件所持有特有的API

  2. 使用

    • 引入 import { withRouter } from 'react-router-dom';
    • 使用 export default withRouter(IndexPage)

状态管理库redux

认识redux
工作流程

image-20240331225145765.png

redux精简版
  1. 新建文件夹store,并创建文件store.jscount_reducer.js

  2. 编写store.js

    • 引入redux中的createStore函数,创建一个store对象
    • createStore函数调用时需要传入一个为其服务的reducer
  3. 编写count_reducer.js

    • reducer本质上是一个函数,入参为(preState, action)出参为状态结果
    • reducer进行初始化状态以及加工状态
    • reducer被第一次调用时,是store自动触发的,传递的preStateundefined
  4. index.js中检测store中状态的改变,一旦发生改变重新渲染<App />

    • redux 只负责状态管理,状态改变驱动页面的展示(重新渲染)需要我们写
import store from './redux/store'
import App from './App'
import ReactDOM from 'react-dom'
​
store.subscribe(() => {
    ReactDOM.render(<App />, document.getElementById('root'));
})
react-redux 基本使用

image-20240401223617663.png

  1. 容器组件与UI组件

    • UI组件:不能使用任何reduxapi,只负责页面的呈现、交互等
    • 容器组件:负责和redux通信,将结果交给UI组件
  2. 创建一个容器组件

    • connect(mapStateToProps, mapDispatchToProps)(UI组件)

      • mapStateToProps:映射状态,返回值是一个对象
      • mapDispatchToProps:映射操作状态的方法,返回值是一个对象
  3. 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入