【React新手学习指南】02 React生命周期浅析(超详细小白也能看懂)

写在前面,大家好!我是【跨考菌】,一枚跨界的程序猿,专注于后台技术的输出,目标成为全栈攻城狮!这博客是对我跨界过程的总结和思考。如果你也对Java后端技术感兴趣,抑或是正在纠结于跨界,都可以关注我的动态,让我们一起学习,一起进步~
我的博客地址为:【跨考菌】的博客


上篇【React新手学习指南】01 React修炼入门(小白专属系列) 介绍了React基础知识和组件开发的内容。本文开始详细介绍面向组件开发的相关内容。和【跨考菌】一起加油吧~

在这里插入图片描述

如果你觉得对你有帮助的话,记得帮博主一键三连😊哦


1 组件开发理解和使用

1.1 效果图

在这里插入图片描述
Js时面向对象编程的;
React时面向组件编程的

1.2 自定义组件

1)定义组件的2种方式

/*方式1: 工厂函数组件(简单组件)*/
function MyComponent () {
  return <h2>工厂函数组件(简单组件)</h2>
}

/*方式2:  ES6类组件(复杂组件)*/
class MyComponent2 extends React.Component {
  render () {
    console.log(this) // MyComponent2的实例对象
    return <h2>ES6类组件(复杂组件)</h2>
  }
}

2)渲染组件标签

ReactDOM.render(<MyComponent />, document.getElementById('example1'))

1.3 注意

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

1.4 render()渲染组件标签的基本流程

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

2 组件的三大属性之state

2.1 概述

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

2.2 编码操作

  1. 初始化状态:
  constructor (props) {
    super(props)
    this.state = {
      stateProp1 : value1,
      stateProp2 : value2
    }
  }
  1. 读取某个状态值
  this.state.statePropertyName
  1. 更新状态---->组件界面更新
  this.setState({
    stateProp1 : value1,
    stateProp2 : value2
  })

2.3 案例

<script type="text/babel">
  /*
  需求: 自定义组件, 功能说明如下
    1. 显示h2标题, 初始文本为: 你喜欢我
    2. 点击标题更新为: 我喜欢你
  */
  class Like extends React.Component {
    constructor (props) {
      super(props)
      // 初始化状态
      this.state = {
        isLikeMe: true
      }
      // 将新增的方法的this,绑定this为组件对象
      this.change = this.change.bind(this)
    }
    change () {
      // 更新状态: this.setState()
      // this.state.isLikeMe = !this.state.isLikeMe // 不能更新更新某个状态
      this.setState({
        isLikeMe: !this.state.isLikeMe
      })
    }
    render () {
      console.log('render()')
      const text = this.state.isLikeMe ? '你喜欢我' : '我喜欢你'
      return <h2 onClick={this.change}>{text}</h2>
    }
  }
  ReactDOM.render(<Like />, document.getElementById('example'))
</script>

在这里插入图片描述

3 组件三大属性之 props

3.1 效果

需求: 自定义用来显示一个人员信息的组件
1). 姓名必须指定
2). 如果性别没有指定, 默认为男
3). 如果年龄没有指定, 默认为18
在这里插入图片描述

3.2 props的理解

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

3.3 props的作用

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

3.4 props的操作

  1. 内部读取某个属性值
this.props.propertyName
  1. 对props中的属性值进行类型限制和必要性限制
Person.propTypes = {
name: React.PropTypes.string.isRequired,
age: React.PropTypes.number.isRequired
}
  1. 扩展属性: 将对象的所有属性通过props传递
<Person {...person}/>
  1. 默认属性值
Person.defaultProps = {
name: 'Mary'
}
  1. 组件类的构造函数
constructor (props) {
super(props)
console.log(props) // 查看所有属性
}

3.5 面试高频

问题: 请区别一下组件的props和state属性

  1. state: 组件自身内部可变化的数据
  2. props: 从组件外部向组件内部传递数据, 组件内部只读不修改

3.6 案例

