关于前端:校招前端高频react面试题合集

3次阅读

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

理解 redux 吗?

  • redux 是一个利用数据流框架,次要解决了组件之间状态共享问题,原理是集中式治理,次要有三个外围办法:action store reduce
  • 工作流程
    view 调用 store 的 dispatch 承受 action 传入的 store,reduce 进行 state 操作

    view 通过 store 提供的 getState 获取最新的数据

  • redux 的长处:

    新增的 state 对状态的治理更加明确

    流程更加标准,缩小手动编写代码,进步编码效率

  • redux 的毛病:

    当数据更新是有时候组件不须要,也要从新绘制,影响效率

diff 算法如何比拟?

  • 只对同级比拟,跨层级的 dom 不会进行复用
  • 不同类型节点生成的 dom 树不同,此时会间接销毁老节点及子孙节点,并新建节点
  • 能够通过 key 来对元素 diff 的过程提供复用的线索
  • 单节点 diff
  • 单点 diff 有如下几种状况:
  • key 和 type 雷同示意能够复用节点
  • key 不同间接标记删除节点,而后新建节点
  • key 雷同 type 不同,标记删除该节点和兄弟节点,而后新创建节点

React 设计思路,它的理念是什么?

(1)编写简略直观的代码

React 最大的价值不是高性能的虚构 DOM、封装的事件机制、服务器端渲染,而是申明式的直观的编码方式。react 文档第一条就是申明式,React 使创立交互式 UI 变得轻而易举。为利用的每一个状态设计简洁的视图,当数据扭转时 React 能无效地更新并正确地渲染组件。以申明式编写 UI,能够让代码更加牢靠,且不便调试。

(2)简化可复用的组件

React 框架外面应用了简化的组件模型,但更彻底地应用了组件化的概念。React 将整个 UI 上的每一个功能模块定义成组件,而后将小的组件通过组合或者嵌套的形式形成更大的组件。React 的组件具备如下的个性∶

  • 可组合:简略组件能够组合为简单的组件
  • 可重用:每个组件都是独立的,能够被多个组件应用
  • 可保护:和组件相干的逻辑和 UI 都封装在了组件的外部,不便保护
  • 可测试:因为组件的独立性,测试组件就变得不便很多。

(3) Virtual DOM

实在页面对应一个 DOM 树。在传统页面的开发模式中,每次须要更新页面时,都要手动操作 DOM 来进行更新。DOM 操作十分低廉。在前端开发中,性能耗费最大的就是 DOM 操作,而且这部分代码会让整体我的项目的代码变得难 以保护。React 把实在 DOM 树转换成 JavaScript 对象树,也就是 Virtual DOM,每次数据更新后,从新计算 Virtual DOM,并和上一次生成的 Virtual DOM 做比照,对发生变化的局部做批量更新。React 也提供了直观的 shouldComponentUpdate 生命周期回调,来缩小数据变动后不必要的 Virtual DOM 比照过程,以保障性能。

(4)函数式编程

React 把过来一直反复构建 UI 的过程形象成了组件,且在给定参数的状况下约定渲染对应的 UI 界面。React 能充分利用很多函数式办法去缩小冗余代码。此外,因为它自身就是简略函数,所以易于测试。

(5)一次学习,随处编写

无论当初正在应用什么技术栈,都能够随时引入 React 来开发新个性,而不须要重写现有代码。

React 还能够应用 Node 进行服务器渲染,或应用 React Native 开发原生挪动利用。因为 React 组件能够映射为对应的原生控件。在输入的时候,是输入 Web DOM,还是 Android 控件,还是 iOS 控件,就由平台自身决定了。所以,react 很不便和其余平台集成

何为高阶组件(higher order component)

高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和疏导形象。最常见的可能是 Redux 的 connect 函数。除了简略分享工具库和简略的组合,HOC 最好的形式是共享 React 组件之间的行为。如果你发现你在不同的中央写了大量代码来做同一件事时,就应该思考将代码重构为可重用的 HOC。

