shouldComponentUpdate有什么用?为什么它很重要?

组件状态数据或者属性数据产生更新的时候,组件会进入存在期,视图会渲染更新。在生命周期办法 should ComponentUpdate中,容许抉择退出某些组件(和它们的子组件)的和解过程。
和解的最终目标是依据新的状态,以最无效的形式更新用户界面。如果咱们晓得用户界面的某一部分不会扭转,那么没有理由让 React弄清楚它是否应该更新渲染。通过在 shouldComponentUpdate办法中返回 false, React将让以后组件及其所有子组件放弃与以后组件状态雷同。

React 组件生命周期有哪些不同阶段?

在组件生命周期中有四个不同的阶段:

  1. Initialization:在这个阶段,组件筹备设置初始化状态和默认属性。
  2. Mounting:react 组件曾经筹备好挂载到浏览器 DOM 中。这个阶段包含componentWillMountcomponentDidMount生命周期办法。
  3. Updating:在这个阶段,组件以两种形式更新,发送新的 props 和 state 状态。此阶段包含shouldComponentUpdatecomponentWillUpdatecomponentDidUpdate生命周期办法。
  4. Unmounting:在这个阶段,组件曾经不再被须要了,它从浏览器 DOM 中卸载下来。这个阶段蕴含 componentWillUnmount 生命周期办法。
    除以上四个罕用生命周期外,还有一个错误处理的阶段:
    Error Handling:在这个阶段,不管在渲染的过程中,还是在生命周期办法中或是在任何子组件的构造函数中产生谬误,该组件都会被调用。这个阶段蕴含了 componentDidCatch 生命周期办法。

React的虚构DOM和Diff算法的外部实现

传统 diff 算法的工夫复杂度是 O(n^3),这在前端 render 中是不可承受的。为了升高工夫复杂度,react 的 diff 算法做了一些斗争,放弃了最优解,最终将工夫复杂度升高到了 O(n)。

那么 react diff 算法做了哪些斗争呢?,参考如下:

  1. tree diff:只比照同一层的 dom 节点,疏忽 dom 节点的跨层级挪动

如下图,react 只会对雷同色彩方框内的 DOM 节点进行比拟,即同一个父节点下的所有子节点。当发现节点不存在时,则该节点及其子节点会被齐全删除掉,不会用于进一步的比拟。

这样只须要对树进行一次遍历,便能实现整个 DOM 树的比拟。

这就意味着,如果 dom 节点产生了跨层级挪动,react 会删除旧的节点,生成新的节点,而不会复用。

  1. component diff:如果不是同一类型的组件,会删除旧的组件,创立新的组件

  1. element diff:对于同一层级的一组子节点,须要通过惟一 id 进行来辨别
  2. 如果没有 id 来进行辨别,一旦有插入动作,会导致插入地位之后的列表全副从新渲染
  3. 这也是为什么渲染列表时为什么要应用惟一的 key。

生命周期调用办法的程序是什么?

React生命周期分为三大周期,11个阶段,生命周期办法调用程序别离如下。
(1)在创立期的五大阶段,调用办法的程序如下。

  • getDetaultProps:定义默认属性数据。
  • getInitialState:初始化默认状态数据。
  • component WillMount:组件行将被构建。
  • render:渲染组件。
  • componentDidMount:组件构建实现

(2)在存在期的五大阶段,调用办法的程序如下。

  • componentWillReceiveProps:组件行将接管新的属性数据。
  • shouldComponentUpdate:判断组件是否应该更新。
  • componnent WillUpdate:组件行将更新。
  • render:渲染组件。
  • componentDidUpdate:组件更新实现。

(3)在销毁期的一个阶段,调用办法 componentWillUnmount,示意组件行将被销毀。

在哪个生命周期中你会收回Ajax申请?为什么?

Ajax申请应该写在组件创立期的第五个阶段,即 componentDidMount生命周期办法中。起因如下。
在创立期的其余阶段,组件尚未渲染实现。而在存在期的5个阶段,又不能确保生命周期办法肯定会执行(如通过 shouldComponentUpdate办法优化更新等)。在销毀期,组件行将被销毁,申请数据变得无意义。因而在这些阶段发岀Ajax申请显然不是最好的抉择。
在组件尚未挂载之前,Ajax申请将无奈执行结束,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。
在 componentDidMount办法中,执行Ajax即可保障组件曾经挂载,并且可能失常更新组件。

