关于前端:前端常考react面试题持续更新中

7次阅读

共计 13753 个字符,预计需要花费 35 分钟才能阅读完成。

react diff 算法

咱们晓得 React 会保护两个虚构 DOM,那么是如何来比拟,如何来判断,做出最优的解呢?这就用到了 diff 算法

diff 算法的作用

计算出 Virtual DOM 中真正变动的局部,并只针对该局部进行原生 DOM 操作,而非从新渲染整个页面。

传统 diff 算法

通过循环递归对节点进行顺次比照,算法复杂度达到 O(n^3),n 是树的节点数,这个有多可怕呢?——如果要展现 1000 个节点,得执行上亿次比拟。。即使是 CPU 快能执行 30 亿条命令,也很难在一秒内计算出差别。

React 的 diff 算法

  1. 什么是和谐?

将 Virtual DOM 树转换成 actual DOM 树的起码操作的过程 称为 和谐。

  1. 什么是 React diff 算法?

diff算法是和谐的具体实现。

diff 策略

React 用 三大策略 将 O(n^3) 杂度 转化为 O(n)复杂度

策略一(tree diff):

  • Web UI 中 DOM 节点跨层级的挪动操作特地少,能够忽略不计
  • 同级比拟, 既然 DOM 节点跨层级的挪动操作少到能够忽略不计,那么 React 通过 updateDepth 对 Virtual DOM 树进行层级管制,也就是同一层,在比照的过程中,如果发现节点不在了,会齐全删除不会对其余中央进行比拟,这样只须要对树遍历一次就 OK 了

策略二(component diff):

  • 领有雷同类的两个组件 生成类似的树形构造,
  • 领有不同类的两个组件 生成不同的树形构造。

策略三(element diff):

对于同一层级的一组子节点,通过惟一 id 辨别。

tree diff

  • React 通过 updateDepth 对 Virtual DOM 树进行层级管制。
  • 对树分层比拟,两棵树 只对同一档次节点 进行比拟。如果该节点不存在时,则该节点及其子节点会被齐全删除,不会再进一步比拟。
  • 只需遍历一次,就能实现整棵 DOM 树的比拟。

那么问题来了,如果 DOM 节点呈现了跨层级操作,diff 会咋办呢?

答:diff 只简略思考同层级的节点地位变换,如果是跨层级的话,只有创立节点和删除节点的操作。

如上图所示,以 A 为根节点的整棵树会被从新创立,而不是挪动,因而 官网倡议不要进行 DOM 节点跨层级操作,能够通过 CSS 暗藏、显示节点,而不是真正地移除、增加 DOM 节点

component diff

React 对不同的组件间的比拟,有三种策略

  1. 同一类型的两个组件,按原策略(层级比拟)持续比拟 Virtual DOM 树即可。
  2. 同一类型的两个组件,组件 A 变动为组件 B 时,可能 Virtual DOM 没有任何变动,如果晓得这点(变换的过程中,Virtual DOM 没有扭转),可节俭大量计算工夫,所以 用户 能够通过 shouldComponentUpdate() 来判断是否须要 判断计算。
  3. 不同类型的组件,将一个(将被扭转的)组件判断为dirty component(脏组件),从而替换 整个组件的所有节点。

留神:如果组件 D 和组件 G 的构造类似,然而 React 判断是 不同类型的组件,则不会比拟其构造,而是删除 组件 D 及其子节点,创立组件 G 及其子节点。

element diff

