06- React的原理及优化

105 阅读3分钟

学习目标

  • 能够知道 setState() 更新数据时异步的
  • 能够知道 JSX 语法的转化过程
  • 能够说出React组件的更新机制
  • 能够对组件进行性能优化
  • 能够说出虚拟DOM和Diff算法

一、更新数据

1、更新数据时异步的

setState() 更新数据时异步的

注意:使用该语法时,后面的setState() 不能依赖上一个

可以多次调用setState(), 只会触发一次重新渲染

class App extends React.Component {
  state = {
    count: 1
  }
  handleclick = () => {
    this.setState({
      count: this.state.count + 1
    })
    console.log('count',this.state.count) // 1
    // 页面显示的是2
  }
  render() {
    return (
    	<div>
      	<h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}> +1 </button>
      </div>
    )
  }
}

2、解决方式 (推荐方式)

第一个参数

State: 表示最新的state

props:表示最新的props

state = {
    count: 1
  }

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

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

第二个参数

是个回调函数

this.setState((state,props) => {
  return {
    count: state.count + 1
  }
},() => {
  console.log('状态更新完成之后立即执行',this.state)
})

二、JSX 语法的转化过程

  • JSX 仅仅是 createElement() 方法的语法糖 ( 简化语法 )
  • JSX 语法被 @babel/preset-react 插件编译为 createElement() 方法
  • React 元素:是一个对象,用来描述你希望在屏幕上看到的内容
JSX语法createElement()React元素
// JSX 语法
const element = (
	<h1 className="greeting">
  	Hello JSX;
  </h1>
)
// createElement()
const element = React.createElement(
	'h1',
  {className:"greeting",
  "Hello JSX"
  }
)
// React 元素
const element = {
  type:"h1",
  props: {
    className: 'greeting',
    children: 'Hello JSX'
  }
}

三、组件更新机制

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

例如

![image-20221130113409347](/Users/jirongliang/Library/Application Support/typora-user-images/image-20221130113409347.png)

四、组件性能优化

1、减轻state

  • 减轻state:只存储跟组件渲染相关的数据(比如:count / 列表数据 / loading 等)
  • 注意:不用渲染的数据不要放在state中,比如定时器 id等
  • 对于需要多个方法共享又和渲染没有关系,应该放在this中
class Hello extends Component {
  timerId = null;
  componentDidMount() {
    this.timerId = setInterVal(() => {},2000)
  }
  componentWillUnmount() {
    clearInterval(this.TimerId)
  }
  render() { …… }
}

2、避免不必要的重新渲染

  • 组件更新机制:父组件更新子组件也跟着更新
  • 问题:造成子组件没变化也会更新
  • 解决方式:使用狗子函数should ComponentUpdate(nextProps,nextState)
  • 作用:返回值决定是否更新true更新,false不更新
  • 触发时机:shouldComponentUpdate ---> render
class Hello extends React.Component {
  shouldComponentUpdate(nextProps,nextState) {
   	console.log("最新状态:",nextState,"当前状态:",this.state)
    console.log("最新props:",nextProps,"当前props:",this.props)
    // 与当前的值不一样就更新
    return nextState.number !== this.state.number
  }
  render () {……}
}

3、纯组件

  • 纯组件:PureComponent 与 React.Component功能相似
  • 区别:PureComponent内部自动实现了shouldComponentUpdate
  • 原理:纯组件内部通过分别对比前后俩次props和state的值,来决定重新渲染组件
class Hello extends React.PureComponent {
  render() {
    return (
    	<div>纯组件</div>
    )
  }
}

1、比较原理

纯组件内部的对比上shallow compare(浅层比较)

引用类型来说:只比较对象的引用是否相同

基本类型:比较值是否相同 没有问题

// 对象
const newObj = {...state.obj,number:2}
setState({obj:newObj})

// 数组也是
// 不能用数组的push/unshift直接修改
// 应该用concat或者slice 返回新数组的方法
this.setState({
  list: [...this.state.list,新数据]
})

五、虚拟DOM和Diff算法

render方法调用并不意味着浏览器中的重新渲染!!!!

render方法调用仅仅说明要进行diff

1、虚拟DOM

:本质上就是JS对象,用来描述你希望在屏幕上看到的内容

/* 虚拟DOM对象 */
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello JSX!'
  }
}

/* HTML结构 */
<h1 class="greeting">Hello JSX!</h1>

3、Diff算法

执行过程

  1. 初次渲染时,React会根据初试state(Model) ,创建一个虚拟DOM对象
  2. 根据虚拟DOM生成真正的DOM,渲染到页面中
  3. 当数据变化后(setState()), 重新根据新的数据,创建新的虚拟DON对象
  4. 与上一次得到的虚拟DOM树,使用Diff算法对比,得到需要更新的内容
  5. 最终,React只将变化的内容更新到DOM中,重新渲染