练习


  • 写一个反转其输出的 HOC
  • 写一个从 API 提供数据给传入的组件的 HOC
  • 写一个实现 shouldComponentUpdate 来防止 reconciliation 的 HOC
  • 写一个通过 React.Children.toArray 对传入组件的子组件进行排序的 HOC

**

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 中其余的场景简直都能够应用防抖和节流去进步响应性能。

何为受控组件(controlled component)

在 HTML 中,相似 <input>, <textarea><select> 这样的表单元素会保护本身的状态,并基于用户的输出来更新。当用户提交表单时,后面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,蕴含表单元素的组件将会在 state 中追踪输出的值,并且每次调用回调函数时,如 onChange 会更新 state,从新渲染组件。一个输出表单元素,它的值通过 React 的这种形式来管制,这样的元素就被称为 ” 受控元素 ”。

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

react16 的谬误边界(Error Boundaries)是什么

局部 UI 中的 JavaScript 谬误不应该毁坏整个应用程序。为了解决 React 用户的这个问题,React 16 引入了一个“谬误边界(Error Boundaries)”的新概念。

import React from 'react';
import ReactDOM from 'react-dom';
class ErrorBoundary extends React.Component{constructor(props) {super(props);
        this.state={hasError:false};
    }
    componentDidCatch(err,info) {this.setState({hasError: true});
    }
    render() {if (this.state.hasError) {return <h1>Something Went Wrong</h1>}
        return this.props.children;
    }
}

class Page extends React.Component{render() {
        return (
            <ErrorBoundary>
                <Clock/>
            </ErrorBoundary>
        )
    }
}
class Clock extends React.Component{render() {
        return (<div>hello{null.toString()}</div>
        )
    }
}

ReactDOM.render(<Page/>,document.querySelector('#root'));

React setState 调用的原理

具体的执行过程如下(源码级解析):

  • 首先调用了setState 入口函数,入口函数在这里就是充当一个散发器的角色,依据入参的不同,将其散发到不同的性能函数中去;
ReactComponent.prototype.setState = function (partialState, callback) {this.updater.enqueueSetState(this, partialState);
  if (callback) {this.updater.enqueueCallback(this, callback, 'setState');
  }
};
  • enqueueSetState 办法将新的 state 放进组件的状态队列里,并调用 enqueueUpdate 来解决将要更新的实例对象;
enqueueSetState: function (publicInstance, partialState) {
  // 依据 this 拿到对应的组件实例
  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
  // 这个 queue 对应的就是一个组件实例的 state 数组
  var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
  queue.push(partialState);
  //  enqueueUpdate 用来解决以后的组件实例
  enqueueUpdate(internalInstance);
}
  • enqueueUpdate 办法中引出了一个要害的对象——batchingStrategy,该对象所具备的isBatchingUpdates 属性间接决定了当下是要走更新流程,还是应该排队期待;如果轮到执行,就调用 batchedUpdates 办法来间接发动更新流程。由此能够揣测,batchingStrategy 或者正是 React 外部专门用于管控批量更新的对象。
function enqueueUpdate(component) {ensureInjected();
  // 留神这一句是问题的要害,isBatchingUpdates 标识着以后是否处于批量创立 / 更新组件的阶段
  if (!batchingStrategy.isBatchingUpdates) {
    // 若以后没有处于批量创立 / 更新组件的阶段,则立刻更新组件
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }
  // 否则,先把组件塞入 dirtyComponents 队列里,让它“再等等”dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {component._updateBatchNumber = updateBatchNumber + 1;}
}

留神:batchingStrategy 对象能够了解为“锁管理器”。这里的“锁”,是指 React 全局惟一的 isBatchingUpdates 变量,isBatchingUpdates 的初始值是 false,意味着“以后并未进行任何批量更新操作”。每当 React 调用 batchedUpdate 去执行更新动作时,会先把这个锁给“锁上”(置为 true),表明“当初正处于批量更新过程中”。当锁被“锁上”的时候,任何须要更新的组件都只能临时进入 dirtyComponents 里排队等待下一次的批量更新,而不能随便“插队”。此处体现的“工作锁”的思维,是 React 面对大量状态依然可能实现有序分批解决的基石。