当节点处于同一层级时,diff 提供三种节点操作:删除、插入、挪动。

  • 插入:组件 C 不在汇合(A,B)中,须要插入
  • 删除:

    • 组件 D 在汇合(A,B,D)中,但 D 的节点曾经更改,不能复用和更新,所以须要删除 旧的 D,再创立新的。
    • 组件 D 之前在 汇合(A,B,D)中,但汇合变成新的汇合(A,B)了,D 就须要被删除。
  • 挪动:组件 D 曾经在汇合(A,B,C,D)里了,且汇合更新时,D 没有产生更新,只是地位扭转,如新汇合(A,D,B,C),D 在第二个,毋庸像传统 diff,让旧汇合的第二个 B 和新汇合的第二个 D 比拟,并且删除第二个地位的 B,再在第二个地位插入 D,而是(对同一层级的同组子节点)增加惟一 key 进行辨别,挪动即可。

diff 的有余与待优化的中央

尽量减少相似将最初一个节点挪动到列表首部的操作,当节点数量过大或更新操作过于频繁时,会影响 React 的渲染性能

react-router4 的外围

  • 路由变成了组件
  • 扩散到各个页面,不须要配置 比方<link> <route></route>

什么是 React Fiber?

Fiber 是 React 16 中新的协调引擎或从新实现外围算法。它的次要指标是反对虚构 DOM 的增量渲染。React Fiber 的指标是进步其在动画、布局、手势、暂停、停止或重用等方面的适用性,并为不同类型的更新调配优先级,以及新的并发原语。
React Fiber 的指标是加强其在动画、布局和手势等畛域的适用性。它的次要个性是增量渲染: 可能将渲染工作宰割成块,并将其扩散到多个帧中。

如何 React.createElement?

const element = <h1 className="greeting">Hello, world!</h1>;

上述代码如何应用 React.createElement 来实现:

const element = React.createElement("h1", { className: "greeting"}, "Hello, world!");

如何创立 refs

Refs 是应用 React.createRef() 创立的,并通过 ref 属性附加到 React 元素。在结构组件时,通常将 Refs 调配给实例属性,以便能够在整个组件中援用它们。

class MyComponent extends React.Component {constructor(props) {super(props);
    this.myRef = React.createRef();}
  render() {return <div ref={this.myRef} />;
  }
}

或者这样用:

class UserForm extends Component {handleSubmit = () => {console.log("Input Value is:", this.input.value);
  };
  render() {
    return (<form onSubmit={this.handleSubmit}>
        <input type="text" ref={(input) => (this.input = input)} /> // Access DOM input in handle submit
        <button type="submit">Submit</button>
      </form>
    );
  }
}

这三个点 (…) 在 React 干嘛用的?

... 在 React(应用 JSX)代码中做什么?它叫什么?

<Modal {...this.props} title='Modal heading' animation={false}/>

这个叫扩大操作符号或者开展操作符,例如,如果 this.props 蕴含 a:1b:2,则

<Modal {...this.props} title='Modal heading' animation={false}>

等价于上面内容:

<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>

扩大符号不仅实用于该用例,而且对于创立具备现有对象的大多数(或全副)属性的新对象十分不便,在更新state 咱们就常常这么做:

this.setState((prevState) => {return { foo: { ...prevState.foo, a: "updated"} };
});

参考 前端进阶面试题具体解答

如何配置 React-Router 实现路由切换

(1)应用<Route> 组件

路由匹配是通过比拟 <Route> 的 path 属性和以后地址的 pathname 来实现的。当一个 <Route> 匹配胜利时,它将渲染其内容,当它不匹配时就会渲染 null。没有门路的 <Route> 将始终被匹配。

// when location = {pathname: '/about'}
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>

(2)联合应用 <Switch> 组件和 <Route> 组件

<Switch> 用于将 <Route> 分组。

<Switch>
    <Route exact path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="/contact" component={Contact} />
</Switch>

<Switch> 不是分组 <Route> 所必须的,但他通常很有用。一个 <Switch> 会遍历其所有的子 <Route>元素,并仅渲染与以后地址匹配的第一个元素。

(3)应用 <Link>、<NavLink>、<Redirect> 组件

<Link> 组件来在你的应用程序中创立链接。无论你在何处渲染一个<Link>,都会在应用程序的 HTML 中渲染锚(<a>)。

