React基础篇——16.0-17.0版本

本文介绍了React的基础知识,包括React的简介、入门案例、JSX语法规则,深入探讨了面向组件编程,如函数式组件、类式组件、state和props的使用,以及组件生命周期和key的作用。文章适合初学者了解和学习React的基本概念和技术要点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. React 简介

1. react是什么

React 用于构建用户界面 JS 库,是一个将数据渲染为 HTML 试图的开源 JS 库。

2. 为什么学:

  • 原生 JS 操作 DOM 繁琐,效率低
  • 使用 JS 直接操作 DOM,浏览器会进行大量的重绘重排
  • 原生 JS 没有组件化编码方案,代码复用低。

二. React入门

1. 基础案例

创建虚拟 DOM,渲染虚拟 DOM 到页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>
    <div id="demo"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建虚拟 DOM
    const VDOM = (
        <h1 id="title">
            <span>Hello,React</span>    
        </h1>
    ) /* 此处一定不要写引号,因为不是字符串 */
    // 2.渲染虚拟 DOM 到页面
    ReactDOM.render(VDOM,document.getElementById('test'))
    const TDOM = document.getElementById('test')
    console.log('虚拟DOM',VDOM)    
    console.log('真实DOM',TDOM)   
    debugger;                             
    console.log(typeof VDOM)
    console.log(VDOM instanceof Object)

    // 关于虚拟DOM
    // 1.本质是Object类型的对象(一般对象)
    // 2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么的属性。
    // 3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
    </script>
</body>
</html>

2. JSX语法规则

1. 定义虚拟 DOM 时,不要写引号。

2. 标签中混入 JS 表达式时要用{}。

3. 样式的类名指定不要用 class,要用 className 。

4. 内联样式,要用 style={{key:value}} 的形式去写

5. 只有一个跟标签

6. 标签必须闭合

7. 标签首字母

  • 若小写字母开头,则将该标签转为 html 中同名元素,若 html 中无该标签对应的同名元素,则报错。
  • 若大写字母开头,react 就去渲染对应的组件,若组件没有定义,则报错。

示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    /* 
        一定要注意区分:【js语句(代码)}】 与 【js表达式】
            1.表达式:一个表达式会产生一个值,可以放在任何一个需求值的地方
                下面这些都是表达式:
                (1). a
                (2). a+b
                (3). demo(1)
                (4). arr.map()
                (5). function test () {}
            2.语句(代码):
                下面这些都是语句(代码):
                    (1).if(){}
                    (1).for(){}
                    (1).switch(){case:xxx}
    */

    // 模拟一些数据
    const data = ['Angular','React','Vue']
    // 1.创建虚拟 DOM
    const VDOM = (
        <div>
            <h1>demo</h1>
            <ul>
                {
                    data.map((item, index) => {
                        return <li key={index}>{item}</li>
                    })
                }
            </ul>
        </div>
    )
    // 2.渲染虚拟DOM到页面
    ReactDOM.render(VDOM,document.getElementById('test'))
    </script>
</body>
</html>

3. 模块化与组件、模块化与组件化的理解

1. 模块

  1. 理解:向外提供特定功能的 JS 程序,一般就是一个 JS 文件
  2. 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂
  3. 作用:复用 JS,简化 JS 的编写,提高 JS 运行效率

2. 组件

  1. 理解:用来实现局部功能效果的代码和资源的集合(html / css / js / image 等等)
  2. 为什么:一个界面的功能更复杂
  3. 作用:复用编码,简化项目编码,提高运行效率

3.模块化

当应用的 JS 都以模块化来编写的,这个应用就是一个模块化的应用

4.组件化

当应用是以多组件的方式实现,这个应用就是一个组件化的应用


三. React 面向组件编程

1. 基本理解和使用

