Blog
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)

HiuZing

🍑
首页
文档
收藏
关于
  • 在线转换时间戳 (opens new window)
  • 在线压缩图片 (opens new window)
  • Float-Double转二进制 (opens new window)
  • 文件转Hex字符串 (opens new window)
  • 前端面试题

  • JavaScript

  • Vue2

  • port

  • CSS

  • Node.js

  • JavaScript优化

  • uniapp

  • Mini Program

  • TypeScript

  • 面向对象编程

  • UI组件

  • Plugin

  • Vue3

  • 性能优化

  • Axios

  • 状态管理

  • React

    • 教程

      • React 简介
      • React 组件
        • 基本理解和使用
          • 渲染类组件标签的基本流程
          • 函数式组件
          • 类式组件
        • 组件(组件实例的)三大核心属性
          • state
          • props
          • refs与事件处理
        • 收集表单数据
          • 受控组件(类似Vue的双向绑定)
          • 非受控组件
          • 场景
          • 总结
        • 组件的生命周期
          • 生命周期流程图(旧)
          • 生命周期流程图(新)
        • 虚拟DOM与DOM Diffing算法
          • 经典面试题:
          • 案例
          • 慢镜回放
      • React 应用
      • React AJAX
      • React Router5
      • React Redux
      • React 扩展内容
      • React Router6
      • React Router Hooks
      • React Hooks
    • 实战

  • Mock

  • Icon

  • Template

  • 构建工具

  • 项目规范配置

  • Taro

  • SVG

  • React Native

  • 前端
  • React
  • 教程
HiuZing
2023-04-10
目录

React 组件

# 基本理解和使用

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

# 渲染类组件标签的基本流程

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

# 函数式组件

适用于【简单组件】的定义

//1.创建函数式组件
function MyComponent(){
    console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
    return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))

// 执行了ReactDOM.render(<MyComponent/>.......之后
1
2
3
4
5
6
7
8
9
  1. React解析组件标签,找到了MyComponent组件。
  2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

# 类式组件

适用于【复杂组件(有状态)】的定义

<script type="text/babel">
    //1.创建类式组件
    class MyComponent extends React.Component {
        render(){
            //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
            //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
            console.log('render中的this:',this);
            return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
        }
    }
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
</script>

// 执行了ReactDOM.render(<MyComponent/>.......之后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. React解析组件标签,找到了MyComponent组件。
  2. 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
  3. 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

# 组件(组件实例的)三大核心属性

数据放在状态,组件的状态放着数据,数据改变就驱动着页面展示

# state

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

警告

  1. 组件中render方法中的this为组件实例对象
  2. 组件自定义的方法中this为undefined如何解决?
    1. 强制绑定this: 通过函数对象的bind()
    2. 箭头函数
  3. 状态数据,不能直接修改或更新,必须通过内置的setState进行更新

# 标准模式/简写模式

标准模式执行顺序:

首先构造器和render各调用一次,当你触发changeWeather方法,changeWeather和render再各调用一次

