PART13 React-基础3

发布于:2022-12-04 ⋅ 阅读:(334) ⋅ 点赞:(0)

组件生命周期

  1. 组件的生命周期: 组件从创建到挂在到页面中运行,再到组件不用时卸载的过程
  2. 生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数
  3. 钩子函数的作用: 为开发人员在不同阶段操作组件提供了时机
  4. 只有类组件,才会有生命周期

生命周期三个阶段

创建时

consturctor=>render=>componentDidMount

钩子函数 触发时机 作用
constructor 创建组件时,最先执行 1. 初始化state 2. 为时间处理程序绑定this
render 每次组件渲染都会触发 渲染UI(注意: 不能调用setState())
componentDidMount 组件挂在(完成DOM渲染)后 1. 发送网络请求 2. DOM操作

更新时

render===>componentDidUpdate
当调用New props组件接收到props属性,setState(),forceUpdate()时候会触发render的钩子函数

钩子函数 触发时机 作用
render 每次组件渲染都会触发 渲染UI(与挂在阶段是同一个render)
componentDidUpdate 组件更新(完成DOM渲染)后 1. 发送网络请求 2. DOM操作 注意: 如果要setState()必须放在一个if条件中

卸载时

组件从页面中消失

钩子函数 触发时机 作用
componentWillUnmount 组件卸载(从页面中消失) 执行清理工作(比如: 清理定时器等)

render-props和高阶组件

复用相似的功能(联想函数封装)

render props模式

  1. 创建Mouse组件,在组件中提供复用的状态逻辑代码(1. 状态 2.操作状态的方法)
  2. 将要复用的状态作为props.render(state)方法的参数,暴露到组件外部
  3. 使用props.render()的返回值作为将要渲染的内容
import React from 'react';
import ReactDOM from 'react-dom';

class Mouse extends React.Component {
    state= {
      x: 0,
      y: 0
    }

    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    //监听鼠标移动事件
    componentDidMount(){
      window.addEventListener('mousemove',this.handleMouseMove)
    }

    render(){
      return this.props.render(this.state)
    }
}


class App extends React.Component {
  render(){
    return (
      <div>
        <h1>render props 模式</h1>
        <Mouse render={(mouse)=>{
          return <p>鼠标位置: {mouse.x} {mouse.y}</p>
        }}/>
      </div>
    )
  }
}

//渲染组件
ReactDOM.render(<App />,document.getElementById('root'))

使用children代替render属性

<Mouse>
	{({x,y}) => <p>鼠标的位置: {x}  {y}</p>}
</Mouse>

//组件内部
this.props.children(this.state)

高阶组件模式(HOC)

包装模式去实现状态逻辑复用

  • 高阶组件,是一个函数,接收要包装的组件,返回增强后的组件
  • 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件
const EnhancedComponent = withHOC(组件名称)

使用步骤

  1. 创建函数,名称约定以with开头
  2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
  3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
  4. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
  5. 调用高阶组件,传入要增强的组件,通过返回值拿到要增强后的组件,并将其渲染到页面中
function withMouse(WrappedComponent){
	class Mouse extends React.Component {
		return Mouse
	}
}

示例

import React from 'react';
import ReactDOM from 'react-dom';



//创建高阶组件
function withMouse(WrappedComponent){
  class Mouse extends React.Component{
    //鼠标状态
    state = {
      x: 0,
      y: 0
    }


    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    //控制逻辑
    componentDidMount(){
      window.addEventListener('mousemove',this.handleMouseMove)
    }

    componentWillUnmount(){
      window.removeEventListener('mousemove',this.handleMouseMove)
    }

    render(){
      return <WrappedComponent {...this.state}></WrappedComponent>
    }
  }
  return Mouse
}

//用来测试高阶组件
const Position = props => (
  <p>
    鼠标当前位置: (x: {props.x},y: {props.y})
  </p>
)

const MousePosition = withMouse(Position)

class App extends React.Component {
render(){
  return (
    <div>
      <h1>render props 模式</h1>
      <MousePosition />
    </div>
  )
}
}

//渲染组件
ReactDOM.render(<App />,document.getElementById('root'))

设置displayName属性

用来区分组件的名称

Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`

function getDisplayName(WrappedComponent){
	return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

传递props

  • 问题: props丢失
  • 原因: 高阶组件没有往下传递props
  • 解决方式: 渲染WrappedComponent时,将state和this.props一起传递给组件
  • 传递方式:
<WrappedComponent {...this.state} {...this.props} />

React原理

setState()的说明

  • setState()是异步操作的
  • 在使用该语法的时候,后面的setState()不要依赖于前面的setState()
  • 之后触发一次render()进行渲染

推荐语法

setState((state,props) => {})这种语法也是异步的形式,state是更新的

this.setState((state,props) => {
	return {
		count: state.count + 1
	}
})
console.log(this.state.count)

第二个参数

在状态更新(页面完成重新渲染)后立即执行某个操作
setState(updater,[callback])

this.setState(
	(state,props)=>{},
	()=>{
		document.title = '更新state后的标题: ' + this.state.count
	}
)

JSX语法的转化过程

  • JSX仅仅是createElement方法的语法糖(简化语法)
  • JSX语法被@babel/preset-react插件编译为createElement() 方法
  • React元素: 是一个对象,用来描述你希望在屏幕上看到的内容

JSX语法 ====> createElement() ====> React元素

组件更新机制

  • setState()的两个作用:
  1. 修改state
  2. 更新组件(UI)
    父组件重新渲染时,也会重新渲染子组件,但指挥渲染当前组件子树(当前组件及其所有组件)

组件性能优化

  1. 减轻state: 只存储根组件渲染相关的数据(比如: count / 列表数据 / loading等)
    不同做渲染的数据不要放在state中,比如定时器id等

避免比不要的重新渲染

  • 组件更新机制: 父组件更新会引起子组件也被更新
    问题: 子组件没有任何变化的情况下也会重新渲染
    解决方法: 使用钩子函数shouldComponentUpdate(nextProps,nextState)
    触发时机: 更新阶段的钩子函数,组件重新渲染执行(shouldComponentUpdate ----> render)
class Hello extends Component {
	shouldComponentUpdate(nextProps,nextState) {
		//根据条件,决定是否重新渲染组件
		return nextState.number !== this.state.number
	}
}

纯组件

定义冲纯组件只需要将React.Component换成React.PureComponent
区别: PureComponent内部自动实现了shouldComponentUpdate钩子,不需要手动比较

  • 说明: 纯组件内部的对比时shallow comare(浅层对比)
  • 对于值类型来说: 比较两个值是否相同(直接赋值即可)
  • 对于引用类型来说: 值比较对象的引用(地址)是否相同。state或props中属性值为引用类型时,应该创建新数据,不要直接修改原来数据!
const newObj  = { ...this.state.obj,number: Math.floor(Math.random() * 3) }
this.setState(()=>{
	return {
		obj: newObj
	}
})

虚拟DOM和Diff算法

  • React更新视图的思想是: 只要state变化就会重新渲染视图
  • 部分更新,只更新变化的地方

虚拟DOM

本质上就是液体个JS对象,用来描述希望在屏幕上看到的内容(UI)

Diff算法

  1. 初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象(树)
  2. 根据虚拟DOM生成真正的DOM,渲染到页面中
  3. 当数据变化后(setState()),重新根据的数据,创建新的虚拟DOM对象(树)
  4. 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
  5. 最终,React只将变化的内容跟新(patch)到DOM中,重新渲染页面
本文含有隐藏内容,请 开通VIP 后查看