除了在构造函数中绑定 this,还有其它形式吗

你能够应用属性初始值设定项 (property initializers) 来正确绑定回调,create-react-app 也是默认反对的。在回调中你能够应用箭头函数,但问题是每次组件渲染时都会创立一个新的回调。

React-Router 的路由有几种模式?

React-Router 反对应用 hash(对应 HashRouter)和 browser(对应 BrowserRouter)两种路由规定,react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现利用的 UI 和 URL 同步:

  • BrowserRouter 创立的 URL 格局:xxx.com/path
  • HashRouter 创立的 URL 格局:xxx.com/#/path

(1)BrowserRouter

它应用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来放弃 UI 和 URL 的同步。由此能够看出,BrowserRouter 是应用 HTML 5 的 history API 来管制路由跳转的:

<BrowserRouter
    basename={string}
    forceRefresh={bool}
    getUserConfirmation={func}
    keyLength={number}
/>

其中的属性如下:

  • basename 所有路由的基准 URL。basename 的正确格局是后面有一个前导斜杠,但不能有尾部斜杠;
<BrowserRouter basename="/calendar">
    <Link to="/today" />
</BrowserRouter>

等同于

<a href="/calendar/today" />
  • forceRefresh 如果为 true,在导航的过程中整个页面将会刷新。个别状况下,只有在不反对 HTML5 history API 的浏览器中应用此性能;
  • getUserConfirmation 用于确认导航的函数,默认应用 window.confirm。例如,当从 /a 导航至 /b 时,会应用默认的 confirm 函数弹出一个提醒,用户点击确定后才进行导航,否则不做任何解决;
