react
React生命周期有哪些
15生命周期
初始化阶段
-
constructor构造函数 -
getDefaultPropsprops默认值 -
getInitialStatestate默认值
挂载阶段
-
componentWillMount组件初始化渲染前调用 -
render组件渲染 -
componentDidMount组件挂载到DOM后调用
更新阶段
-
componentWillReceiveProps组件将要接收新props前调用 -
shouldComponentUpdate组件是否需要更新 -
componentWillUpdate组件更新前调用 -
render组件渲染 -
componentDidUpdate组件更新后调用
卸载阶段
-
componentWillUnmount组件卸载前调用
16生命周期
初始化阶段
-
constructor构造函数 -
getDefaultPropsprops默认值 -
getInitialStatestate默认值
挂载阶段
-
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的新特性。