<Link to="/">Home</Link>   
// <a href='/'>Home</a>

是一种非凡类型的 当它的 to 属性与以后地址匹配时,能够将其定义为 ” 沉闷的 ”。

// location = {pathname: '/react'}
<NavLink to="/react" activeClassName="hurray">
    React
</NavLink>
// <a href='/react' className='hurray'>React</a>

当咱们想强制导航时,能够渲染一个 <Redirect>,当一个<Redirect> 渲染时,它将应用它的 to 属性进行定向。

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 最新版本解决了什么问题,减少了哪些货色

React 16.x 的三大新个性 Time Slicing、Suspense、hooks

  • Time Slicing(解决 CPU 速度问题)使得在执行工作的期间能够随时暂停,跑去干别的事件,这个个性使得 react 能在性能极其差的机器跑时,依然放弃有良好的性能
  • Suspense(解决网络 IO 问题) 和 lazy 配合,实现异步加载组件。能暂停以后组件的渲染,当实现某件事当前再持续渲染,解决从 react 出世到当初都存在的「异步副作用」的问题,而且解决得非的优雅,应用的是 T 异步然而同步的写法,这是最好的解决异步问题的形式
  • 提供了一个 内置函数 componentDidCatch,当有谬误产生时,能够敌对地展现 fallback 组件; 能够捕捉到它的子元素(包含嵌套子元素)抛出的异样; 能够复用谬误组件。

(1)React16.8 退出 hooks,让 React 函数式组件更加灵便,hooks 之前,React 存在很多问题:

  • 在组件间复用状态逻辑很难
  • 简单组件变得难以了解,高阶组件和函数组件的嵌套过深。
  • class 组件的 this 指向问题
  • 难以记忆的生命周期

hooks 很好的解决了上述问题,hooks 提供了很多办法

  • useState 返回有状态值,以及更新这个状态值的函数
  • useEffect 承受蕴含命令式,可能有副作用代码的函数。
  • useContext 承受上下文对象(从 React.createContext 返回的值)并返回以后上下文值,
  • useReducer useState 的代替计划。承受类型为(state,action)=> newState 的 reducer,并返回与 dispatch 办法配对的以后状态。
  • useCalLback 返回一个回顾的 memoized 版本,该版本仅在其中一个输出产生更改时才会更改。纯函数的输入输出确定性 o useMemo 纯的一个记忆函数 o useRef 返回一个可变的 ref 对象,其 Current 属性被初始化为传递的参数,返回的 ref 对象在组件的整个生命周期内放弃不变。
  • useImperativeMethods 自定义应用 ref 时公开给父组件的实例值
  • useMutationEffect 更新兄弟组件之前,它在 React 执行其 DOM 扭转的同一阶段同步触发
  • useLayoutEffect DOM 扭转后同步触发。应用它来从 DOM 读取布局并同步从新渲染

(2)React16.9

  • 重命名 Unsafe 的生命周期办法。新的 UNSAFE_前缀将有助于在代码 review 和 debug 期间,使这些有问题的字样更突出
  • 废除 javascrip: 模式的 URL。以 javascript: 结尾的 URL 非常容易蒙受攻打,造成安全漏洞。
  • 废除 ”Factory” 组件。工厂组件会导致 React 变大且变慢。
  • act()也反对异步函数,并且你能够在调用它时应用 await。
  • 应用 <React.ProfiLer> 进行性能评估。在较大的利用中追踪性能回归可能会很不便

(3)React16.13.0

  • 反对在渲染期间调用 setState,但仅实用于同一组件
  • 可检测抵触的款式规定并记录正告
  • 废除 unstable_createPortal,应用 CreatePortal
  • 将组件堆栈增加到其开发正告中,使开发人员可能隔离 bug 并调试其程序,这能够分明地阐明问题所在,并更快地定位和修复谬误。

React.forwardRef 有什么用