// 这是默认的确认函数
const getConfirmation = (message, callback) => {const allowTransition = window.confirm(message);
  callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />

须要配合<Prompt> 一起应用。

  • KeyLength 用来设置 Location.Key 的长度。

(2)HashRouter

应用 URL 的 hash 局部(即 window.location.hash)来放弃 UI 和 URL 的同步。由此能够看出,HashRouter 是通过 URL 的 hash 属性来管制路由跳转的:

<HashRouter
    basename={string}
    getUserConfirmation={func}
    hashType={string}  
/>

其参数如下

  • basename, getUserConfirmation 和 BrowserRouter 性能一样;
  • hashType window.location.hash 应用的 hash 类型,有如下几种:

    • slash – 前面跟一个斜杠,例如 #/ 和 #/sunshine/lollipops;
    • noslash – 前面没有斜杠,例如 # 和 #sunshine/lollipops;
    • hashbang – Google 格调的 ajax crawlable,例如 #!/ 和 #!/sunshine/lollipops。

React 性能优化在哪个生命周期?它优化的原理是什么?

react 的父级组件的 render 函数从新渲染会引起子组件的 render 办法的从新渲染。然而,有的时候子组件的承受父组件的数据没有变动。子组件 render 的执行会影响性能,这时就能够应用 shouldComponentUpdate 来解决这个问题。

应用办法如下:

shouldComponentUpdate(nexrProps) {if (this.props.num === nexrProps.num) {return false}
    return true;
}

shouldComponentUpdate 提供了两个参数 nextProps 和 nextState,示意下一次 props 和一次 state 的值,当函数返回 false 时候,render()办法不执行,组件也就不会渲染,返回 true 时,组件照常重渲染。此办法就是拿以后 props 中值和下一次 props 中的值进行比照,数据相等时,返回 false,反之返回 true。

须要留神,在进行新旧比照的时候,是 浅比照,也就是说如果比拟的数据时援用数据类型,只有数据的援用的地址没变,即便内容变了,也会被断定为 true。

面对这个问题,能够应用如下办法进行解决:
(1)应用 setState 扭转数据之前,先采纳 ES6 中 assgin 进行拷贝,然而 assgin 只深拷贝的数据的第一层,所以说不是最完满的解决办法:

const o2 = Object.assign({},this.state.obj)
    o2.student.count = '00000';
    this.setState({obj: o2,})

(2)应用 JSON.parse(JSON.stringfy())进行深拷贝,然而遇到数据为 undefined 和函数时就会错。

const o2 = JSON.parse(JSON.stringify(this.state.obj))
    o2.student.count = '00000';
    this.setState({obj: o2,})

React 事件机制

<div onClick={this.handleClick.bind(this)}> 点我 </div>

React 并不是将 click 事件绑定到了 div 的实在 DOM 上,而是在 document 处监听了所有的事件,当事件产生并且冒泡到 document 处的时候,React 将事件内容封装并交由真正的处理函数运行。这样的形式不仅仅缩小了内存的耗费,还能在组件挂在销毁时对立订阅和移除事件。

除此之外,冒泡到 document 上的事件也不是原生的浏览器事件,而是由 react 本人实现的合成事件(SyntheticEvent)。因而如果不想要是事件冒泡的话应该调用 event.preventDefault()办法,而不是调用 event.stopProppagation()办法。JSX 上写的事件并没有绑定在对应的实在 DOM 上,而是通过事件代理的形式,将所有的事件都对立绑定在了 document 上。这样的形式不仅缩小了内存耗费,还能在组件挂载销毁时对立订阅和移除事件。

另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 本人实现的合成事件(SyntheticEvent)。因而咱们如果不想要事件冒泡的话,调用 event.stopPropagation 是有效的,而应该调用 event.preventDefault

实现合成事件的目标如下:

  • 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
  • 对于原生浏览器事件来说,浏览器会给监听器创立一个事件对象。如果你有很多的事件监听,那么就须要调配很多的事件对象,造成高额的内存调配问题。然而对于合成事件来说,有一个事件池专门来治理它们的创立和销毁,当事件须要被应用时,就会从池子中复用对象,事件回调完结后,就会销毁事件对象上的属性,从而便于下次复用事件对象。

(组件的)状态 (state) 和属性 (props) 之间有何不同

State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着工夫的推移而产生渐变,但少数时候是作为用户事件行为的后果。

Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能扭转本身的 props,然而能够把其子组件的 props 放在一起(对立治理)。Props 也不仅仅是数据 – 回调函数也能够通过 props 传递。

为什么调用 setState 而不是间接扭转 state?

解答

如果您尝试间接扭转组件的状态,React 将无奈得悉它须要从新渲染组件。通过应用 setState() 办法,React 能够更新组件的 UI。

另外,您还能够谈谈如何不保障状态更新是同步的。如果须要基于另一个状态(或属性)更新组件的状态,请向 setState() 传递一个函数,该函数将 state 和 props 作为其两个参数:

this.setState((state, props) => ({counter: state.counter + props.increment}));

在 React 中组件的 this.state 和 setState 有什么区别?

this.state 通常是用来初始化 state 的,this.setState 是用来批改 state 值的。如果初始化了 state 之后再应用 this.state,之前的 state 会被笼罩掉,如果应用 this.setState,只会替换掉相应的 state 值。所以,如果想要批改 state 的值,就须要应用 setState,而不能间接批改 state,间接批改 state 之后页面是不会更新的。

Redux Thunk 的作用是什么

Redux thunk 是一个容许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。如果满足某个条件,thunk 则能够用来提早 action 的派发(dispatch),这能够解决异步 action 的派发(dispatch)。

React 16 中新生命周期有哪些

对于 React16 开始利用的新生命周期:能够看出,React16 自上而下地对生命周期做了另一种维度的解读:

  • Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
  • Pre-commit 阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
  • Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。

与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:

  • 挂载过程:

    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • 更新过程:

    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • 卸载过程:

    • componentWillUnmount

React 的生命周期有哪些?

React 通常将组件生命周期分为三个阶段:

  • 装载阶段(Mount),组件第一次在 DOM 树中被渲染的过程;
  • 更新过程(Update),组件状态发生变化,从新更新渲染的过程;
  • 卸载过程(Unmount),组件从 DOM 树中被移除的过程;

1)组件挂载阶段