<script type="text/babel">

  /*
需求: 自定义用来显示一个人员信息的组件, 效果如页面. 说明
  1). 如果性别没有指定, 默认为男
  2). 如果年龄没有指定, 默认为18
  */

  //1. 定义组件类
  class Person extends React.Component {
    render() {
      console.log(this)
      return (
        <ul>
          <li>姓名: {this.props.name}</li>
          <li>性别: {this.props.sex}</li>
          <li>年龄: {this.props.age}</li>
        </ul>
      )
    }
  }
  // 对标签属性进行限制
  Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number
  }
  // 指定属性的默认值
  Person.defaultProps = {
    sex: '男',
    age: 18
  }

  //2. 渲染组件标签
  const person = {
    name: 'Tom',
    sex: '女',
    age: 18
  }
  ReactDOM.render(<Person {...person}/>, document.getElementById('example1'))
  const person2 = {
    myName: 'JACK',
    age: 17
  }
  ReactDOM.render(<Person name={person2.myName} age={person2.age}/>,
    document.getElementById('example2'))
</script>

在这里插入图片描述

4 组件三大属性之refs与事件处理

4.1 效果

需求: 自定义组件, 功能说明如下:

  1. 点击按钮, 提示第一个输入框中的值
  2. 当第2个输入框失去焦点时, 提示这个输入框中的值

在这里插入图片描述

4.2 ref概述

  1. 组件内的标签都可以定义ref属性来标识自己
    a. <input type="text" ref={input => this.msgInput = input}/>
    b. 回调函数在组件初始化渲染完或卸载时自动调用
  2. 在组件中可以通过this.msgInput来得到对应的真实DOM元素
  3. 作用: 通过ref获取组件内容特定标签对象, 进行读取其相关数据

4.3 事件处理

  1. 通过onXxx属性指定组件的事件处理函数(注意大小写)
    a. React使用的是自定义(合成)事件, 而不是使用的原生DOM事件
    b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
  2. 通过event.target得到发生事件的DOM元素对象
<input onFocus={this.handleClick}/>
handleFocus(event) {
event.target  //返回input对象
}

4.4 注意点

  1. 组件内置的方法中的this为组件对象
  2. 在组件类中自定义的方法中this为null
    a. 强制绑定this: 通过函数对象的bind()
    b. 箭头函数(ES6模块化编码时才能使用)

4.5 案例

 <script type="text/babel">
    /*
    需求: 自定义组件, 功能说明如下:
      1. 界面如果页面所示
      2. 点击按钮, 提示第一个输入框中的值
      3. 当第2个输入框失去焦点时, 提示这个输入框中的值
   */
    //定义组件
    class MyComponent extends React.Component {
      constructor(props) {
        super(props) // 调用父类(Component)的构造函数
        //console.log(this)
        // 将自定义的函数强制绑定为组件对象
        this.handleClick = this.handleClick.bind(this) // 将返回函数中的this强制绑定为指定的对象, 并没有改变原来的函数中的this
      }
      // 自定义的方法中的this默认为null
      handleClick () {
        // alert(this) //this默认是null, 而不是组件对象
        // 得到绑定在当前组件对象上的input的值
        alert(this.msgInput.value)
      }
      handleBlur (event) { // 事件的处理方式,所有的事件函数都有一个形参,event!!!!
        alert(event.target.value)
      }
      render () {
        return (
          <div>
            <input type="text" ref={input => this.msgInput = input}/>{' '} // refs可以理解为一个数组,放了很多东西;
            <button onClick={this.handleClick}>提示输入数据</button>{' '}
            <input type="text" placeholder="失去焦点提示数据" onBlur={this.handleBlur}/>
          </div>
        )
      }
    }
    // 渲染组件标签
    ReactDOM.render(<MyComponent />, document.getElementById('example'))
  </script>

在这里插入图片描述

5 组件的组合

5.1 效果

功能: 组件化实现此功能

  1. 显示所有todo列表
  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

在这里插入图片描述

5.2 功能界面的组件化编码流程

  1. 拆分组件: 拆分界面,抽取组件
  2. 实现静态组件: 使用组件实现静态页面效果
  3. 实现动态组件
    a. 动态显示初始化数据
    b. 交互功能(从绑定事件监听开始)

5.3 案例