标准模式执行次数:

  1. 构造器调用几次? ———— 1次
  2. render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
  3. changeWeather调用几次? ———— 点几次调几次






     






























    <script type="text/babel">
        //1.创建组件
        class Weather extends React.Component{
            constructor(props){
                super(props)
                //初始化状态
                this.state = {isHot:false,wind:'微风'}
                //解决changeWeather中this指向问题
                this.changeWeather = this.changeWeather.bind(this)
            }
    
            render(){
                // 读取状态
                const {isHot,wind} = this.state
                // 注意:此处点击事件中不可写小括号,因为会把该方法调用的返回值交给onClick作为回调,返回值为undefined,把undefined作为回调是不对的,
                // 等你触发onClick时候帮你调用undefined,应表达的意思是赋值语句,将函数交给onClick作为回调,等你触发的时候帮你调用方法
                return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
            }
    
            changeWeather(){
                // changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
                // 由于changeWeather是作为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>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <script type="text/babel">
        //1.创建组件
        class Weather extends React.Component{
            //初始化状态
            state = {isHot:false,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>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // Make sure to add code blocks to your code group

    # props

    # 理解

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

    # 作用

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

    # 编码

    1. 内部读取某个属性值

      this.props.name
      
      1
    2. 对props中的属性值进行类型限制和必要性限制

      Person.propTypes = {
        name: PropTypes.string.isRequired,
        age: PropTypes.number. 
      }
      
      1
      2
      3
      4
    3. 扩展属性: 将对象的所有属性通过props传递

      <Person {...person}/>
      
      1
    4. 默认属性值

      Person.defaultProps = {
        age: 18,
        sex:'男'
      }
      
      1
      2
      3
      4
    5. 组件类的构造函数

      constructor(props){
        super(props)
        console.log(props)//打印所有属性
      }
      
      1
      2
      3
      4

    # 基本使用

    <script type="text/babel">
        //创建组件
        class Person extends React.Component{
            render(){
                const {name,age,sex} = this.props // 组件外部传进来,内部用props接收
                return (
                    <ul>
                        <li>姓名:{name}</li>
                        <li>性别:{sex}</li>
                        <li>年龄:{age+1}</li>
                    </ul>
                    )
    		}
    }
    //渲染组件到页面
    ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))
    ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
    
    const p = {name:'老刘',age:18,sex:'女'}
    // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
    // 通过拓展运算符将p对象中的属性
    ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    # 对props进行限制(类组件)


















       
       
       
       
       
       
       
       
       
       
       
       













      <script type="text/babel">
          //创建组件
          class Person extends React.Component{
              render(){
                  // console.log(this);
                  const {name,age,sex} = 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
      }
      //渲染组件到页面
      ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test1'))
      ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
      
      const p = {name:'老刘',age:18,sex:'女'}
      // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
      ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
      
      function speak(){
          console.log('我说话了');
      }
      </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41












       
       
       
       
       
       
       
       
       
       
       













      <script type="text/babel">
          //创建组件
          class Person extends React.Component{
              constructor(props){
                  //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
                  super(props)
                  console.log('constructor',this.props);
              }
              //对标签属性进行类型、必要性的限制
              static propTypes = {
                  name:PropTypes.string.isRequired, //限制name必传,且为字符串
                  sex:PropTypes.string,//限制sex为字符串
                  age:PropTypes.number,//限制age为数值
              }
              //指定默认标签属性值
              static defaultProps = {
                  sex:'男',//sex默认值为男
                  age:18 //age默认值为18
              }
              render(){
                  const {name,age,sex} = this.props
                  //props是只读的
                  //this.props.name = 'jack' //此行代码会报错,因为props是只读的
                  return (
                      <ul>
                          <li>姓名:{name}</li>
                          <li>性别:{sex}</li>
                          <li>年龄:{age+1}</li>
                      </ul>
                      )
      		}
      }
      //渲染组件到页面
      ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
          </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      // Make sure to add code blocks to your code group

      # 对props进行限制(函数组件)

      <script type="text/babel">
          //创建组件
          function Person (props){
          const {name,age,sex} = props
          return (
              <ul>
                  <li>姓名:{name}</li>
                  <li>性别:{sex}</li>
                  <li>年龄:{age}</li>
              </ul>
             )
      }
      Person.propTypes = {
          name:PropTypes.string.isRequired, //限制name必传,且为字符串
          sex:PropTypes.string,//限制sex为字符串
          age:PropTypes.number,//限制age为数值
      }
      
      //指定默认标签属性值
      Person.defaultProps = {
          sex:'男',//sex默认值为男
          age:18 //age默认值为18
      }
      //渲染组件到页面
      ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
      </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26

      # refs与事件处理

      # 理解

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

      # 编码

      1. 字符串形式的ref

        <input ref="input1"/>
        
        1
      2. 回调形式的ref

        <input ref={(c)=>{this.input1 = c}}/>
        
        1
      3. createRef创建ref容器

        myRef = React.createRef() 
        <input ref={this.myRef}/>
        
        1
        2

      # 字符串形式的ref(不建议使用)






       










       










      <script type="text/babel">
          //创建组件
          class Demo extends React.Component{
              //展示左侧输入框的数据
              showData = ()=>{
                  const {input1} = this.refs
                  alert(input1.value)
              }
              //展示右侧输入框的数据
              showData2 = ()=>{
                  const {input2} = this.refs
                  alert(input2.value)
              }
              render(){
                  return(
                      <div>
                      <input ref="input1" type="text" placeholder="点击按钮提示数据"/>
                      <button onClick={this.showData}>点我提示左侧的数据</button>
      				<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
          			</div>
      				)
      		}
      }
      //渲染组件到页面
      ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
          </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26

      # 回调形式的ref






       











       











      <script type="text/babel">
          //创建组件
          class Demo extends React.Component{
              //展示左侧输入框的数据
              showData = ()=>{
                  const {input1} = this
                  alert(input1.value)
              }
              //展示右侧输入框的数据
              showData2 = ()=>{
                  const {input2} = this
                  alert(input2.value)
              }
              render(){
                  return(
                      <div>
                      // 把ref当前所处的节点(react帮你调用传进去)挂载实例自身上
                      // 并起个名字input1,this指向-->render-->组件实例对象
                      <input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
              		<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
      				<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
      				</div>
      				)
      		}
      }
      //渲染组件到页面
      ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
      </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      # 回调ref中回调执行次数的问题




























       
       











      <script type="text/babel">
      	//创建组件
      	class Demo extends React.Component{
      
      		state = {isHot:false}
      
      		showInfo = ()=>{
      			const {input1} = this
      			alert(input1.value)
      		}
      
      		changeWeather = ()=>{
      			//获取原来的状态
      			const {isHot} = this.state
      			//更新状态
      			this.setState({isHot:!isHot})
      		}
      
      		saveInput = (c)=>{
      			this.input1 = c;
      			console.log('@',c);
      		}
      
      		render(){
      			const {isHot} = this.state
      			return(
      				<div>
      					<h2>今天天气很{isHot ? '炎热':'凉爽'}</h2>
      					{/*内联函数在页面更新的时候,执行两次,第一次是null,第二次是当前节点(确保每次调用容器是干净的,所以要将置null)*/}
      					{/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/}
      					<input ref={this.saveInput} type="text"/><br/><br/>{/*使用class定义的方法可以避免(但问题不大)*/}
      					<button onClick={this.showInfo}>点我提示输入的数据</button>
      					<button onClick={this.changeWeather}>点我切换天气</button>
      				</div>
      			)
      		}
      	}
      	//渲染组件到页面
      	ReactDOM.render(<Demo/>,document.getElementById('test'))
      </script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40

      # createRef

      React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的





       












       










      	<script type="text/babel">
      		//创建组件
      		class Demo extends React.Component{
                  // 存储多个就创建多个
      			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="点击按钮提示数据"/>&nbsp;
      						<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
      						<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
      					</div>
      				)
      			}
      		}
      		//渲染组件到页面
      		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
      	</script>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27

      # 事件处理

      1. 通过onXxx属性指定事件处理函数(注意大小写)
      2. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
      3. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
      4. 通过event.target得到发生事件的DOM元素对象
      <script type="text/babel">
          //创建组件
          class Demo extends React.Component{
              /* 
      				(1).通过onXxx属性指定事件处理函数(注意大小写)
      						a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
      						b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
      				(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
      			 */
              //创建ref容器
              myRef = React.createRef()
              myRef2 = React.createRef()
      
              //展示左侧输入框的数据
              showData = (event)=>{
                  console.log(event.target);
                  alert(this.myRef.current.value);
              }
      
              //展示右侧输入框的数据
              showData2 = (event)=>{
                  alert(event.target.value);
              }
      
              render(){
                  return(
                      <div>
                      <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
              		<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
      				<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
      				</div>
      				)
      	}
      }
      //渲染组件到页面
      ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
      </script>
      
      // this.showData2  作为事件回调,this指向会丢,因此要写赋值语句+箭头函数的形式
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39

      # 收集表单数据

      包含表单的组件分类

      1. 受控组件

        在HTML中,标签input、textarea、select的值的改变通常是根据用户输入进行更新。在React中,可变状态通常保存在组件的状态属性中,并且只能使用 setState() 更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况,以这种由React控制的输入表单元素而改变其值的方式

      2. 非受控组件

        表单数据由DOM本身处理。即不受*setState()*的控制,与传统的HTML表单输入相似,input输入值即显示最新值(使用 ref从DOM获取表单值)

      # 受控组件(类似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>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        <script type="text/babel">
        		//创建组件
        		class Login extends React.Component{
        			//初始化状态
        			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>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        <script type="text/babel">
          	//创建组件
          	class Login extends React.Component{
          		//初始化状态
          		state = {
          			username:'', //用户名
          			password:'' //密码
          		}
          		//保存表单数据到状态中
          		saveFormData = (dataType,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={event => this.saveFormData('username',event) } type="text" name="username"/>
          					密码:<input onChange={event => this.saveFormData('password',event) } type="password" name="password"/>
          					<button>登录</button>
          				</form>
          			)
          		}
          	}
          	//渲染组件
          	ReactDOM.render(<Login/>,document.getElementById('test'))
          </script>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        // Make sure to add code blocks to your code group

        # 非受控组件

        <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>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21

        # 场景

        特征 非受控制 受控
        一次性检索(例如表单提交) yes yes
        及时验证 no yes
        有条件的禁用提交按钮 no yes
        执行输入格式 no yes
        一个数据的几个输入 no yes
        动态输入 no yes

        在这里插入图片描述

        # 总结

        React中的组件分为受控组件和非受控组件

        受控组件的两个要点:

        • 组件的value属性与React中的状态绑定
        • 组件内声明了onChange事件处理value的变化

        非受控组件更像是传统的HTML表单元素,数据存储在DOM中,而不是组件内部,获取数据的方式是通过ref引用

        一些建议:

        • 尽可能使用受控组件
        • 受控组件是将状态交由React处理,可以是任何元素,不局限于表单元素
        • 对于有大量表单元素的页面,使用受控组件会使程序变得繁琐难控,此时使用非受控组件更为明智
        • 在受控组件中,数据流是单向的(state是变化来源),因此在改变state时都应该使用setState,而不要强制赋值
        • Refs不能用于函数式组件,因为函数式组件没有实例
        • 在函数式组件内部,是可以使用Refs

        # 组件的生命周期

        生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子

        1. 组件从创建到死亡它会经历一些特定的阶段。
        2. React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
        3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
        <script type="text/babel">
        		//创建组件
        		class Life extends React.Component{
        			state = {opacity:1}
        			death = ()=>{
        				//卸载组件
        				ReactDOM.unmountComponentAtNode(document.getElementById('test'))
        			}
        			//组件挂完毕
        			componentDidMount(){
        				console.log('componentDidMount');
        				this.timer = setInterval(() => {
        					//获取原状态
                            // 该this是实例对象调用
        					let {opacity} = this.state
        					//减小0.1
        					opacity -= 0.1
        					if(opacity <= 0) opacity = 1
        					//设置新的透明度
        					this.setState({opacity})
        				}, 200);
        			}
        			//组件将要卸载
        			componentWillUnmount(){
        				//清除定时器
        				clearInterval(this.timer)
        			}
        			//初始化渲染、状态更新之后
        			render(){
        				return(
        					<div>
        						<h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
        						<button onClick={this.death}>不活了</button>
        					</div>
        				)
        			}
        		}
        		//渲染组件
        		ReactDOM.render(<Life/>,document.getElementById('test'))
        	</script>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40

        # 生命周期流程图(旧)

        image-20230413111617623

        1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
          1. constructor()——构造器
          2. componentWillMount()——组件将要挂载的钩子
          3. render()
          4. componentDidMount()——组件挂载完毕的钩子
        2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
          1. shouldComponentUpdate()(强迫更新不使用该钩子)——控制组件更新的“阀门”
          2. componentWillUpdate()——组件将要更新的钩子
          3. render()
          4. componentDidUpdate()——组件更新完毕的钩子
        3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
          1. componentWillUnmount()——组件将要卸载的钩子

        # 生命周期流程图(新)

        image-20230428195044887

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

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

          1. constructor
          2. getDerivedStateFromProps
          3. render
          4. componentDidMount
        2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

          1. getDerivedStateFromProps
          2. shouldComponentUpdate
          3. render
          4. getSnapshotBeforeUpdate
          5. componentDidUpdate
        3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

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

        1. componentWillUnmount

        # 重要的勾子

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

        # 即将废弃的勾子

        1. componentWillMount
        2. componentWillReceiveProps

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

        # 虚拟DOM与DOM Diffing算法

        image-20230428213859640

        # 经典面试题:

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

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

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

            2. 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比

              较,比较规则如下:

              1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
                1. 若虚拟DOM中内容没变, 直接使用之前的真实DOM
                2. 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
              2. 旧虚拟DOM中未找到与新虚拟DOM相同的key
                1. 根据数据创建新的真实DOM,随后渲染到到页面
          2. # 用index作为key可能会引发的问题:
            1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:

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

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

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

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

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

          3. # 开发中如何选择key?
            1. 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
            2. 如果确定只是简单的展示数据,用index也是可以的。

        # 案例

        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:[p,...persons]})
            }
        
            render(){
                return (
                    <div>
                    <h2>展示人员信息</h2>
                    <button onClick={this.add}>添加一个小王</button>
        			<h3>使用index(索引值)作为key</h3>
        			<ul>
                    {
                    this.state.persons.map((personObj,index)=>{
                        return <li key={index}>{personObj.name}---{personObj.age}<input type="text"/></li>
                    })
        			}
            </ul>
        		<hr/>
                <hr/>
                <h3>使用id(数据的唯一标识)作为key</h3>
        		<ul>
                {
                this.state.persons.map((personObj)=>{
                    return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li>
                })
        }
            </ul>
        </div>
        )
        }
        }
        
        ReactDOM.render(<Person/>,document.getElementById('test'))
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44

        # 慢镜回放

        只有放在页面上的真实DOM,input才有.value属性,留下用户输入

        		慢动作回放----使用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>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        #React
        上次更新: 2024/08/14, 04:14:33
        React 简介
        React 应用

        ← React 简介 React 应用→

        最近更新
        01
        React Native 使用SVG
        08-13
        02
        Docker基础命令
        08-04
        03
        算数逻辑单元
        07-30
        更多文章>
        Theme by Vdoing | Copyright © 2021-2024 WeiXiaojing | 友情链接
        • 跟随系统
        • 浅色模式
        • 深色模式
        • 阅读模式