react中这两个生命周期会触发死循环
componentWillUpdate
生命周期在shouldComponentUpdate
返回true后被触发。在这两个生命周期只有视图更新就会触发,因而不能再这两个生命周期中应用setState。否则会导致死循环
react性能优化是在哪个生命周期函数中
在shouldComponentUpdate 这个办法中,这个办法次要用来判断是否须要调用render办法重绘DOM
因为DOM的描述十分耗费性能,如果可能在shouldComponentUpdate办法中能写出更优化的 diff算法,极大的进步性能
React有哪些优化性能的伎俩
类组件中的优化伎俩
- 应用纯组件
PureComponent
作为基类。 - 应用
React.memo
高阶函数包装组件。 - 应用
shouldComponentUpdate
生命周期函数来自定义渲染逻辑。
办法组件中的优化伎俩
- 应用
useMemo
。 - 应用
useCallBack
。
其余形式
- 在列表须要频繁变动时,应用惟一 id 作为 key,而不是数组下标。
- 必要时通过扭转 CSS 款式暗藏显示组件,而不是通过条件判断显示暗藏组件。
- 应用
Suspense
和 lazy 进行懒加载,例如:
import React, { lazy, Suspense } from "react";export default class CallingLazyComponents extends React.Component { render() { var ComponentToLazyLoad = null; if (this.props.name == "Mayank") { ComponentToLazyLoad = lazy(() => import("./mayankComponent")); } else if (this.props.name == "Anshul") { ComponentToLazyLoad = lazy(() => import("./anshulComponent")); } return ( <div> <h1>This is the Base User: {this.state.name}</h1> <Suspense fallback={<div>Loading...</div>}> <ComponentToLazyLoad /> </Suspense> </div> ) }}
React 的生命周期办法有哪些?
componentWillMount
:在渲染之前执行,用于根组件中的 App 级配置。componentDidMount
:在第一次渲染之后执行,能够在这里做AJAX申请,DOM 的操作或状态更新以及设置事件监听器。componentWillReceiveProps
:在初始化render
的时候不会执行,它会在组件承受到新的状态(Props)时被触发,个别用于父组件状态更新时子组件的从新渲染shouldComponentUpdate
:确定是否更新组件。默认状况下,它返回true
。如果确定在state
或props
更新后组件不须要在从新渲染,则能够返回false
,这是一个进步性能的办法。componentWillUpdate
:在shouldComponentUpdate
返回true
确定要更新组件之前件之前执行。componentDidUpdate
:它次要用于更新DOM以响应props
或state
更改。componentWillUnmount
:它用于勾销任何的网络申请,或删除与组件关联的所有事件监听器。
约束性组件( controlled component)与非约束性组件( uncontrolled component)有什么区别?
在 React中,组件负责管制和治理本人的状态。
如果将HTML中的表单元素( input、 select、 textarea等)增加到组件中,当用户与表单产生交互时,就波及表单数据存储问题。依据表单数据的存储地位,将组件分成约東性组件和非约東性组件。
约束性组件( controlled component)就是由 React管制的组件,也就是说,表单元素的数据存储在组件外部的状态中,表单到底出现什么由组件决定。
如下所示, username没有存储在DOM元素内,而是存储在组件的状态中。每次要更新 username时,就要调用 setState更新状态;每次要获取 username的值,就要获取组件状态值。
class App extends Component { //初始化状态 constructor(props) { super(props); this.state = { username: "有课前端网", }; } //查看后果 showResult() { //获取数据就是获取状态值 console.log(this.state.username); } changeUsername(e) { //原生办法获取 var value = e.target.value; //更新前,能够进行脏值检测 //更新状态 this.setState({ username: value, }); } //渲染组件 render() { //返回虚构DOM return ( <div> <p> {/*输入框绑定va1ue*/} <input type="text" onChange={this.changeUsername.bind(this)} value={this.state.username} /> </p> <p> <button onClick={this.showResult.bind(this)}>查看后果</button> </p> </div> ); }}
非约束性组件( uncontrolled component)就是指表单元素的数据交由元素本身存储并解决,而不是通过 React组件。表单如何出现由表单元素本身决定。
如下所示,表单的值并没有存储在组件的状态中,而是存储在表单元素中,当要批改表单数据时,间接输出表单即可。有时也能够获取元素,再手动批改它的值。当要获取表单数据时,要首先获取表单元素,而后通过表单元素获取元素的值。
留神:为了不便在组件中获取表单元素,通常为元素设置ref属性,在组件外部通过refs属性获取对应的DOM元素。
class App extends Component { //查看后果 showResult() { //获取值 console.log(this.refs.username.value); //批改值,就是批改元素本身的值 this.refs.username.value = "业余前端学习平台"; //渲染组件 //返回虚构DOM return ( <div> <p> {/*非约束性组件中,表单元素通过 defaultvalue定义*/} <input type="text" ref=" username" defaultvalue="有课前端网" /> </p> <p> <button onClick={this.showResult.bind(this)}>查看后果</button> </p> </div> ); }}
尽管非约東性组件通常更容易实现,能够通过refs间接获取DOM元素,并获取其值,然而 React倡议应用约束性组件。次要起因是,约東性组件反对即时字段验证,容许有条件地禁用/启用按钮,强制输出格局等。
React 父组件如何调用子组件中的办法?
- 如果是在办法组件中调用子组件(>= [email protected]),能够应用 useRef 和
useImperativeHandle
:
const { forwardRef, useRef, useImperativeHandle } = React;const Child = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ getAlert() { alert("getAlert from Child"); } })); return <h1>Hi</h1>;});const Parent = () => { const childRef = useRef(); return ( <div> <Child ref={childRef} /> <button onClick={() => childRef.current.getAlert()}>Click</button> </div> );};
- 如果是在类组件中调用子组件(
>= [email protected]
),能够应用 createRef:
const { Component } = React;class Parent extends Component { constructor(props) { super(props); this.child = React.createRef(); } onClick = () => { this.child.current.getAlert(); }; render() { return ( <div> <Child ref={this.child} /> <button onClick={this.onClick}>Click</button> </div> ); }}class Child extends Component { getAlert() { alert('getAlert from Child'); } render() { return <h1>Hello</h1>; }}
传入 setState 函数的第二个参数的作用是什么?
该函数会在 setState
函数调用实现并且组件开始重渲染的时候被调用,咱们能够用该函数来监听渲染是否实现:
this.setState( { username: 'tylermcginnis33' }, () => console.log('setState has finished and the component has re-rendered.'))
this.setState((prevState, props) => { return { streak: prevState.streak + props.count }})
参考:前端react面试题具体解答
类组件与函数组件有什么异同?
相同点: 组件是 React 可复用的最小代码片段,它们会返回要在页面中渲染的 React 元素。也正因为组件是 React 的最小编码单位,所以无论是函数组件还是类组件,在应用形式和最终出现成果上都是完全一致的。
咱们甚至能够将一个类组件改写成函数组件,或者把函数组件改写成一个类组件(尽管并不举荐这种重构行为)。从使用者的角度而言,很难从应用体验上辨别两者,而且在古代浏览器中,闭包和类的性能只在极其场景下才会有显著的差异。所以,根本可认为两者作为组件是完全一致的。
不同点:
- 它们在开发时的心智模型上却存在微小的差别。类组件是基于面向对象编程的,它主打的是继承、生命周期等外围概念;而函数组件内核是函数式编程,主打的是 immutable、没有副作用、援用通明等特点。
- 之前,在应用场景上,如果存在须要应用生命周期的组件,那么主推类组件;设计模式上,如果须要应用继承,那么主推类组件。但当初因为 React Hooks 的推出,生命周期概念的淡出,函数组件能够齐全取代类组件。其次继承并不是组件最佳的设计模式,官网更推崇“组合优于继承”的设计概念,所以类组件在这方面的劣势也在淡出。
- 性能优化上,类组件次要依附 shouldComponentUpdate 阻断渲染来晋升性能,而函数组件依附 React.memo 缓存渲染后果来晋升性能。
- 从上手水平而言,类组件更容易上手,从将来趋势上看,因为React Hooks 的推出,函数组件成了社区将来主推的计划。
- 类组件在将来工夫切片与并发模式中,因为生命周期带来的复杂度,并不易于优化。而函数组件自身轻量简略,且在 Hooks 的根底上提供了比原先更细粒度的逻辑组织与复用,更能适应 React 的将来倒退。
对于store的了解
Store 就是把它们分割到一起的对象。Store 有以下职责:
- 维持利用的 state;
- 提供 getState() 办法获取 state;
- 提供 dispatch(action) 办法更新 state;
- 通过 subscribe(listener)注册监听器;
- 通过 subscribe(listener)返回的函数登记监听器
应用状态要留神哪些事件?
要留神以下几点。
- 不要间接更新状态
- 状态更新可能是异步的
- 状态更新要合并。
- 数据从上向下流动
如何用 React构建( build)生产模式?
通常,应用 Webpack的 DefinePlugin办法将 NODE ENV设置为 production。这将剥离 propType验证和额定的正告。除此之外,还能够缩小代码,因为 React应用 Uglify的dead-code来打消开发代码和正文,这将大大减少包占用的空间。
react组件的划分业务组件技术组件?
- 依据组件的职责通常把组件分为UI组件和容器组件。
- UI 组件负责 UI 的出现,容器组件负责管理数据和逻辑。
- 两者通过
React-Redux
提供connect
办法分割起来
在生命周期中的哪一步你应该发动 AJAX 申请
咱们该当将AJAX 申请放到 componentDidMount
函数中执行,次要起因有下
React
下一代和谐算法Fiber
会通过开始或进行渲染的形式优化利用性能,其会影响到componentWillMount
的触发次数。对于componentWillMount
这个生命周期函数的调用次数会变得不确定,React
可能会屡次频繁调用componentWillMount
。如果咱们将AJAX
申请放到componentWillMount
函数中,那么不言而喻其会被触发屡次,天然也就不是好的抉择。- 如果咱们将
AJAX
申请搁置在生命周期的其余函数中,咱们并不能保障申请仅在组件挂载结束后才会要求响应。如果咱们的数据申请在组件挂载之前就实现,并且调用了setState
函数将数据增加到组件状态中,对于未挂载的组件则会报错。而在componentDidMount
函数中进行AJAX
申请则能无效防止这个问题
React 中的key是什么?为什么它们很重要?
key能够帮忙 React跟踪循环创立列表中的虚构DOM元素,理解哪些元素已更改、增加或删除。
每个绑定key的虚构DOM元素,在兄弟元素之间都是举世无双的。在 React的和解过程中,比拟新的虛拟DOM树与上一个虛拟DOM树之间的差别,并映射到页面中。key使 React解决列表中虛拟DOM时更加高效,因为 React能够应用虛拟DOM上的key属性,疾速理解元素是新的、须要删除的,还是批改过的。如果没有key,Rat就不晓得列表中虚构DOM元素与页面中的哪个元素绝对应。所以在创立列表的时候,不要疏忽key。
hooks 和 class 比拟的劣势?
一、更容易复用代码
二、清新的代码格调+代码量更少
毛病
状态不同步
不好用的useEffect,
为什么要应用 React. Children. map( props. children,( )=>)而不是props. children. map ( ( ) => )?
因为不能保障 props. children将是一个数组。
以上面的代码为例。
<Parent> <h1>有课前端网</h1></Parent>
在父组件外部,如果尝试应用 props.children. map映射子对象,则会抛出谬误,因为props. children是一个对象,而不是一个数组。
如果有多个子元素, React会使 props.children成为一个数组,如下所示。
<Parent> <h1>有课前端网</h1> <h2>前端技术学习平台</h2></Parent>;//不倡议应用如下形式,在这个案例中会抛出谬误。class Parent extends Component { render() { return <div> {this.props.children.map((obj) => obj)}</div>; }}
倡议应用如下形式,防止在上一个案例中抛出谬误。
class Parent extends Component { render() { return <div> {React.Children.map(this.props.children, (obj) => obj)}</div>; }}
useEffect 与 useLayoutEffect 的区别
(1)共同点
- 使用成果: useEffect 与 useLayoutEffect 两者都是用于解决副作用,这些副作用包含扭转 DOM、设置订阅、操作定时器等。在函数组件外部操作副作用是不被容许的,所以须要应用这两个函数去解决。
- 应用形式: useEffect 与 useLayoutEffect 两者底层的函数签名是完全一致的,都是调用的 mountEffectImpl办法,在应用上也没什么差别,根本能够间接替换。
(2)不同点
- 应用场景: useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景;而 useLayoutEffect 会在所有的 DOM 变更之后同步调用,次要用于解决 DOM 操作、调整款式、防止页面闪动等问题。也正因为是同步解决,所以须要防止在 useLayoutEffect 做计算量较大的耗时工作从而造成阻塞。
- 应用成果: useEffect是依照程序执行代码的,扭转屏幕像素之后执行(先渲染,后扭转DOM),当扭转屏幕内容时可能会产生闪动;useLayoutEffect是扭转屏幕像素之前就执行了(会推延页面显示的事件,先扭转DOM后渲染),不会产生闪动。useLayoutEffect总是比useEffect先执行。
在将来的趋势上,两个 API 是会长期共存的,临时没有删减合并的打算,须要开发者依据场景去自行抉择。React 团队的倡议十分实用,如果切实分不清,先用 useEffect,个别问题不大;如果页面有异样,再间接替换为 useLayoutEffect 即可。
**
React 与 Vue 的 diff 算法有何不同?
diff 算法是指生成更新补丁的形式,次要利用于虚构 DOM 树变动后,更新实在 DOM。所以 diff 算法肯定存在这样一个过程:触发更新 → 生成补丁 → 利用补丁。
React 的 diff 算法,触发更新的机会次要在 state 变动与 hooks 调用之后。此时触发虚构 DOM 树变更遍历,采纳了深度优先遍历算法。但传统的遍历形式,效率较低。为了优化效率,应用了分治的形式。将繁多节点比对转化为了 3 种类型节点的比对,别离是树、组件及元素,以此晋升效率。
- 树比对:因为网页视图中较少有跨层级节点挪动,两株虚构 DOM 树只对同一档次的节点进行比拟。
- 组件比对:如果组件是同一类型,则进行树比对,如果不是,则间接放入到补丁中。
- 元素比对:次要产生在同层级中,通过标记节点操作生成补丁,节点操作对应实在的 DOM 剪裁操作。
以上是经典的 React diff 算法内容。自 React 16 起,引入了 Fiber 架构。为了使整个更新过程可随时暂停复原,节点与树别离采纳了 FiberNode 与 FiberTree 进行重构。fiberNode 应用了双链表的构造,能够间接找到兄弟节点与子节点。整个更新过程由 current 与 workInProgress 两株树双缓冲实现。workInProgress 更新实现后,再通过批改 current 相干指针指向新节点。
Vue 的整体 diff 策略与 React 对齐,尽管不足工夫切片能力,但这并不意味着 Vue 的性能更差,因为在 Vue 3 初期引入过,前期因为收益不高移除掉了。除了高帧率动画,在 Vue 中其余的场景简直都能够应用防抖和节流去进步响应性能。
为什么有些react生命周期钩子被标记为UNSAFE
componentWillMount
componentWillMount生命周期产生在首次渲染前,个别应用的小伙伴大多在这里初始化数据或异步获取内部数据赋值。初始化数据,react官网倡议放在constructor外面。而异步获取内部数据,渲染并不会期待数据返回后再去渲染
class Example extends React.Component { state = { value: '' }; componentWillMount() { this.setState({ value: this.props.source.value }); this.props.source.subscribe(this.handleChange); } componentWillUnmount() { this.props.source.unsubscribe(this.handleChange ); } handleChange = source => { this.setState({ value: source.value }); }; }
- 试想一下,如果组件在第一次渲染的时候被中断,因为组件没有实现渲染,所以并不会执行componentWillUnmount生命周期(注:很多人常常认为componentWillMount和componentWillUnmount总是配对,但这并不是肯定的。只有调用componentDidMount后,React能力保障稍后调用componentWillUnmount进行清理)。因而handleSubscriptionChange还是会在数据返回胜利后被执行,这时候setState因为组件曾经被移除,就会导致内存透露。所以倡议把异步获取内部数据写在componentDidMount生命周期里,这样就能保障componentWillUnmount生命周期会在组件移除的时候被执行,防止内存透露的危险。
- 当初,小伙伴分明为什么了要用
UNSAFE_componentWillMount
替换componentWillMount
了吧
componentWillReceiveProps
componentWillReceiveProps生命周期是在props更新时触发。个别用于props参数更新时同步更新state参数。但如果在componentWillReceiveProps生命周期间接调用父组件的某些有调用setState的函数,会导致程序死循环
// 如下是子组件componentWillReceiveProps里调用父组件扭转state的函数示例class Parent extends React.Component{ constructor(){ super(); this.state={ list: [], selectedData: {} }; } changeSelectData = selectedData => { this.setState({ selectedData }); } render(){ return ( <Clild list={this.state.list} changeSelectData={this.changeSelectData}/> ); }}...class Child extends React.Component{ constructor(){ super(); this.state={ list: [] }; } componentWillReceiveProps(nextProps){ this.setState({ list: nextProps.list }) nextProps.changeSelectData(nextProps.list[0]); //默认抉择第一个 } ...}
- 如上代码,在Child组件的componentWillReceiveProps里间接调用Parent组件的changeSelectData去更新Parent组件state的selectedData值。会触发Parent组件从新渲染,而Parent组件从新渲染会触发Child组件的componentWillReceiveProps生命周期函数执行。如此就会陷入死循环。导致程序解体。
- 所以,React官网把componentWillReceiveProps替换为UNSAFE_componentWillReceiveProps,让小伙伴在应用这个生命周期的时候留神它会有缺点,要留神防止,比方下面例子,Child在componentWillReceiveProps调用changeSelectData时先判断list是否有更新再确定是否要调用,就能够防止死循环。
componentWillUpdate
componentWillUpdate生命周期在视图更新前触发。个别用于视图更新前保留一些数据不便视图更新实现后赋值。 案例三:如下是列表加载更新后回到以后滚动条地位的案例
class ScrollingList extends React.Component { listRef = null; previousScrollOffset = null; componentWillUpdate(nextProps, nextState) { if (this.props.list.length < nextProps.list.length) { this.previousScrollOffset = this.listRef.scrollHeight - this.listRef.scrollTop; } } componentDidUpdate(prevProps, prevState) { if (this.previousScrollOffset !== null) { this.listRef.scrollTop = this.listRef.scrollHeight - this.previousScrollOffset; this.previousScrollOffset = null; } } render() { return ( `<div>` {/* ...contents... */}`</div>` ); } setListRef = ref => { this.listRef = ref; };
- 因为componentWillUpdate和componentDidUpdate这两个生命周期函数有肯定的时间差(componentWillUpdate后通过渲染、计算、再更新DOM元素,最初才调用componentDidUpdate),如果这个时间段内用户刚好拉伸了浏览器高度,那componentWillUpdate计算的previousScrollOffset就不精确了。如果在componentWillUpdate进行setState操作,会呈现屡次调用只更新一次的问题,把setState放在componentDidUpdate,能保障每次更新只调用一次。
- 所以,react官网倡议把componentWillUpdate替换为UNSAFE_componentWillUpdate。如果真的有以上案例的需要,能够应用16.3新退出的一个周期函数getSnapshotBeforeUpdat
论断
- React意识到componentWillMount、componentWillReceiveProps和componentWillUpdate这三个生命周期函数有缺点,比拟容易导致解体。然而因为旧的我的项目曾经在用以及有些老开发者习惯用这些生命周期函数,于是通过给它加UNSAFE_来揭示用它的人要留神它们的缺点
- React退出了两个新的生命周期函数getSnapshotBeforeUpdate和getDerivedStateFromProps,目标为了即便不应用这三个生命周期函数,也能实现只有这三个生命周期能实现的性能
diff算法是怎么运作
每一种节点类型有本人的属性,也就是prop,每次进行diff的时候,react会先比拟该节点类型,如果节点类型不一样,那么react会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟prop是否有更新,如果有prop不一样,那么react会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点