forwardRef

  • 应用 forwardRefforward 在这里是「传递」的意思)后,就能跨组件传递ref
  • 在例子中,咱们将 inputRefForm跨组件传递到 MyInput 中,并与 input 产生关联
const MyInput = forwardRef((props, ref) => {return <input {...props} ref={ref} />;
});

function Form() {const inputRef = useRef(null);

  function handleClick() {inputRef.current.focus();
  }

  return (
    <>
      <MyInput ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}

useImperativeHandle

除了「限度跨组件传递 ref」外,还有一种「避免ref 失控的措施」,那就是 useImperativeHandle,他的逻辑是这样的:既然「ref 失控」 是因为「应用了不该被应用的 DOM 办法」(比方 appendChild),那我能够限度「ref 中只存在能够被应用的办法」。用 useImperativeHandle 批改咱们的 MyInput 组件:

const MyInput = forwardRef((props, ref) => {const realInputRef = useRef(null);
  useImperativeHandle(ref, () => ({focus() {realInputRef.current.focus();
    },
  }));
  return <input {...props} ref={realInputRef} />;
});

当初,Form组件中通过 inputRef.current 只能取到如下数据结构:

{focus() {realInputRef.current.focus();
  },
}

就杜绝了 「开发者通过 ref 取到 DOM 后,执行不该被应用的 API,呈现 ref 失控」 的状况

  • 为了避免错用 / 滥用导致 ref 失控,React 限度「默认状况下,不能跨组件传递 ref」
  • 为了破除这种限度,能够应用forwardRef
  • 为了缩小 refDOM的滥用,能够应用 useImperativeHandle 限度 ref 传递的数据结构。

React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代

这三者是目前 react 解决代码复用的次要形式:

  • 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
  • render props 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
  • 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。

(1)HOC 官网解释∶

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。

简言之,HOC 是一种组件的设计模式,HOC 承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。

// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {constructor(props) {super(props);
      this.state = {data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑解决
    render() {
      // ... 并应用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };

// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id));

HOC 的优缺点∶

  • 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
  • 毛病∶ hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩

(2)Render props 官网解释∶

“render prop” 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术

具备 render prop 的组件承受一个返回 React 元素的函数,将 render 的渲染逻辑注入到组件外部。在这里,”render” 的命名能够是任何其余无效的标识符。

// DataProvider 组件外部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {name: 'Tom'}

    render() {
    return (
        <div>
          <p> 共享数据组件本人外部的渲染逻辑 </p>
          {this.props.render(this.state) }      </div>
    );
  }
}

// 调用形式
<DataProvider render={data => (<h1>Hello {data.name}</h1>
)}/>

由此能够看到,render props 的优缺点也很显著∶

  • 长处:数据共享、代码复用,将组件内的 state 作为 props 传递给调用者,将渲染逻辑交给调用者。
  • 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅

(3)Hooks 官网解释∶

Hook 是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义 hook,能够复用代码逻辑。

// 自定义一个获取订阅数据的 hook
function useSubscription() {const data = DataSource.getComments();
  return [data];
}
// 
function CommentList(props) {const {data} = props;
  const [subData] = useSubscription();
    ...
}
// 应用
<CommentList data='hello' />

以上能够看出,hook 解决了 hoc 的 prop 笼罩的问题,同时应用的形式解决了 render props 的嵌套天堂的问题。hook 的长处如下∶

  • 应用直观;
  • 解决 hoc 的 prop 重名问题;
  • 解决 render props 因共享数据 而呈现嵌套天堂的问题;
  • 能在 return 之外应用数据的问题。

须要留神的是:hook 只能在组件顶层应用,不可在分支语句中应用。、

为什么 React 元素有一个 $$typeof 属性

目标是为了避免 XSS 攻打。因为 Synbol 无奈被序列化,所以 React 能够通过有没有 $$typeof 属性来断出以后的 element 对象是从数据库来的还是本人生成的。

  • 如果没有 $$typeof 这个属性,react 会回绝解决该元素。
  • 在 React 的古老版本中,上面的写法会呈现 XSS 攻打:
// 服务端容许用户存储 JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {__html: '/* 把你想的搁着 */'},
  },
  // ...
};
let message = {text: expectedTextButGotJSON};