为什么要应用 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>;  }}

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

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的渲染性能

在 ReactNative中,如何解决 adb devices找不到连贯设施的问题?

在应用 Genymotion时,首先须要在SDK的 platform-tools中退出环境变量,而后在 Genymotion中单击 Setting,抉择ADB选项卡,单击 Use custom Android SDK tools,浏览本地SDK的地位,单击OK按钮就能够了。启动虛拟机后,在cmd中输出 adb devices能够查看设施。

调用 setState 之后产生了什么

在代码中调用 setState 函数之后,React 会将传入的参数与之前的状态进行合并,而后触发所谓的和谐过程(Reconciliation)。通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面。在 React 失去元素树之后,React 会计算出新的树和老的树之间的差别,而后依据差别对界面进行最小化从新渲染。通过 diff 算法,React 可能准确制导哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。
  • 在 setState 的时候,React 会为以后节点创立一个 updateQueue 的更新列队。
  • 而后会触发 reconciliation 过程,在这个过程中,会应用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是能够做到异步可中断的执行。
  • 而后 React Scheduler 会依据优先级高下,先执行优先级高的节点,具体是执行 doWork 办法。
  • 在 doWork 办法中,React 会执行一遍 updateQueue 中的办法,以取得新的节点。而后比照新旧节点,为老节点打上 更新、插入、替换 等 Tag。
  • 以后节点 doWork 实现后,会执行 performUnitOfWork 办法取得新节点,而后再反复下面的过程。
  • 当所有节点都 doWork 实现后,会触发 commitRoot 办法,React 进入 commit 阶段。
  • 在 commit 阶段中,React 会依据后面为各个节点打的 Tag,一次性更新整个 dom 元素

ref是一个函数又有什么益处?

  • 不便react销毁组件、从新渲染的时候去清空refs的货色,避免内存泄露

为什么虚构dom会进步性能

虚构dom相当于在js和实在dom两头加了一个缓存,利用dom diff算法防止了没有必要的dom操作,从而进步性能

具体实现步骤如下

  • JavaScript 对象构造示意 DOM 树的构造;而后用这个树构建一个真正的 DOM 树,插到文档当中
  • 当状态变更的时候,从新结构一棵新的对象树。而后用新的树和旧的树进行比拟,记录两棵树差别
  • 把2所记录的差别利用到步骤1所构建的真正的DOM树上,视图就更新

虚构DOM肯定会进步性能吗?

很多人认为虚构DOM肯定会进步性能,肯定会更快,其实这个说法有点全面,因为虚构DOM尽管会缩小DOM操作,但也无奈防止DOM操作
  • 它的劣势是在于diff算法和批量解决策略,将所有的DOM操作收集起来,一次性去扭转实在的DOM,但在首次渲染上,虚构DOM会多了一层计算,耗费一些性能,所以有可能会比html渲染的要慢
  • 留神,虚构DOM实际上是给咱们找了一条最短,最近的门路,并不是说比DOM操作的更快,而是门路最简略

这段代码有什么问题?

class App extends Component {  constructor(props) {    super(props);    this.state = {      username: "有课前端网",      msg: " ",    };  }  render() {    return <div> {this.state.msg}</div>;  }  componentDidMount() {    this.setState((oldState, props) => {      return {        msg: oldState.username + " - " + props.intro,      };    });  }}

render ( < App intro=" 前端技术业余学习平台"></App>,ickt )
在页面中失常输入“有课前端网-前端技术业余学习平台”。然而这种写法很少应用,并不是罕用的写法。React容许对 setState办法传递一个函数,它接管到先前的状态和属性数据并返回一个须要批改的状态对象,正如咱们在下面所做的那样。它岂但没有问题,而且如果依据以前的状态( state)以及属性来批改以后状态,举荐应用这种写法。

传入 setstate函数的第二个参数的作用是什么?

第二个参数是一个函数,该函数会在 setState函数调用实现并且组件开始重渲染时调用,能够用该函数来监听渲染是否实现。

this.setstate(  {    username: "有课前端网",  },  () => console.log("re-rendered success. "));

react-router里的<Link>标签和<a>标签有什么区别

比照<a>,Link组件防止了不必要的重渲染

React怎么做数据的检查和变动

Model扭转之后(可能是调用了setState),触发了virtual dom的更新,再用diff算法来把virtual DOM比拟real DOM,看看是哪个dom节点更新了,再渲染real dom