挂载阶段组件被创立,而后组件实例插入到 DOM 中,实现组件的第一次渲染,该过程只会产生一次,在此阶段会顺次调用以下这些办法:

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount
(1)constructor

组件的构造函数,第一个被执行,若没有显式定义它,会有一个默认的构造函数,然而若显式定义了构造函数,咱们必须在构造函数中执行 super(props),否则无奈在构造函数中拿到 this。

如果不初始化 state 或不进行办法绑定,则不须要为 React 组件实现构造函数Constructor

constructor 中通常只做两件事:

  • 初始化组件的 state
  • 给事件处理办法绑定 this
constructor(props) {super(props);
  // 不要在构造函数中调用 setState,能够间接给 state 设置初始值
  this.state = {counter: 0}
  this.handleClick = this.handleClick.bind(this)
}
(2)getDerivedStateFromProps
static getDerivedStateFromProps(props, state)

这是个静态方法,所以不能在这个函数里应用 this,有两个参数 propsstate,别离指接管到的新参数和以后组件的 state 对象,这个函数会返回一个对象用来更新以后的 state 对象,如果不须要更新能够返回 null

该函数会在装载时,接管到新的 props 或者调用了 setStateforceUpdate 时被调用。如当接管到新的属性想批改 state,就能够应用。

// 当 props.counter 变动时,赋值给 state 
class App extends React.Component {constructor(props) {super(props)
    this.state = {counter: 0}
  }
  static getDerivedStateFromProps(props, state) {if (props.counter !== state.counter) {
      return {counter: props.counter}
    }
    return null
  }

  handleClick = () => {
    this.setState({counter: this.state.counter + 1})
  }
  render() {
    return (
      <div>
        <h1 onClick={this.handleClick}>Hello, world!{this.state.counter}</h1>
      </div>
    )
  }
}

当初能够显式传入 counter,然而这里有个问题,如果想要通过点击实现 state.counter 的减少,但这时会发现值不会产生任何变动,始终放弃 props 传进来的值。这是因为在 React 16.4^ 的版本中 setStateforceUpdate 也会触发这个生命周期,所以当组件外部 state 变动后,就会从新走这个办法,同时会把 state 值赋值为 props 的值。因而须要多加一个字段来记录之前的 props 值,这样就会解决上述问题。具体如下:

// 这里只列出须要变动的中央
class App extends React.Component {constructor(props) {super(props)
    this.state = {
      // 减少一个 preCounter 来记录之前的 props 传来的值
      preCounter: 0,
      counter: 0
    }
  }
  static getDerivedStateFromProps(props, state) {
    // 跟 state.preCounter 进行比拟
    if (props.counter !== state.preCounter) {
      return {
        counter: props.counter,
        preCounter: props.counter
      }
    }
    return null
  }
  handleClick = () => {
    this.setState({counter: this.state.counter + 1})
  }
  render() {
    return (
      <div>
        <h1 onClick={this.handleClick}>Hello, world!{this.state.counter}</h1>
      </div>
    )
  }
}
(3)render

render 是 React 中最外围的办法,一个组件中必须要有这个办法,它会依据状态 state 和属性 props 渲染组件。这个函数只做一件事,就是返回须要渲染的内容,所以不要在这个函数内做其余业务逻辑,通常调用该办法会返回以下类型中一个:

  • React 元素:这里包含原生的 DOM 以及 React 组件;
  • 数组和 Fragment(片段):能够返回多个元素;
  • Portals(插槽):能够将子元素渲染到不同的 DOM 子树种;
  • 字符串和数字:被渲染成 DOM 中的 text 节点;
  • 布尔值或 null:不渲染任何内容。
(4)componentDidMount()

componentDidMount()会在组件挂载后(插入 DOM 树中)立刻调。该阶段通常进行以下操作:

  • 执行依赖于 DOM 的操作;
  • 发送网络申请;(官网倡议)
  • 增加订阅音讯(会在 componentWillUnmount 勾销订阅);