<script type="text/babel">
    /*
    1)拆分组件: 拆分界面,抽取组件
    2)实现静态组件: 使用组件实现静态页面效果
    3)实现动态组件
        ① 动态显示初始化数据
        ② 交互功能(从绑定事件监听开始)

    1、数据保存在哪儿?
        数据时哪个组件需要,就放到哪个组件;还是某些组件需要,放到父组件。
    2、需要在子组件中改变父组件的状态??
        子组件不能直接改变父组件的额状态;
        状态在哪个组件,更新状态就应该在哪个组件中。
        解决:父组件定义函数,传给子组件。
     */
    // 应用组件
    class App extends React.Component {
      constructor (props) {
        super(props)
        // 初始化状态
        this.state = {
          todos: ['吃饭', '睡觉', '打豆豆']
        }
        this.add = this.add.bind(this)
      }
      add (todo) {
        const {todos} = this.state
        todos.unshift(todo) // 放到最前面
        //更新状态
        this.setState({todos}) // 这样才是更新了状态,刷新界面了!!!
      }
      render () {
        const {todos} = this.state
        return (
          <div>
            <TodoAdd add={this.add} count={todos.length} /> // 子组件的属性从父组件来赋值。
            <TodoList todos={todos} />
          </div>
        )
      }
    }

    // 添加todo组件
    class TodoAdd extends React.Component {
      constructor (props) {
        super(props)
        this.addTodo = this.addTodo.bind(this)
      }
      addTodo () {
        // 读取输入数据
        const text = this.input.value.trim()
        // 查检
        if(!text) {
          return
        }
        // 保存到todos
        this.props.add(text)
        // 清除输入
        this.input.value = ''
      }
      render () {
        return (
          <div>
            <h2>Simple TODO List</h2>
            <input type="text" ref={input => this.input=input}/>
            <button onClick={this.addTodo}>Add #{this.props.count}</button>
          </div>
        )
      }
    }
    TodoAdd.propTypes = {
      add: PropTypes.func.isRequired,
      count: PropTypes.number.isRequired
    }

    // todo列表组件
    class TodoList extends React.Component {
      render () {
        const {todos} = this.props
        return (
          <ul>
            {
              todos.map((todo, index) => <li key={index}>{todo}</li>)
            }
          </ul>
        )
      }
    }

    TodoList.propTypes = {
      todos: PropTypes.array.isRequired
    }

    // 渲染应用组件标签
    ReactDOM.render(<App />, document.getElementById('example'))

  </script>

6 收集表单数据

6.1 效果

需求: 自定义包含表单的组件

  1. 输入用户名密码后, 点击登陆提示输入信息
  2. 不提交表单

在这里插入图片描述

6.2 理解

  1. 问题: 在react应用中, 如何收集表单输入数据
  2. 包含表单的组件分类
    a. 受控组件: 表单项输入数据能自动收集成状态(state)
    b. 非受控组件: 需要时才手动读取表单输入框中的数据

6.3 案例

<script type="text/babel">
  /*
  1. 问题: 在react应用中, 如何收集表单输入数据
  2. 包含表单的组件分类
    受控组件
    非受控组件
  */
  /*
  需求: 自定义包含表单的组件
    1. 界面如下所示
    2. 输入用户名密码后, 点击登陆提示输入信息
    3. 不提交表单
  */
  class LoginForm extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
        username: ''
      }
      this.handleSubmit = this.handleSubmit.bind(this)
      this.handleChange = this.handleChange.bind(this)
    }

    handleChange(event) {
      this.setState({username: event.target.value})
    }

    handleSubmit(event) {
      alert(`准备提交的用户名为: ${this.state.username}, 密码:${this.pwdInput.value}`)

      // 阻止事件的默认行为: 提交表单(其实这里就是不用跳转到/test页面去)
      event.preventDefault()
    }

    render () {

      return (
        <form onSubmit={this.handleSubmit} action="/test">
          <label>
            用户名:
            <input type="text" value={this.state.username} onChange={this.handleChange} />
          </label>&nbsp;
          <label>
            密码:
            <input type="password" ref={(input) => this.pwdInput = input} />
          </label>&nbsp;
          <input type="submit" value="登陆" />
        </form>
      )
    }
  }
  
  ReactDOM.render(<LoginForm />, document.getElementById('example'))
</script>

7 组件声明周期

7.1 效果

需求: 自定义组件

  1. 让指定的文本做显示/隐藏的渐变动画
  2. 切换持续时间为2S
  3. 点击按钮从界面中移除组件界面
    在这里插入图片描述

7.2 理解

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

7.3 声明周期流程图

在这里插入图片描述

