事件系统(SyntheticEvent)
react中的合成事件是对浏览器event对象的二次封装。当绑定一个事件到dom对象上时,react会把该事件绑定到document对象上,并且注册一个事件集合。利用事件委托机制(冒泡),当document触发相关事件时,通过触发当事件名称和触发对象在事件集合中找到对应的回调函数,然后执行。
// 伪代码
function onClick() {}
<div id="box" onClick={onCLick}></div>
// react通过上述代码维护一个事件集合
const eventMap = {
onClick: {
#box: onClick
}
}
document.addEventListener('click', event => {
const fun = eventMap.onClick.#box;
fun();
})
生命周期函数
挂载阶段:constructor, getDerivedStateFromProps, render, componentDidMount
constructor:初始化state;为事件绑定this;
getDerivedStateFromProps(props, state): 初始化挂载和更新时,render之前调用,返回一个对象来更新this.state。
componentDidMount:第一次render执行之后触发,可以获取到真实对dom对象。
更新阶段:getDerivedStateFromProps,shouldComponentUpdate,render,getSnpashotBeforeUpdate,componentDidUpdate
shouldComponentUpdate(nextProps, nextState): 返回一个boolean,决定是否执行render函数。如果返回false,则不执行重新渲染。PureComponent就是在该函数中对nextProps和this.props,nextState和this.state分别做了浅比较(引用相等)。实践中可以引入immutable.js对其做深比较。
getSnapshotBeforeUpdate(prevProps, prevState):在dom更新执行执行,可以用来保存更新之前的快照,比如滚动条位置等。返回值会作为componentDidUpdate的第三个参数。
componentDidUpdate(prevProps, prevState, snapshot): dom更新之后执行,可以获取到最新的dom。注意:在这个函数中没有条件语句执行setState会造成死循环。didUpdate=》setState =》render =》didUpdate。。。。
卸载阶段:componentWillUnMount
componentWillUnMount:组件卸载之前执行。用来清除定时器获取监听器等。
JSX
jsx是js的语法扩展,经过babel编译之后,会变成React.createElement的调用。
// 伪代码
<div id="box"></div>
经过babel编译
React.createElement('div', { id: 'box' })
hook
hook解决了什么问题:
1. 更便利地抽取和复用公共逻辑。在class中,只能通过高阶组件等方式去实现,造成组件嵌套过深。
2. 将相互关联的逻辑(订阅和取消/更新之后执行副作用)抽取成更小粒度的函数(useEffect)。而在class中,相关联的逻辑可能分散在不同的生命周期函数中,比如在componentDidMount中订阅,而在comPonentWillUnMount中取消订阅。
hook的原理:
hook本质上就是包含公共逻辑的函数,
hook函数在第一次执行的时候,会将initialValue按照执行的顺序依次push到一个memoizedState数组里面,这样hook的调用顺序和数组里面保存的值的索引就会对应上。
// 伪代码
const [name, setName] = useState('tom');
const [age, setAge] = useState('12');
memoizedState = ['tom', '12']; // 保存state的数组
当调用setName或者setAge的时候,因为这些函数保存了hook的调用顺序(闭包),所以可以根据索引修改memoizedState中保存的对应的值,然后重新执行函数组件执行渲染。
因为hook的调用顺序和memoizedState中保存的值的索引有对应关系,所以如果改hook的执行顺序和次数的话会直接影响到从memoizedState中获取对应值的逻辑,因此hook只能在函数组件的最顶层调用,不能在判断语句或者循环中调用。
fiber
fiber是用来描述dom节点的一种数据结构,也称为虚拟dom节点。
react16之前,虚拟dom树是树型结构,react在协调阶段只能通过递归的方式去diff,一旦开始diff,那么就不能停止。而浏览器渲染引擎和js引擎是互斥的,如果diff过程长时间占用线程,就回导致浏览器渲染卡顿。
react16引入了fiber的概念,将之前的树结构改成链表结构,每个fiber节点都保存了父节点,兄弟节点和子节点,这样diff过程就可以由递归变成遍历,并且通过requestIdleCallback达到暂停和重启遍历过程的目的。这样在浏览器由高优先级的渲染任务情况下,就可以把暂停diff过程,将执行权交还给渲染引擎,等到浏览器空闲的时候,重启diff过程。