如果在 componentDidMount 中调用 setState,就会触发一次额定的渲染,多调用了一次 render 函数,因为它是在浏览器刷新屏幕前执行的,所以用户对此是没有感知的,然而我该当防止这样应用,这样会带来肯定的性能问题,尽量是在 constructor 中初始化 state 对象。

在组件装载之后,将计数数字变为 1:

class App extends React.Component  {constructor(props) {super(props)
    this.state = {counter: 0}
  }
  componentDidMount () {
    this.setState({counter: 1})
  }
  render ()  {
    return (
      <div className="counter">
        counter 值: {this.state.counter}      </div>
    )
  }
}

2)组件更新阶段

当组件的 props 扭转了,或组件外部调用了 setState/forceUpdate,会触发更新从新渲染,这个过程可能会产生屡次。这个阶段会顺次调用上面这些办法:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate
(1)shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState)

在说这个生命周期函数之前,来看两个问题:

  • setState 函数在任何状况下都会导致组件从新渲染吗?例如上面这种状况:
this.setState({number: this.state.number})
  • 如果没有调用 setState,props 值也没有变动,是不是组件就不会从新渲染?

第一个问题答案是 ,第二个问题如果是父组件从新渲染时,不论传入的 props 有没有变动,都会引起子组件的从新渲染。

那么有没有什么办法解决在这两个场景下不让组件从新渲染进而晋升性能呢?这个时候 shouldComponentUpdate 退场了,这个生命周期函数是用来晋升速度的,它是在从新渲染组件开始前触发的,默认返回 true,能够比拟 this.propsnextPropsthis.statenextState 值是否变动,来确认返回 true 或者 false。当返回 false 时,组件的更新过程进行,后续的 rendercomponentDidUpdate 也不会被调用。

留神: 增加 shouldComponentUpdate 办法时,不倡议应用深度相等查看(如应用 JSON.stringify()),因为深比拟效率很低,可能会比从新渲染组件效率还低。而且该办法保护比拟艰难,倡议应用该办法会产生显著的性能晋升时应用。

(2)getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(prevProps, prevState)

这个办法在 render 之后,componentDidUpdate 之前调用,有两个参数 prevPropsprevState,示意更新之前的 propsstate,这个函数必须要和 componentDidUpdate 一起应用,并且要有一个返回值,默认是 null,这个返回值作为第三个参数传给 componentDidUpdate

(3)componentDidUpdate

componentDidUpdate() 会在更新后会被立刻调用,首次渲染不会执行此办法。该阶段通常进行以下操作:

  • 当组件更新后,对 DOM 进行操作;
  • 如果你对更新前后的 props 进行了比拟,也能够抉择在此处进行网络申请;(例如,当 props 未发生变化时,则不会执行网络申请)。
componentDidUpdate(prevProps, prevState, snapshot){}

该办法有三个参数:

  • prevProps: 更新前的 props
  • prevState: 更新前的 state
  • snapshot: getSnapshotBeforeUpdate()生命周期的返回值

3)组件卸载阶段

卸载阶段只有一个生命周期函数,componentWillUnmount() 会在组件卸载及销毁之前间接调用。在此办法中执行必要的清理操作:

  • 革除 timer,勾销网络申请或革除
  • 勾销在 componentDidMount() 中创立的订阅等;

这个生命周期在一个组件被卸载和销毁之前被调用,因而你不应该再这个办法中应用 setState,因为组件一旦被卸载,就不会再装载,也就不会从新渲染。

4)错误处理阶段

componentDidCatch(error, info),此生命周期在后辈组件抛出谬误后被调用。它接管两个参数∶

  • error:抛出的谬误。
  • info:带有 componentStack key 的对象,其中蕴含无关组件引发谬误的栈信息

