React生命周期有哪些
15生命周期
初始化阶段
-
constructor
构造函数 -
getDefaultProps
props
默认值 -
getInitialState
state
默认值
挂载阶段
-
componentWillMount
组件初始化渲染前调用 -
render
组件渲染 -
componentDidMount
组件挂载到DOM
后调用
更新阶段
-
componentWillReceiveProps
组件将要接收新props
前调用 -
shouldComponentUpdate
组件是否需要更新 -
componentWillUpdate
组件更新前调用 -
render
组件渲染 -
componentDidUpdate
组件更新后调用
卸载阶段
-
componentWillUnmount
组件卸载前调用
16生命周期
初始化阶段
-
constructor
构造函数 -
getDefaultProps
props
默认值 -
getInitialState
state
默认值
挂载阶段
-
staticgetDerivedStateFromProps(props,state)
-
render
-
componentDidMount
getDerivedStateFromProps
:组件每次被rerender
的时候,包括在组件构建之后(虚拟dom
之后,实际dom
挂载之前),每次获取新的props
或state
之后;每次接收新的props之后都会返回一个对象作为新的state
,返回null则说明不需要更新state
;配合componentDidUpdate
,可以覆盖componentWillReceiveProps
的所有用法
更新阶段
-
staticgetDerivedStateFromProps(props,state)
-
shouldComponentUpdate
-
render
-
getSnapshotBeforeUpdate(prevProps,prevState)
-
componentDidUpdate
getSnapshotBeforeUpdate
:触发时间:update
发生的时候,在render
之后,在组件dom
渲染之前;返回一个值,作为componentDidUpdate
的第三个参数;配合componentDidUpdate
, 可以覆盖componentWillUpdate
的所有用法
卸载阶段
componentWillUnmount
错误处理
componentDidCatch
React16
新的生命周期弃用了 componentWillMount、componentWillReceivePorps,componentWillUpdate
新增了 getDerivedStateFromProps、getSnapshotBeforeUpdate
来代替弃用的三个钩子函数。
React16
并没有删除这三个钩子函数,但是不能和新增的钩子函数混用,React17
将会删除这三个钩子函数,新增了对错误的处理(componentDidCatch
)
React如何实现自己的事件机制?
React
事件并没有绑定在真实的 Dom
节点上,而是通过事件代理,在最外层的 document
上对事件进行统一分发。
组件挂载、更新时:
-
通过
lastProps
、nextProps
判断是否新增、删除事件分别调用事件注册、卸载方法。 -
调用
EventPluginHub
的enqueuePutListener
进行事件存储 -
获取
document
对象。 -
根据事件名称(如
onClick
、onCaptureClick
)判断是进行冒泡还是捕获。 -
判断是否存在
addEventListener
方法,否则使用attachEvent
(兼容IE)。 -
给
document
注册原生事件回调为dispatchEvent
(统一的事件分发机制)。
事件初始化:
-
EventPluginHub
负责管理React
合成事件的callback
,它将callback
存储在listenerBank
中,另外还存储了负责合成事件的Plugin
。 -
获取绑定事件的元素的唯一标识
key
。 -
将
callback
根据事件类型,元素的唯一标识key
存储在listenerBank
中。 -
listenerBank
的结构是:listenerBank[registrationName][key]
。
触发事件时:
-
触发
document
注册原生事件的回调dispatchEvent
-
获取到触发这个事件最深一级的元素
-
遍历这个元素的所有父元素,依次对每一级元素进行处理。
-
构造合成事件。
-
将每一级的合成事件存储在
eventQueue
事件队列中。 -
遍历
eventQueue
。 -
通过
isPropagationStopped
判断当前事件是否执行了阻止冒泡方法。 -
如果阻止了冒泡,停止遍历,否则通过
executeDispatch
执行合成事件。 -
释放处理完成的事件。
React
在自己的合成事件中重写了stopPropagation
方法,将isPropagationStopped
设置为true
,然后在遍历每一级事件的过程中根据此遍历判断是否继续执行。这就是React
自己实现的冒泡机制。
原生事件和React事件的区别?
-
React
事件使用驼峰命名,而不是全部小写。 -
通过
JSX
, 你传递一个函数作为事件处理程序,而不是一个字符串。 -
在
React
中你不能通过返回false
来阻止默认行为。必须明确调用preventDefault
。
React的合成事件是什么?
React
根据 W3C
规范定义了每个事件处理函数的参数,即合成事件。
事件处理程序将传递 SyntheticEvent
的实例,这是一个跨浏览器原生事件包装器。它具有与浏览器原生事件相同的接口,包括 stopPropagation()
和 preventDefault()
,在所有浏览器中他们工作方式都相同。
React
合成的 SyntheticEvent
采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。
另外,不管在什么浏览器环境下,浏览器会将该事件类型统一创建为合成事件,从而达到了浏览器兼容的目的。
React和原生事件的执行顺序是什么?可以混用吗?
React
的所有事件都通过 document
进行统一分发。当真实 Dom
触发事件后冒泡到 document
后才会对 React
事件进行处理。
所以原生的事件会先执行,然后执行 React
合成事件,最后执行真正在 document
上挂载的事件
React
事件和原生事件最好不要混用。原生事件中如果执行了 stopPropagation
方法,则会导致其他 React
事件失效。因为所有元素的事件将无法冒泡到 document
上,导致所有的 React
事件都将无法被触发
虚拟Dom是什么?
在原生的 JavaScript
程序中,我们直接对 DOM
进行创建和更改,而 DOM
元素通过我们监听的事件和我们的应用程序进行通讯。
而 React
会先将你的代码转换成一个 JavaScript
对象,然后这个 JavaScript
对象再转换成真实 DOM
。这个 JavaScript
对象就是所谓的虚拟 DOM
。
当我们需要创建或更新元素时, React
首先会让这个 VitrualDom
对象进行创建和更改,然后再将 VitrualDom
对象渲染成真实DOM。
当我们需要对 DOM
进行事件监听时,首先对 VitrualDom
进行事件监听, VitrualDom
会代理原生的 DOM
事件从而做出响应。
虚拟Dom比普通Dom更快吗?
直接操作 DOM
是非常耗费性能的,但是 React
使用 VitrualDom
也是无法避免操作 DOM
的。
如果是首次渲染, VitrualDom
不具有任何优势,甚至它要进行更多的计算,消耗更多的内存。
VitrualDom
的优势在于 React
的 Diff
算法和批处理策略, React
在页面更新之前,提前计算好了如何进行更新和渲染 DOM
。VitrualDom
帮助我们提高了开发效率,在重复渲染时帮助我们计算如何更高效的更新,而不是它比 DOM
操作更快。
React组件的渲染流程是什么?
-
使用
React.createElement
或JSX
编写React
组件,实际上所有的JSX
代码最后都会转换成React.createElement(...)
,Babel
帮助我们完成了这个转换的过程。 -
createElement
函数对key
和ref
等特殊的props
进行处理,并获取defaultProps
对默认props
进行赋值,并且对传入的孩子节点进行处理,最终构造成一个ReactElement
对象(所谓的虚拟DOM
)。 -
ReactDOM.render
将生成好的虚拟DOM
渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
。
为什么代码中一定要引入React?
JSX
只是为 React.createElement(component,props,...children)
方法提供的语法糖。
所有的 JSX
代码最后都会转换成 React.createElement(...)
, Babel
帮助我们完成了这个转换的过程。
所以使用了 JSX
的代码都必须引入 React
。
为什么React组件首字母必须大写?
babel
在编译时会判断 JSX
中组件的首字母,当首字母为小写时,其被认定为原生 DOM
标签, createElement
的第一个变量被编译为字符串;当首字母为大写时,其被认定为自定义组件, createElement
的第一个变量被编译为对象;
React在渲染真实Dom时做了哪些性能优化?
在 IE(8-11)
和 Edge
浏览器中,一个一个插入无子孙的节点,效率要远高于插入一整个序列化完整的节点树。
React
通过 lazyTree
,在 IE(8-11)
和 Edge
中进行单个节点依次渲染节点,而在其他浏览器中则首先将整个大的 DOM
结构构建好,然后再整体插入容器。
并且,在单独渲染节点时, React
还考虑了 fragment
等特殊节点,这些节点则不会一个一个插入渲染。
HOC在业务场景中有哪些实际应用场景?
HOC
可以实现的功能:
-
组合渲染
-
条件渲染
-
操作
props
-
获取
refs
-
状态管理
-
操作
state
-
渲染劫持
HOC
在业务中的实际应用场景:
-
日志打点
-
权限控制
-
双向绑定
-
表单校验
高阶组件(HOC)和Mixin的异同点是什么?
Mixin
和HOC
都可以用来解决React
的代码复用问题。
-
Mixin
可能会相互依赖,相互耦合,不利于代码维护 -
不同的
Mixin
中的方法可能会相互冲突 -
Mixin
非常多时,组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性
而 HOC
的出现可以解决这些问题:
-
高阶组件就是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合
-
高阶组件也有可能造成冲突,但我们可以在遵守约定的情况下避免这些行为
-
高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担
Hook有哪些优势?
- 减少状态逻辑复用的风险
Hook
和 Mixin
在用法上有一定的相似之处,但是 Mixin
引入的逻辑和状态是可以相互覆盖的,而多个 Hook
之间互不影响,这让我们不需要在把一部分精力放在防止避免逻辑复用的冲突上。在不遵守约定的情况下使用 HOC
也有可能带来一定冲突,比如 props
覆盖等等,使用 Hook
则可以避免这些问题。
- 避免地狱式嵌套
大量使用 HOC
的情况下让我们的代码变得嵌套层级非常深,使用 HOC
,我们可以实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。
- 让组件更容易理解
在使用 class
组件构建我们的程序时,他们各自拥有自己的状态,业务逻辑的复杂使这些组件变得越来越庞大,各个生命周期中会调用越来越多的逻辑,越来越难以维护。使用 Hook
,可以让你更大限度的将公用逻辑抽离,将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割。
- 使用函数代替class
相比函数,编写一个 class
可能需要掌握更多的知识,需要注意的点也越多,比如 this
指向、绑定事件等等。另外,计算机理解一个 class
比理解一个函数更快。Hooks
让你可以在 classes
之外使用更多 React
的新特性。
网友评论