// React 0.13 中有危险
<p>
  {message.text}
</p>

react router

import React from 'react'
import {render} from 'react-dom'
import {browserHistory, Router, Route, IndexRoute} from 'react-router'

import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'

render(<Router history={browserHistory}>  // history 路由
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='about' component={About} />
      <Route path='features' component={Features} />
    </Route>
  </Router>,
  document.getElementById('app')
)
render(<Router history={browserHistory} routes={routes} />,
  document.getElementById('app')
)

React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件能够拦挡正在产生的跳转,或在来到 route 前提醒用户。routerWillLeave 返回值有以下两种:

return false 勾销此次跳转
return 返回提示信息,在来到 route 前提醒用户进行确认。

高阶组件

高阶函数:如果一个函数 承受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数

高阶组件:如果一个函数 承受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件

react 中的高阶组件

React 中的高阶组件次要有两种模式:属性代理 反向继承

属性代理 Proxy

  • 操作 props
  • 抽离 state
  • 通过 ref 拜访到组件实例
  • 用其余元素包裹传入的组件 WrappedComponent

反向继承

会发现其属性代理和反向继承的实现有些相似的中央,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent

反向继承能够用来做什么:

1. 操作 state

高阶组件中能够读取、编辑和删除 WrappedComponent 组件实例中的 state。甚至能够减少更多的state 项,然而 十分不倡议这么做 因为这可能会导致 state 难以保护及治理。

function withLogging(WrappedComponent) {    
    return class extends WrappedComponent {render() {    
            return (    
                <div>;    
                    <h2>;Debugger Component Logging...<h2>;    
                    <p>;state:<p>;    
                    <pre>;{JSON.stringify(this.state, null, 4)}<pre>;    
                    <p>props:<p>;    
                    <pre>{JSON.stringify(this.props, null, 4)}<pre>;    
                    {super.render()}    
                <div>;    
            );    
        }    
    };    
}

2. 渲染劫持(Render Highjacking)

条件渲染通过 props.isLoading 这个条件来判断渲染哪个组件。

批改由 render() 输入的 React 元素树

为什么应用 jsx 的组件中没有看到应用 react 却须要引入 react?

实质上来说 JSX 是 React.createElement(component, props, ...children) 办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel 会把组件转换为 CreateElement 模式。在 React 17 之后,就不再须要引入,因为 babel 曾经能够帮咱们主动引入 react。

useEffect(fn, []) 和 componentDidMount 有什么差别

useEffect 会捕捉 props 和 state。所以即使在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,能够应用 ref。

react 中这两个生命周期会触发死循环

componentWillUpdate生命周期在 shouldComponentUpdate 返回 true 后被触发。在这两个生命周期只有视图更新就会触发,因而不能再这两个生命周期中应用 setState。否则会导致死循环

在 React 中如何防止不必要的 render?

React 基于虚构 DOM 和高效 Diff 算法的完满配合,实现了对 DOM 最小粒度的更新。大多数状况下,React 对 DOM 的渲染效率足以业务日常。但在个别简单业务场景下,性能问题仍然会困扰咱们。此时须要采取一些措施来晋升运行性能,其很重要的一个方向,就是防止不必要的渲染(Render)。这里提下优化的点:

  • shouldComponentUpdate 和 PureComponent

在 React 类组件中,能够利用 shouldComponentUpdate 或者 PureComponent 来缩小因父组件更新而触发子组件的 render,从而达到目标。shouldComponentUpdate 来决定是否组件是否从新渲染,如果不心愿组件从新渲染,返回 false 即可。

  • 利用高阶组件