React 常见的生命周期如下:React 常见生命周期的过程大抵如下:

  • 挂载阶段,首先执行 constructor 构造方法,来创立组件
  • 创立实现之后,就会执行 render 办法,该办法会返回须要渲染的内容
  • 随后,React 会将须要渲染的内容挂载到 DOM 树上
  • 挂载实现之后就会执行 componentDidMount 生命周期函数
  • 如果咱们给组件创立一个 props(用于组件通信)、调用 setState(更改 state 中的数据)、调用 forceUpdate(强制更新组件)时,都会从新调用 render 函数
  • render 函数从新执行之后,就会从新进行 DOM 树的挂载
  • 挂载实现之后就会执行 componentDidUpdate 生命周期函数
  • 当移除组件时,就会执行 componentWillUnmount 生命周期函数

React 次要生命周期总结:

  1. getDefaultProps:这个函数会在组件创立之前被调用一次(有且仅有一次),它被用来初始化组件的 Props;
  2. getInitialState:用于初始化组件的 state 值;
  3. componentWillMount:在组件创立后、render 之前,会走到 componentWillMount 阶段。这个阶段我集体始终没用过、十分鸡肋。起初 React 官网曾经不举荐大家在 componentWillMount 里做任何事件、到当初 React16 间接废除了这个生命周期,足见其鸡肋水平了;
  4. render:这是所有生命周期中惟一一个你必须要实现的办法。一般来说须要返回一个 jsx 元素,这时 React 会依据 props 和 state 来把组件渲染到界面上;不过有时,你可能不想渲染任何货色,这种状况下让它返回 null 或者 false 即可;
  5. componentDidMount:会在组件挂载后(插入 DOM 树中后)立刻调用,标记着组件挂载实现。一些操作如果依赖获取到 DOM 节点信息,咱们就会放在这个阶段来做。此外,这还是 React 官网举荐的发动 ajax 申请的机会。该办法和 componentWillMount 一样,有且仅有一次调用。

Redux 中的 connect 有什么作用

connect 负责连贯 React 和 Redux

(1)获取 state

connect 通过 context 获取 Provider 中的 store,通过 store.getState() 获取整个 store tree 上所有 state

(2)包装原组件

将 state 和 action 通过 props 的形式传入到原组件外部 wrapWithConnect 返回—个 ReactComponent 对 象 Connect,Connect 重 新 render 内部传入的原组件 WrappedComponent,并把 connect 中传入的 mapStateToProps,mapDispatchToProps 与组件上原有的 props 合并后,通过属性的形式传给 WrappedComponent

(3)监听 store tree 变动

connect 缓存了 store tree 中 state 的状态,通过以后 state 状态 和变更前 state 状态进行比拟,从而确定是否调用 this.setState()办法触发 Connect 及其子组件的从新渲染

React Hook 的应用限度有哪些?

React Hooks 的限度次要有两条:

  • 不要在循环、条件或嵌套函数中调用 Hook;
  • 在 React 的函数组件中调用 Hook。

那为什么会有这样的限度呢?Hooks 的设计初衷是为了改良 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。

  • 组件之间难以复用状态逻辑。过来常见的解决方案是高阶组件、render props 及状态治理框架。
  • 简单的组件变得难以了解。生命周期函数与业务逻辑耦合太深,导致关联局部难以拆分。
  • 人和机器都很容易混同类。常见的有 this 的问题,但在 React 团队中还有类难以优化的问题,心愿在编译优化层面做出一些改良。

这三个问题在肯定水平上妨碍了 React 的后续倒退,所以为了解决这三个问题,Hooks 基于函数组件 开始设计。然而第三个问题决定了 Hooks 只反对函数组件。

那为什么不要在循环、条件或嵌套函数中调用 Hook 呢?因为 Hooks 的设计是基于数组实现。在调用时按程序退出数组中,如果应用循环、条件或嵌套函数很有可能导致数组取值错位,执行谬误的 Hook。当然,本质上 React 的源码里不是数组,是链表。

这些限度会在编码上造成肯定水平的心智累赘,老手可能会写错,为了防止这样的状况,能够引入 ESLint 的 Hooks 查看插件进行预防。

正文完
 0