高阶组件存在的问题

  • 静态方法失落(必须将静态方法做拷贝)
  • refs 属性不能透传(如果你向一个由高阶组件创立的组件的元素增加ref援用,那么ref指向的是最外层容器组件实例的,而不是被包裹的WrappedComponent组件。)
  • 反向继承不能保障残缺的子组件树被解析
    React 组件有两种模式,别离是 class 类型和 function 类型(无状态组件)。

咱们晓得反向继承的渲染劫持能够管制 WrappedComponent 的渲染过程,也就是说这个过程中咱们能够对 elements treestatepropsrender() 的后果做各种操作。

然而如果渲染 elements tree 中蕴含了 function 类型的组件的话,这时候就不能操作组件的子组件了。

React中什么是受控组件和非控组件?

(1)受控组件 在应用表单来收集用户输出时,例如<input><select><textearea>等元素都要绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,更新组件的state。这种组件在React中被称为受控组件,在受控组件中,组件渲染出的状态与它的value或checked属性绝对应,react通过这种形式打消了组件的部分状态,使整个状态可控。react官网举荐应用受控表单组件。

受控组件更新state的流程:

  • 能够通过初始state中设置表单的默认值
  • 每当表单的值发生变化时,调用onChange事件处理器
  • 事件处理器通过事件对象e拿到扭转后的状态,并更新组件的state
  • 一旦通过setState办法更新state,就会触发视图的从新渲染,实现表单组件的更新

受控组件缺点: 表单元素的值都是由React组件进行治理,当有多个输入框,或者多个这种组件时,如果想同时获取到全副的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种状况,呈现了非受控组件。

(2)非受控组件 如果一个表单组件没有value props(单选和复选按钮对应的是checked props)时,就能够称为非受控组件。在非受控组件中,能够应用一个ref来从DOM取得表单值。而不是为每个状态更新编写一个事件处理程序。

React官网的解释:

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你能够应用 ref来从 DOM 节点中获取表单数据。
因为非受控组件将实在数据贮存在 DOM 节点中,所以在应用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码好看性,并且心愿疾速编写代码,应用非受控组件往往能够缩小你的代码量。否则,你应该应用受控组件。

例如,上面的代码在非受控组件中接管单个属性:

class NameForm extends React.Component {  constructor(props) {    super(props);    this.handleSubmit = this.handleSubmit.bind(this);  }  handleSubmit(event) {    alert('A name was submitted: ' + this.input.value);    event.preventDefault();  }  render() {    return (      <form onSubmit={this.handleSubmit}>        <label>          Name:          <input type="text" ref={(input) => this.input = input} />        </label>        <input type="submit" value="Submit" />      </form>    );  }}

总结: 页面中所有输出类的DOM如果是现用现取的称为非受控组件,而通过setState将输出的值保护到了state中,须要时再从state中取出,这里的数据就受到了state的管制,称为受控组件。

父子组件的通信形式?

父组件向子组件通信:父组件通过 props 向子组件传递须要的信息。

// 子组件: Childconst Child = props =>{  return <p>{props.name}</p>}// 父组件 Parentconst Parent = ()=>{    return <Child name="react"></Child>}

子组件向父组件通信:: props+回调的形式。

// 子组件: Childconst Child = props =>{  const cb = msg =>{      return ()=>{          props.callback(msg)      }  }  return (      <button onClick={cb("你好!")}>你好</button>  )}// 父组件 Parentclass Parent extends Component {    callback(msg){        console.log(msg)    }    render(){        return <Child callback={this.callback.bind(this)}></Child>        }}

React- Router有几种模式?

有以下几种模式。
HashRouter,通过散列实现,路由要带#。
BrowerRouter,利用HTML5中 history API实现,须要服务器端反对,兼容性不是很好。

什么是高阶组件(HOC)

  • 高阶组件(Higher Order Componennt)自身其实不是组件,而是一个函数,这个函数接管一个元组件作为参数,而后返回一个新的加强组件,高阶组件的呈现自身也是为了逻辑复用,举个例子
function withLoginAuth(WrappedComponent) {  return class extends React.Component {      constructor(props) {          super(props);          this.state = {            isLogin: false          };      }      async componentDidMount() {          const isLogin = await getLoginStatus();          this.setState({ isLogin });      }      render() {        if (this.state.isLogin) {            return <WrappedComponent {...this.props} />;        }        return (<div>您还未登录...</div>);      }  }}