在函数组件中,并没有 shouldComponentUpdate 这个生命周期,能够利用高阶组件,封装一个相似 PureComponet 的性能

  • 应用 React.memo

React.memo 是 React 16.6 新的一个 API,用来缓存组件的渲染,防止不必要的更新,其实也是一个高阶组件,与 PureComponent 非常相似,但不同的是,React.memo 只能用于函数组件。

对 React-Intl 的了解,它的工作原理?

React-intl 是雅虎的语言国际化开源我的项目 FormatJS 的一部分,通过其提供的组件和 API 能够与 ReactJS 绑定。

React-intl 提供了两种应用办法,一种是援用 React 组件,另一种是间接调取 API,官网更加举荐在 React 我的项目中应用前者,只有在无奈应用 React 组件的中央,才应该调用框架提供的 API。它提供了一系列的 React 组件,包含数字格式化、字符串格式化、日期格式化等。

在 React-intl 中,能够配置不同的语言包,他的工作原理就是依据须要,在语言包之间进行切换。

新版生命周期

在新版本中,React 官网对生命周期有了新的 变动倡议:

  • 应用 getDerivedStateFromProps 替换componentWillMount;
  • 应用 getSnapshotBeforeUpdate 替换componentWillUpdate;
  • 防止应用componentWillReceiveProps

其实该变动的起因,正是因为上述提到的 Fiber。首先,从下面咱们晓得 React 能够分成 reconciliationcommit两个阶段,对应的生命周期如下:

reconciliation

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

commit

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Fiber 中,reconciliation 阶段进行了工作宰割,波及到 暂停 和 重启,因而可能会导致 reconciliation 中的生命周期函数在一次更新渲染循环中被 屡次调用 的状况,产生一些意外谬误

新版的倡议生命周期如下:

class Component extends React.Component {
  // 替换 `componentWillReceiveProps`,// 初始化和 update 时被调用
  // 动态函数,无奈应用 this
  static getDerivedStateFromProps(nextProps, prevState) {}

  // 判断是否须要更新组件
  // 能够用于组件性能优化
  shouldComponentUpdate(nextProps, nextState) {}

  // 组件被挂载后触发
  componentDidMount() {}

  // 替换 componentWillUpdate
  // 能够在更新之前获取最新 dom 数据
  getSnapshotBeforeUpdate() {}

  // 组件更新后调用
  componentDidUpdate() {}

  // 组件行将销毁
  componentWillUnmount() {}

  // 组件已销毁
  componentDidUnMount() {}
}

应用倡议:

  • constructor 初始化 state
  • componentDidMount 中进行事件监听,并在 componentWillUnmount 中解绑事件;
  • componentDidMount 中进行数据的申请,而不是在componentWillMount
  • 须要依据 props 更新 state 时,应用getDerivedStateFromProps(nextProps, prevState)

    • 旧 props 须要本人存储,以便比拟;
public static getDerivedStateFromProps(nextProps, prevState) {
    // 当新 props 中的 data 发生变化时,同步更新到 state 上
    if (nextProps.data !== prevState.data) {
        return {data: nextProps.data}
    } else {return null1}
}

能够在 componentDidUpdate 监听 props 或者 state 的变动,例如:

componentDidUpdate(prevProps) {
    // 当 id 发生变化时,从新获取数据
    if (this.props.id !== prevProps.id) {this.fetchData(this.props.id);
    }
}
  • 在 componentDidUpdate 应用 setState 时,必须加条件,否则将进入死循环;
  • getSnapshotBeforeUpdate(prevProps, prevState)能够在更新之前获取最新的渲染数据,它的调用是在 render 之后,update 之前;
  • shouldComponentUpdate: 默认每次调用 setState,肯定会最终走到 diff 阶段,但能够通过 shouldComponentUpdate 的生命钩子返回 false 来间接阻止前面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。
正文完
 0