52.React学习 —— 组件优化

Fragment

react组件中只能有一个根组件.
之前使用div包裹的方式会给html结构增加很多无用的层级

为了解决这个问题,可以使用React.Fragment

function Hello(){
    return (
      // 渲染到页面之后,这个div就是一个多余的
      <div>
        <h1>fragment</h1>
        <p>hello react</p>
      </div>
    ) 
}

// 将上面的写法,修改为下面的写法
function Hello(){
    return (
      // 这样就只会渲染h1和p
      <React.Fragment>
        <h1>fragment</h1>
        <p>hello react</p>
      </React.Fragment>
    ) 
}

// 简写形式
function Hello(){
    return (
      // 这是React.Fragment的简写形式
      <>
        <h1>fragment</h1>
        <p>hello react</p>
      </>
    ) 
}

React性能优化

  • 减轻state 跟页面渲染无关的数据,不要放在state中(比如定时器的id.不要放在state中)

    • 可以有效减少页面重新渲染次数
  • shouldComponentUpdate (减轻不必要的重新渲染)

    • **组件更新的机制: **父组件重新渲染时,也会重新渲染子组件

    • 如何避免不必要的重新渲染呢?

    • 解决方式:使用钩子函数 shouldComponentUpdate(nextProps, nextState)

    • 作用:通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染

    • 触发时机:更新阶段的钩子函数,组件重新渲染前执行 (shouldComponentUpdate => render)

      // 父组件
      class Far extends Component {
        state = {
          num: 0
        }
        
        // 返回min~max之间的随机整数(包含min和max)
        getRandomIntInclusive(min, max) {
          min = Math.ceil(min)
          max = Math.floor(max)
          return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值
        }
        
        handle = () => {
          let num = this.getRandomIntInclusive(1, 3)
          this.setState({
            num
          })
        }
        
        //有效减少组件渲染次数
        shouldComponentUpdate(props, state) {
          // this.state 原来的state
          // state 最新的state
          return this.state.num !== state.num 
        }
        
        render() {
          console.log('父组件渲染了')
          return (
            <div>
              <h1 onClick={this.handle}>Far组件</h1>
              <p>{this.state.num}</p>
              <Son num={this.state.num}></Son>
            </div>
          )
        }
      }
      
      // 不管子组件有没有用到父组件的数据,只要父组件重新渲染了,子组件就会跟着渲染
      
      // 子组件
      class Son extends Component {
        //有效减少组件渲染次数
        shouldComponentUpdate(props, state) {
          // this.props 原来的props
          // props 最新的props
          return props.num !== this.props.num 
        }
        
        render() {
          console.log('Son组件渲染了')
          return (
            <div>
              <h1>Son组件</h1>
              <p>{this.props.num}</p>
            </div>
          )
        }
      }
      
  • 纯组件 pureComponent

    • PureComponent 与 React.Component 功能相似,直接替换即可

    • 区别:PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较

    • 原理:纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件

    • 注意: 纯组件内部的对比是 shallow compare(浅层对比)

      • 对于引用类型来说:只比较对象的引用(地址)是否相同
      • 所以,state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
      class Far extends PureComponent {
        state = {
          obj: {
            num: 0
          }
        }
      
        getRandomIntInclusive(min, max) {
          min = Math.ceil(min)
          max = Math.floor(max)
          return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值
        }
        
        handle = () => {
          // 错误的写法
          // 重新设置的obj和上一次的obj是同一个.
          // 组件不会重新渲染了
          // let num = this.getRandomIntInclusive(1, 3)
          // let obj = this.state.obj 或者 let {obj} = this.state
          // obj.num = num
           
          // 正确的写法:
          let num = this.getRandomIntInclusive(1, 3)
          // 创建一个新的对象
          let obj = { num }
          console.log(num)
          this.setState({
            obj
          })
        }
      
        render() {
          console.log('父组件渲染了')
          return (
            <div>
              <h1 onClick={this.handle}>Far组件</h1>
              <p>{this.state.obj.num}</p>
              <Son num={this.state.obj.num}></Son>
            </div>
          )
        }
      }
      

React.forwardRef(不常用.可以通过props传递)

函数组件不可以添加ref属性

作用: 将函数组件内部的元素,交给父组件使用

// 实现: 
// App组件
const ref = React.createRef()
class App extends React.Component {
  componentDidMount() {
    // 在app组件中,通过ref获取到了button的Dom对象
    console.log(ref.current)
  }
  
  render() {
    return (
      <div>
        // 使用React.forwardRef包装后的FancyButton就可以添加ref属性了
        <FancyButton ref={ref}>Click me!</FancyButton>
      </div>
    )
  }
}
// FancyButton
const FancyButton = React.forwardRef((props, ref) => {
  console.log(props, ref)
  return (
    <button ref={ref} className='FancyButton'>
      {props.children}
    </button>
  )
})

// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();

<FancyButton ref={ref}>Click me!</FancyButton>;

Portal

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>App组件</h1>
		// 在app组件中使用了portal组件
		// 但是渲染dom的时候,portal的内容不在这个div中
        <Portal></Portal>
      </div>
    )
  }
}

export default class Portal extends Component {
  constructor() {
    super()
    this.container = document.createElement('div')
  }
  
  componentDidMount() {
    document.body.appendChild(this.container)
  }
  
  render() {
      
    let node = (
      <div>
        <div className='modal'>模态框</div>
        <button>按钮</button>
      </div>
    )
    // 当前portal组件不直接返回元素,而是返回一个portal
    // react底层会将node的内容,渲染到this.container中
    return ReactDOM.createPortal(node, this.container)
  }

  componentWillUnmount() {
    document.body.removeChild(this.container)
  }
}

上一篇:组件通讯
下一篇:高阶组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值