7.4 生命周期详解

  1. 组件的三个生命周期状态:
    • Mount:插入真实 DOM
    • Update:被重新渲染
    • Unmount:被移出真实 DOM
  2. React 为每个状态都提供了勾子(hook)函数
    • componentWillMount()
    • componentDidMount()
    • componentWillUpdate()
    • componentDidUpdate()
    • componentWillUnmount() // 组件被销毁的时候会调用的!!!!
  3. 生命周期流程:
    a. 第一次初始化渲染显示: ReactDOM.render()
    • constructor(): 创建对象初始化state
    • componentWillMount() : 将要插入回调
    • render() : 用于插入虚拟DOM回调
    • componentDidMount() : 已经插入回调
      b. 每次更新state: this.setSate()
    • componentWillUpdate() : 将要更新回调
    • render() : 更新(重新渲染)
    • componentDidUpdate() : 已经更新回调
      c. 移除组件: ReactDOM.unmountComponentAtNode(containerDom)
    • componentWillUnmount() : 组件将要被移除回调

7.5 重要的钩子函数

  1. render(): 初始化渲染或更新渲染调用
  2. componentDidMount(): 开启监听, 发送ajax请求
  3. componentWillUnmount(): 做一些收尾工作, 如: 清理定时器
  4. componentWillReceiveProps(): 后面需要时讲
    componentWillReceiveProps(nextProps): 监视接收到新的props,(父亲变了,可以及时的捕捉)

7.6 案例

<script type="text/babel">
  /*
  需求: 自定义组件
    1. 让指定的文本做显示/隐藏的动画
    2. 切换时间为2S
    3. 点击按钮从界面中移除组件界面
   */
  class Fade extends React.Component {

    constructor (props) {
      super(props)
      console.log('constructor(): 创建组件对象')
      this.state = {
        opacity: 1
      }
      this.removeComponent = this.removeComponent.bind(this)
    }

    componentWillMount () {
      console.log('componentWillMount(): 初始化将要挂载')
    }

    componentDidMount () {// 在此方法中启动定时器/绑定监听/发送ajax请求
      console.log('componentDidMount(): 初始化已经挂载')
      // 保存到当前组件对象中
      this.intervalId = setInterval(function () {
        console.log('--------')
        // 得到当前opacity
        let {opacity} = this.state
        // 更新opacity
        opacity -= 0.1
        if(opacity<=0) {
          opacity = 1
        }
        // 更新状态
        this.setState({opacity})
      }.bind(this), 200)
    }

    componentWillUpdate () {
      console.log('componentWillUpdate(): 将要更新')
    }
    componentDidUpdate () {
      console.log('componentDidUpdate(): 已经更新')
    }

    componentWillUnmount () {// 清除定时器/解除监听(在清理组件的时候会调用的!!!!!)
      console.log('componentWillUnmount(): 将要被移除')
      clearInterval(this.intervalId)
    }

    removeComponent () {
      ReactDOM.unmountComponentAtNode(document.getElementById('example'))
    }

    render() {
      console.log('render() 渲染组件')
      return (
        <div>
          <h2 style={{opacity:this.state.opacity}}>{this.props.content}</h2>
          <button onClick={this.removeComponent}>不活了</button>
        </div>
      )
    }
  }
  ReactDOM.render(<Fade content="react学不会, 怎么办?"/>, document.getElementById('example'))
</script>

说明:

之后的渲染是因为:
定时器内部修改了state中opacity的值,会自动触发对应的Update声明周期方法。

8 虚拟DOM和DOM diff算法

8.1 效果

在这里插入图片描述

8.2 案例

<script type="text/babel">
  /*
  验证:
  虚拟DOM+DOM Diff算法: 最小化页面重绘
  */

  class HelloWorld extends React.Component {
    constructor(props) {
      super(props)
      this.state = {
          date: new Date()
      }
    }

    /**
     * 用箭头函数回调等价于bind(this)!!!!!!!!!!!
     */
    componentDidMount () {
      setInterval(() => {
        this.setState({
            date: new Date()
        })
      }, 1000)
    }

    render () {
      console.log('render()')
      return (
        <p>
          Hello, <input type="text" placeholder="Your name here"/>!&nbsp;
          <span>It is {this.state.date.toTimeString()}</span>
        </p>
      )
    }
  }

  ReactDOM.render(
    <HelloWorld/>,
    document.getElementById('example')
  )
</script>

8.3 原理

在这里插入图片描述

在这里插入图片描述
如果对你有帮助,记得帮博主一键三连😊哦

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值