1. 函数式组件

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>函数式组件</title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建函数式组件
    function MyComponent () {
        console.log(this) //此处的this是undefined,因为babel编译后开启了严格模式
        return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2> //函数必须有返回值
    }
    // 2.渲染组件到页面
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))

    /* 
        执行了ReactDOM.render(<MyComponent/>,document.getElementById('test'))之后发生了什么?
            1.React解析组件标签,找到了MyComponent组件。
            2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

    */
    </script>
</body>
</html>

类的继承:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        /* 
            总结:
                1.类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
                2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
                3.类中所定义的方法,都是放在了类的原型对象上,供实例去使用。
        */

        // 创建一个Person类
        class Person {
            // 构造器方法
            constructor (name ,age) {
                // 构造器中的this是谁? ———— 类的实例对象性
                this.name = name
                this.age = age
            }
            // 一般方法
            speak () {
                // speak 方法放在了哪里? ———— 类的原型对象上,供实例使用
                // 通过Person实例调用speak时,speak中的this就是Person实例
                console.log(`我叫${this.name},我年龄是${this.age}`)
            }
        }
        
        // 创建一个Student类,继承与Person类
        class Student extends Person {
            constructor (name,age,grade) {
                super(name,age)// super 必须放在最前面
                this.grade = grade
                this.school = 'xxx学院'
            }
            // 重写从父类继承过来的方法
            speak () {
                console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}`)
            }
            // study 方法放在了哪里? ———— 类的原型对象上,供实例使用
            // 通过Person实例调用study时,study中的this就是Student实例
            study () {
                console.log('我很努力的学习')
            }
        }

        const s1 = new Student('yzh',25,'大四')
        console.log(s1)
        s1.speak()
        s1.study()
    </script>
</body>
</html>

2.类式组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建类式组件
    /* 
        注意:
            1.必须继承父类
            2.必须要写render
            3.render必须有返回值
    */
    class MyComponent extends React.Component {
        render () {
        //render是放在哪里的?———— MyComponent的原型对象上,供实例使用。
        //render中的this是谁?———— MyComponent的实例对象 <=> MyComponent组件实例对象
            console.log('render中的this',this)
            return <h1>我是用类定义的组件(适用于【复杂组件】的定义)</h1>
        }

    }

    // 2.渲染组件到页面
    ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    /* 
        执行了ReactDOM.render(<MyComponent/>,document.getElementById('test'))之后发生了什么?
        1.React解析组件标签,找到了MyComponent组件。
        2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
        3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

    */
    </script>
</body>
</html>

2. 组件实例三大属性

1. state

  • state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
  • 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

强力注意:

1. 组件中render方法中的this为组件实例对象

2. 组件自定义的方法中this为undefined,如何解决?

  • 强制绑定this:通过函数对象的bind()
  • 箭头函数

3. 状态数据,不能直接修改或更新

基础标准写法示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建组件
    class Weather extends React.Component{
        // 构造器调用几次? ———— 1次
        constructor (props) {
            console.log('constructor')
            super(props)
            // 初始化状态
            this.state = {
                isHot:true,
                wind:'微风'
            }
            // 解决changeWeather中this只想问题
            this.changeWeather = this.changeWeather.bind(this)
        }

        // render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
        render () {
            console.log('render')
            // 读取状态
            const { isHot, wind } = this.state
            return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷' },{wind}</h1>
        }

        // changeWeather调用几次? ———— 点几次调几次
        changeWeather () {
            console.log('changeWeather')
            // changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
            // 由于changWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
            // 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined

            // 获取原来的isHot值
            const isHot = this.state.isHot
            // 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换
            this.setState({
                isHot:!isHot
            })

            // 严重注意:状态(state)不可直接更改,下面这行就是直接更改。
            // this.state.isHot = !isHot //这是错误的写法
        }
    }
    // 2.渲染组件到页面
    ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>
</body>
</html>

简写方式示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建组件
    class Weather extends React.Component{
        //初始化状态
        state = { isHot:true, wind:'微风' }

        render () {
            const { isHot, wind } = this.state
            return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '寒冷' },{wind}</h1>
        }

        // 自定义方法 ———— 要用赋值语句的形式+箭头函数
        changeWeather = () => {
            const isHot = this.state.isHot
            this.setState({ isHot:!isHot })
        }
    }
    // 2.渲染组件到页面
    ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>
</body>
</html>

2. props

理解:

  1. 每个组件对象都会有props(properties的简写)属性
  2. 组件标签的所有属性都保存在props中

作用:

  1. 通过标签属性从组件外向组件内传递变化的数据
  2. 注意:组件内部不要修改props数据

基础标准写法示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test1"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建组件
    class Person extends React.Component{
        render () {
            console.log(this)
            const { name, sex, age } = this.props 
            //props是只读的
            // this.props.name = 'jack' //此行代码会报错,因为props是只读的
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age+1}</li>
                </ul>
                
            )
        }
    }

    // 对标签属性进行类型、必传的限制
    Person.propTypes = {
        name:PropTypes.string.isRequired,//限制name必传,且为字符串
        sex:PropTypes.string,//限制sex为字符串
        age:PropTypes.number,//限制age为字符串
        speak:PropTypes.func//限制speak为函数
    }

    // 指定默认标签属性值
    Person.defaultProps = {
        sex:'男',//sex默认值为男
        age:18//age默认值为18
    }

    // 2.渲染组件到页面
    ReactDOM.render(<Person name="yzh" speak={speak}/>,document.getElementById('test1'))
    ReactDOM.render(<Person name="yyy" sex="女" age={20}/>,document.getElementById('test2'))

    const info = {name:'叶', sex:'男', age:25}
    ReactDOM.render(<Person {...info}/>,document.getElementById('test3'))

    function speak () {
        console.log('123')
    }
    </script>
</body>
</html>

简写方式示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="test1"></div>
    <div id="test2"></div>
    <div id="test3"></div>

    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作 DOM -->
    <script type="text/javascript" src="../js/react-dom.development.js"></script>
    <!-- 引入 babel,用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <script type="text/babel"> /* 此处一定要写 babel */
    // 1.创建组件
    class Person extends React.Component{
        // 对标签属性进行类型、必传的限制
        static propTypes = {
            name:PropTypes.string.isRequired,//限制name必传,且为字符串
            sex:PropTypes.string,//限制sex为字符串
            age:PropTypes.number,//限制age为字符串
            speak:PropTypes.func//限制speak为函数
        }

        // 指定默认标签属性值
        static defaultProps = {
            sex:'男',//sex默认值为男
            age:18//age默认值为18
        }
        render () {
            console.log(this)
            const { name, sex, age } = this.props 
            //props是只读的
            // this.props.name = 'jack' //此行代码会报错,因为props是只读的
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age+1}</li>
                </ul>
                
            )
        }
    }

    // 2.渲染组件到页面
    ReactDOM.render(<Person name="yzh" speak={speak}/>,document.getElementById('test1'))
    ReactDOM.render(<Person name="yyy" sex="女" age={20}/>,document.getElementById('test2'))

    const info = {name:'叶', sex:'男', age:25}
    ReactDOM.render(<Person {...info}/>,document.getElementById('test3'))

    function speak () {
        console.log('123')
    }
    </script>
</body>
</html>

3. ref

理解:组件内的标签可以定义ref属性来标识自己

字符串形式的ref 示例代码如下:

// 1.创建组件
    class Demo extends React.Component{
        // 展示左侧输入框的数据
        showData = () => {
            const { input } = this.refs
            alert(input.value)
        }
        // 展示右侧输入框的数据
        showData2 = () => {
            const { input2 } = this.refs
            alert(input2.value)
        }
        render(){
            return (
                <div>
                    <input ref="input" type="text" placeholder="点击按钮提示数据"/>
                    <button type="button" onClick={this.showData}>点击提示左侧的数据</button>
                    <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>  
                </div>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>,document.getElementById('test'))

回调函数形式的ref 示例代码如下:

// 1.创建组件
    class Demo extends React.Component{
        // 展示左侧输入框的数据
        showData = () => {
            const { input1 } = this
            alert(input1.value)
        }
        // 展示右侧输入框的数据
        showData2 = () => {
            const { input2 } = this
            alert(input2.value)
        }
        render(){
            return (
                <div>
                    <input ref={e => this.input1 = e} type="text" placeholder="点击按钮提示数据"/>
                    <button type="button" onClick={this.showData}>点击提示左侧的数据</button>
                    <input ref={e => this.input2 = e} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>  
                </div>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>,document.getElementById('test'))

回调ref中回调执行次数的问题 示例代码如下:

// 1.创建组件
    class Demo extends React.Component{
        state = {isHot:false}
        showData = () => {
            const { input1 } = this
            alert(input1.value)
        }
        changeWeather = () => {
            // 获取原来的状态
            const { isHot } = this.state
            // 更新状态
            this.setState({isHot:!isHot})
        }
        saveInput = (e) => {
            this.input1 = e
            console.log('e',e)
        }
        render(){
            const { isHot } = this.state
            return (
                <div>
                    <h1>今天天气{isHot ? '炎热':'凉爽'}</h1>
                    {/* <input ref={(e) => {this.input1 = e;console.log('e',e)}} type="text" placeholder="点击按钮提示数据"/> */}
                    <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>
                    <button type="button" onClick={this.showData}>点击提示左侧的数据</button>
                    <button type="button" onClick={this.changeWeather}>点击切换天气</button>
                </div>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>,document.getElementById('test'))

createRef 示例代码如下:

// 1.创建组件
    class Demo extends React.Component{
        /* 
            React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的 
        */
        myRef = React.createRef()
        myRef2 = React.createRef()

        // 展示左侧输入框的数据
        showData = () => {
            alert(this.myRef.current.value)
        }
        // 展示右侧输入框的数据
        showData2 = () => {
            alert(this.myRef2.current.value)
        }
        render(){
            return (
                <div>
                    <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
                    <button type="button" onClick={this.showData}>点击提示左侧的数据</button>
                    <input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>  
                </div>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>,document.getElementById('test'))

3. 事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)                             

  • React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ———— 为了更好的兼容性
  • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ———— 为了高效

2.通过event.target得到发生事件的DOM元素对象 ———— 不要过度的使用ref

// 1.创建组件
    class Demo extends React.Component{
        /* 
            1.通过onXxx属性指定事件处理函数(注意大小写)
                React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 ———— 为了更好的兼容性
                React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ———— 为了高效
            2.通过event.target得到发生事件的DOM元素对象 ———— 不要过度的使用ref
        */
        myRef = React.createRef()
        myRef2 = React.createRef()

        // 展示左侧输入框的数据
        showData = () => {
            alert(this.myRef.current.value)
        }
        // 展示右侧输入框的数据
        showData2 = (event) => {
            alert(event.target.value)
        }
        render(){
            return (
                <div>
                    <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
                    <button type="button" onClick={this.showData}>点击提示左侧的数据</button>
                    <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>  
                </div>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>,document.getElementById('test'))

4. 收集表单数据

包含表单的组件分类

  • 受控组件
  • 非受控组件

受控组件示例代码如下:

// 1.创建组件
    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'))

 非受控示例代码如下:

// 1.创建组件
    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={e => this.username = e} type="text" name="username"/>    
                    密码:<input ref={e => this.password = e} type="password" name="password"/>
                    <button>登陆</button>
                </form>
            )
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Login/>,document.getElementById('test'))

5. 高阶函数和函数柯里化

高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数

  1. 若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数
  2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数常见的高阶函数有:Promise、setTimeout、arr.map()等等

函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>高阶函数-函数柯里化</title>
</head>
<body>
  <!-- 准备好一个容器 -->
  <div id="test"></div>

  <!-- 引入 react 核心库 -->
  <script type="text/javascript" src="../js/react.development.js"></script>
  <!-- 引入 react-dom,用于支持 react 操作 DOM -->
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <!-- 引入 babel,用于将 jsx 转为 js -->
  <script type="text/javascript" src="../js/babel.min.js"></script>

  <script type="text/babel"> /* 此处一定要写 babel */
  // 1.创建组件
  class Login extends React.Component{
    /* 
      高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
        1.若A函数,接受的参数是一个函数,那么A就可以称之为高阶函数
        2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
        常见的高阶函数有:Promise、setTimeout、arr.map()等等
      
      函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
      示例: 
      function sum(a){
        return (b) => {
          return (c) => {
            return a+b+c
          }
        }
      }
      const result = sum(1)(2)(3)
      console.log(result)
    */
   
    // 初始化状态
    state = {
      username:'',//用户名
      password:''
    }
    // 保存表单数据到状态中
    saveFormData = (dataType) => {
      return (event) => {
        this.setState({[dataType]:event.target.value})
      }
    }
    // 表单提交的回调
    handleSubmit = (event) => {
      event.preventDefault()//阻止表单提交
      const { username, password } = this.state
      alert(`你输入的用户名是${username},你输入的密码是${password}`)
    }
    render(){
      return (
        <form onSubmit={this.handleSubmit}>
          用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>    
          密码:<input  onChange={this.saveFormData('password')} type="password" name="password"/>
          <button>登陆</button>
        </form>
      )
    }
  }
  // 渲染组件到页面
  ReactDOM.render(<Login/>,document.getElementById('test'))
  </script>
</body>
</html>

6. 组件的生命周期

理解:

  1. 组件从创建到死亡它会经历一些特定的阶段。
  2. React组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用。
  3. 我们在定义组件时,会在特定的生命周期回调函数中做特定的工作。

  ​​​​​​生命周期的三个阶段(旧)

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

    (1).constructor()

    (2).componentWillMount()

    (3).render()

    (4).componentDidMount()

        常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  2.更新阶段:由组件内部this.setState()或父组件新render触发

    (1).shouldComponentUpdate()

    (2).componentWillUpdate()

    (3).render()

    (4).componentDidUpdate()

  3.卸载组件:由ReactDOM.unmountComponentAtNode()触发

    (1).componentWillUnmount()

        常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>react生命周期(旧)</title>
</head>
<body>
  <!-- 准备好一个容器 -->
  <div id="test"></div>

  <!-- 引入 react 核心库 -->
  <script type="text/javascript" src="../js/react.development.js"></script>
  <!-- 引入 react-dom,用于支持 react 操作 DOM -->
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <!-- 引入 babel,用于将 jsx 转为 js -->
  <script type="text/javascript" src="../js/babel.min.js"></script>

  <script type="text/babel"> /* 此处一定要写 babel */
  // 1.创建组件
  class Count extends React.Component{
    constructor(props){
      console.log('count-constructor')
      super(props)
      // 初始化状态
      this.state = {count:0}
    }
    // 加1按钮的回调
    add = ()=> {
      // 获取原状态
      const { count } = this.state
      // 更新状态
      this.setState({count:count + 1})
    }
    // 卸载组件按钮的回调
    
    death = () => {
      ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }

    // 强制更新按钮的回调
    force = () => {
      this.forceUpdate()
    }

    // 组件将要挂载的钩子
    componentWillMount(){
      console.log('count-componentWillMount')
    }

    //组件挂载完毕的钩子
    componentDidMount(){
      console.log('count-componentDidMount')
    }

    //组件将要卸载的钩子
    componentWillUnmount(){
      console.log('count-componentWillUnmount')
    }

    // 控制组件更新的阀门
    shouldComponentUpdate(){
      console.log('count-shouldComponentUpdate')
      return true
    }

    // 组件将要更新的钩子
    componentWillUpdate(){
      console.log('count-componentWillUpdate')
    }

    // 组件更新完毕的钩子
    componentDidUpdate(){
      console.log('count-componentDidUpdate')
    }

    render(){
      console.log('render')
      const { count } = this.state
      return (
        <div>
          <h2>当前求和为{count}</h2>
          <button onClick={this.add}>点我+1</button>
          <button onClick={this.death}>卸载组件</button>
          <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
        </div>
      )
    }
  }

  class A extends React.Component{
    // 初始化状态
    state = {carName:'奔驰'}

    changeCar = () => {
      this.setState({carName:'宝马'})
    }

    render(){
      return (
        <div>
          <div>我是A组件</div>
          <button onClick={this.changeCar}>换车</button>
          <B carName={this.state.carName}/>  
        </div>
      )
    }
  }

  class B extends React.Component{
    // 组件将要接收新的props的钩子
    componentWillReceiveProps(props){
      console.log('B-componentWillReceiveProps',props)
    }
    // 控制组件更新的阀门
    shouldComponentUpdate(){
      console.log('B-shouldComponentUpdate')
      return true
    }

    // 组件将要更新的钩子
    componentWillUpdate(){
      console.log('B-componentWillUpdate')
    }
    // 组件更新完毕的钩子
    componentDidUpdate(){
      console.log('B-componentDidUpdate')
    }

    render(){
      console.log('B-render')
      return (
        <div>我是B组件,接收到的车是:{this.props.carName}</div>
      )
    }
  }
  // 渲染组件到页面上
  ReactDOM.render(<A/>,document.getElementById('test'))
  </script>
</body>
</html>

生命周期的三个阶段(新)

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

      (1).constructor()

      (2).getDerivedStateFromProps()

      (3).render()

      (4).componentDidMount()

          常用,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

    2.更新阶段:由组件内部this.setState()或父组件新render触发

      (1).getDerivedStateFromProps()

      (2).shouldComponentUpdate()

      (3).render()

      (4).getSnapshotBeforeUpdate()

      (5).componentDidUpdate()

    3.卸载组件:由ReactDOM.unmountComponentAtNode()触发

      (1).componentWillUnmount()

          常用,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>react生命周期(新)</title>
</head>
<body>
  <!-- 准备好一个容器 -->
  <div id="test"></div>

  <!-- 引入 react 核心库 -->
  <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
  <!-- 引入 react-dom,用于支持 react 操作 DOM -->
  <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
  <!-- 引入 babel,用于将 jsx 转为 js -->
  <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

  <script type="text/babel"> /* 此处一定要写 babel */
  // 1.创建组件
  class Count extends React.Component{
    constructor(props){
      console.log('count-constructor')
      super(props)
      // 初始化状态
      this.state = {count:0}
    }
    // 加1按钮的回调
    add = ()=> {
      // 获取原状态
      const { count } = this.state
      // 更新状态
      this.setState({count:count + 1})
    }
    // 卸载组件按钮的回调
    
    death = () => {
      ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }

    // 强制更新按钮的回调
    force = () => {
      this.forceUpdate()
    }

    // 若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
    static getDerivedStateFromProps(props,state){
      console.log('count-getDerivedStateFromProps',props,state)
      return null
    }

    // 在更新之前获取快照
    getSnapshotBeforeUpdate(){
      console.log('count-getSnapshotBeforecUpdate')
      return 'yzh'
    }

    //组件挂载完毕的钩子
    componentDidMount(){
      console.log('count-componentDidMount')
    }

    //组件将要卸载的钩子
    componentWillUnmount(){
      console.log('count-componentWillUnmount')
    }

    // 控制组件更新的阀门
    shouldComponentUpdate(){
      console.log('count-shouldComponentUpdate')
      return true
    }
 
    // 组件更新完毕的钩子
    componentDidUpdate(preProps,preState,snapshotValue){
      console.log('count-componentDidUpdate',preProps,preState,snapshotValue)
    }

    render(){
      console.log('render')
      const { count } = this.state
      return (
        <div>
          <h2>当前求和为{count}</h2>
          <button onClick={this.add}>点我+1</button>
          <button onClick={this.death}>卸载组件</button>
          <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
        </div>
      )
    }
  }

  // 渲染组件到页面上
  ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
  </script>
</body>
</html>

 重要的钩子:

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听,发送ajax请求
  3. componentWillUnmount:做一些收尾的工作,如:清理定时器

即将废弃的钩子:

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。


7. key的作用 

经典面试题:

    1). react/vue中的key有什么作用?(key的内部原理是什么?)

    2). 为什么遍历列表时,key最好不要用index?

   

    1. 虚拟DOM中key的作用:

      1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

      2). 详细的说:

        当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,

        随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

          a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:

            (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM

            (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

          b. 旧虚拟DOM中未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM,随后渲染到到页面

                   

    2. 用index作为key可能会引发的问题:

      1). 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

        会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

      2). 如果结构中还包含输入类的DOM:

        会产生错误DOM更新 ==> 界面有问题。

             

      3). 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,

        仅用于渲染列表用于展示,使用index作为key是没有问题的。

       

    3. 开发中如何选择key?:

      1).最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。

      2).如果确定只是简单的展示数据,用index也是可以的。

示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>key的作用</title>
</head>
<body>
  <!-- 准备好一个容器 -->
  <div id="test"></div>

  <!-- 引入 react 核心库 -->
  <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
  <!-- 引入 react-dom,用于支持 react 操作 DOM -->
  <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
  <!-- 引入 babel,用于将 jsx 转为 js -->
  <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

  <script type="text/babel"> /* 此处一定要写 babel */
  /* 
    慢动作回放----使用index索引值作为key

      初始数据:
        {id:1,name:'小张',age:18},
        {id:2,name:'小李',age:19},
      初始的虚拟DOM:
        <li key=0>小张---18<input type="text"/></li>
        <li key=1>小李---19<input type="text"/></li>

      更新后的数据:
        {id:3,name:'小王',age:20},
        {id:1,name:'小张',age:18},
        {id:2,name:'小李',age:19},
      更新数据后的虚拟DOM:
        <li key=0>小王---20<input type="text"/></li>
        <li key=1>小张---18<input type="text"/></li>
        <li key=2>小李---19<input type="text"/></li>

  -----------------------------------------------------------------

    慢动作回放----使用id唯一标识作为key

      初始数据:
        {id:1,name:'小张',age:18},
        {id:2,name:'小李',age:19},
      初始的虚拟DOM:
        <li key=1>小张---18<input type="text"/></li>
        <li key=2>小李---19<input type="text"/></li>

      更新后的数据:
        {id:3,name:'小王',age:20},
        {id:1,name:'小张',age:18},
        {id:2,name:'小李',age:19},
      更新数据后的虚拟DOM:
        <li key=3>小王---20<input type="text"/></li>
        <li key=1>小张---18<input type="text"/></li>
        <li key=2>小李---19<input type="text"/></li>


  */
  
  class Person extends React.Component{
    state = {
      persons:[
        {id:1,name:'小张',age:18},
        {id:2,name:'小李',age:19},
      ]
    }

    add = () => {
      const { persons } = this.state
      const p = {id:persons.length + 1, name:'小王', age:20}
      this.setState({persons:[...persons,p]})
    }

    render(){
      return (
        <div>
          <h1>展示人员信息</h1>
          <button onClick={this.add}>添加一个小王</button>
          <h1>使用index(索引值)作为key</h1>
          <ul>
            {
              this.state.persons.map((personObj,index) => {
                return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
              })
            }  
          </ul>
          <hr/>
          <h1>使用id(数据的唯一标识)作为key</h1>
          <ul>
            {
              this.state.persons.map((personObj,index) => {
                return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/>- </li>
              })
            }  
          </ul>

        </div>
      )
    }
  }

  ReactDOM.render(<Person/>,document.getElementById('test'))
  </script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值