开发中咱们并不能直接经过修改 state
的值来让界面发生更新:react
state
以后, 但愿 React
根据最新的 Stete
来从新渲染界面, 可是这种方式的修改 React
并不知道数据发生了变化React
并无实现相似于 Vue2
中的 Object.defineProperty
或者 Vue3
中的Proxy
的方式来监听数据的变化setState
来告知 React
数据已经发生了变化疑惑: 在组件中并无实现 steState
方法, 为何能够调用呢?git
setState
方法是从 Component
中继承过来的<details>
<summary>setState是异步更新的</summary>
<img src="https://gitee.com/xmkm/cloudPic/raw/master/img/20201001214531.png" />
</details>github
为何setState
设计为异步呢?算法
setState
设计为异步其实以前在 GitHub
上也有不少的讨论简单的总结: setState
设计为异步, 能够显著的提升性能性能优化
setState
都进行一次更新, 那么意味着 render
函数会被频繁的调用界面从新渲染, 这样的效率是很低的若是同步更新了 state
, 但尚未执行 render
函数, 那么state
和props
不能保持同步异步
state
和props
不能保持一致性, 会在开发中产生不少的问题setState
异步更新state
后的值?方式一: setState
的回调函数
setState
接收两个参数: 第二个参数是回调函数(callback
), 这个回调函数会在state
更新后执行componentDidUpdate
生命周期函数
- 其实能够分红两种状况
- 在组件生命周期或React合成事件中,
setState
是异步的- 在
setTimeou
或原生DOM事件中,setState
是同步的
setTimeout
中的更新 —> 同步更新DOM
事件 —> 同步更新经过setState
去修改message
,是不会对其余 state
中的数据产生影响的源码分析
setState
, 只会生效最后一次state
setState
合并时进行累加: 给setState传递函数, 使用前一次state
中的值React
的渲染流程:React
在 props
或 state
发生改变时,会调用 React
的 render
方法,会建立一颗不一样的树React
须要基于这两颗不一样的树之间的差异来判断如何有效的更新UI
:若是一棵树参考另一棵树进行彻底比较更新, 那么即便是最早进的算法, 该算法的复杂程度为 O(n$^3$),其中 n 是树中元素的数量性能
React
中使用了该算法, 那么展现 1000
个元素所须要执行的计算量将在十亿
的量级范围因而,React
对这个算法进行了优化,将其优化成了O(n)
,如何优化的呢?学习
当节点为不一样的元素,React会拆卸原有的树,而且创建起新的树:
<a>
变成 <img>
,从 <Article>
变成 <Comment>
,或从 <button>
变成 <div>
都会触发一个完整的重建流程DOM
节点也会被销毁,组件实例将执行 componentWillUnmount()
方法DOM
节点会被建立以及插入到 DOM
中,组件实例将执行 componentWillMount()
方法,紧接着 componentDidMount()
方法好比下面的代码更改:
好比下面的代码更改:
React
知道只须要修改 DOM
元素上的 className
属性好比下面的代码更改:
style
属性时,React
仅更新有所改变的属性。React
知道只须要修改 DOM
元素上的 color
样式,无需修改 fontWeight
若是是同类型的组件元素:
React
会更新该组件的props
,而且调用componentWillReceiveProps()
和 componentWillUpdate()
方法render()
方法,diff
算法将在以前的结果以及新的结果中进行递归在默认条件下,当递归 DOM
节点的子元素时,React
会同时遍历两个子元素的列表;当产生差别时,生成一个 mutation
咱们来看一下在最后插入一条数据的状况:👇
可是若是咱们是在前面插入一条数据:
<li>星际穿越</li>
和 <li>盗梦空间</li>
的不变key
属性:方式一:在<font color='red'>最后</font>位置插入数据
key
意义并不大方式二:在<font color='red'>前面</font>插入数据
key
的状况下,全部的<li>
都须要进行修改在下面案例: 当子元素 (这里的li
元素) 拥有 key
时
React
使用 key
来匹配原有树上的子元素以及最新树上的子元素:key
为 333
的元素插入到最前面的位置便可
key
的注意事项:
key
应该是惟一的key
不要使用随机数(随机数在下一次render时,会从新生成一个数字)- 使用
index
做为key
,对性能是没有优化的
咱们使用以前的一个嵌套案例:
当点击 +1
时,会从新调用 App
的 render
函数
那么,咱们能够思考一下,在之后的开发中,咱们只要是修改 了App中的数据,全部的子组件都须要从新render
,进行 diff
算法,性能必然是很低的:
render
render
方法如何来控制 render
方法是否被调用呢?
shouldComponentUpdate
方法便可React
给咱们提供了一个生命周期方法shouldComponentUpdate
(不少时候,咱们简称为SCU
),这个方法接受参数,而且须要有返回值;主要做用是: 控制当前类组件对象是否调用render
方法
该方法有两个参数:
nextProps
修改以后, 最新的 porps
属性nextState
修改以后, 最新的 state
属性该方法返回值是一个 booolan 类型
true
, 那么就须要调用 render
方法false
, 那么不须要调用 render
方法好比咱们在App中增长一个message
属性:
JSX
中并没有依赖这个message
, 那么它的改变不该该引发从新渲染setState
修改 state
中的值, 因此最后 render
方法仍是被从新调用了// 决定当前类组件对象是否调用render方法 // 参数一: 最新的props // 参数二: 最新的state shouldComponentUpdate(nextProps, nextState) { // 默认是: return true // 不须要在页面上渲染则不调用render函数 return false }
若是全部的类, 咱们都须要手动来实现 shouldComponentUpdate
, 那么会给咱们开发者增长很是多的工做量
shouldComponentUpdate
中的各类判断目的是什么?props
或者 state
中数据是否发生了改变, 来决定shouldComponentUpdate
返回 true
或 false
事实上 React
已经考虑到了这一点, 因此 React
已经默认帮咱们实现好了, 如何实现呢?
state
和 porps
, 若是组件内没有依赖 porps
或state
将不会调用render
state
或props
, 但却调用了render
函数这个方法中,调用
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
,这个shallowEqual
就是进行浅层比较:
render
: 在没有依赖 state
或 props
但却从新渲染 render
问题咱们须要使用一个高阶组件memo
:
counter
发生改变时,Header、Banner、ProductList的函数不会从新执行,而 Footer 的函数会被从新执行import React, { PureComponent, memo } from 'react' // MemoHeader: 没有依赖props,不会被从新调用render渲染 const MemoHeader = memo(function Header() { console.log('Header被调用') return <h2>我是Header组件</h2> })