最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 2021React面试精选——持续更新

    正文概述 掘金(EmilyYu)   2021-02-14   693

    目录

    1. React的请求应该放在哪个⽣命周期中
    2. jsx的本质是什么
    3. React组件通信如何实现
    4. React最新的⽣命周期是怎样的
    5. setState到底是异步还是同步
    6. React中keys的作用是什么
    7. 受控组件和非受控组件区别是啥
    8. 如何避免组件的重新渲染
    9. 谈一下你对fiber的理解
    10. 什么是高阶组件
    11. 虚拟DOM是什么
    12. 讲下redux的⼯作流程
    13. react-redux是如何⼯作的
    14. redux与mobx的区别
    15. redux中如何进⾏异步操作
    16. redux异步中间件之间的优劣

    1、React的请求应该放在哪个⽣命周期中?

    React的异步请求到底应该放在哪个⽣命周期⾥,有⼈认为在componentWillMount中可以提前进⾏异步请求,避免⽩屏,其实这个观点是有问题的。

    由于JavaScript中异步事件的性质,当您启动API调⽤时,浏览器会在此期间返回执⾏其他⼯作。当React渲染⼀个组件时,它不会等待componentWillMount它完成任何事情。React继续前进并继续render,没有办法“暂停”渲染以等待数据到达。

    ⽽且在componentWillMount请求会有⼀系列潜在的问题。⾸先,在服务器渲染时,如果在componentWillMount⾥获取数据,fetch data会执⾏两次,⼀次在服务端⼀次在客户端,这造成了多余的请求。其次,在React 16进⾏React Fiber重写后, componentWillMount可能在⼀次渲染中多次调⽤。

    ⽬前官⽅推荐的异步请求是在componentDidmount中进⾏。如果有特殊需求需要提前请求,也可以在特殊情况下在constructor中请求。react 17之后 componentWillMount会被废弃,仅仅保留UNSAFE_componentWillMount。

    2、jsx的本质是什么?

    首先了解下jsx是什么

    1. JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
    2. 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
    3. 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind)。

    JSX其实是嵌入到JavaScript中的一种结构语法。

    实际上,jsx仅仅只是React.createElement(component, props, ...children)函数的语法糖。所有的jsx最终都会被转换成React.createElement的函数调用。

    createElement需要传递三个参数

    参数一:type

    • 当前ReactElement的类型;
    • 如果是标签元素,那么就使用字符串表示 “div”;
    • 如果是组件元素,那么就直接使用组件的名称;

    参数二:config

    • 所有jsx中的属性都在config中以对象的属性和值的形式存储

    参数三:children

    • 存放在标签中的内容,以children数组的方式进行存储;
    • 当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方

    3、React组件通信如何实现?

    React组件间通信⽅式:

    • ⽗组件向⼦组件通讯:⽗组件可以向⼦组件通过传props 的⽅式,向⼦组件进⾏通讯;
    • ⼦组件向⽗组件通讯:props+回调的⽅式,⽗组件向⼦组件传递props进⾏通讯,此props为作⽤域为⽗组件⾃身的函数,⼦组件调⽤该函数,将⼦组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中;
    • 兄弟组件通信:找到这两个兄弟节点共同的⽗节点,结合上⾯两种⽅式由⽗节点转发信息进⾏通信;
    • 跨层级通信:Context设计⽬的是为了共享那些对于⼀个组件树⽽⾔是“全局”的数据,例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过 Context通信再适合不过;
    • 发布订阅模式:发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event模块进⾏通信;
    • 全局状态管理⼯具:借助Redux或者Mobx等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store,并根据不同的事件产⽣新的状态。

    4、React最新的⽣命周期是怎样的?

    React 16之后有三个⽣命周期被废弃(但并未删除)

    • componentWillMount
    • componentWillReceiveProps
    • componentWillUpdate

    官⽅计划在17版本完全删除这三个函数,只保留UNSAVE_前缀的三个函数,⽬的是为了向下兼容,但是对于开发者⽽⾔应该尽量避免使⽤他们,⽽是使⽤新增的⽣命周期函数替代它们。

    ⽬前React16.8+的⽣命周期分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。

    挂载阶段:

    • constructor:构造函数,最先被执⾏,我们通常在构造函数⾥初始化state对象或者给⾃定义⽅法绑定this;
    • getDerivedStateFromProps:static getDerivedStateFromProps(nextProps, prevState),这是个静态⽅法,当我们接收到新的属性想去修改我们state, 可以使⽤getDerivedStateFromProps
    • render:render函数是纯函数,只返回需要渲染的东⻄,不应该包含其它的业务逻辑,可以返回原⽣的DOM、React组件、Fragment、Portals、字符串和数字、 Boolean和null等内容;
    • componentDidMount:组件装载之后调⽤,此时我们可以获取到DOM节点并操作,⽐如对canvas,svg的操作,服务器请求,订阅都可以写在这个⾥⾯,但是记得在componentWillUnmount中取消订阅;

    更新阶段:

    • getDerivedStateFromProps: 此⽅法在更新个挂载阶段都可能会调⽤;
    • shouldComponentUpdate:shouldComponentUpdate(nextProps, nextState),有两个参数nextProps和nextState,表示新的属性和变化之后的state,返回⼀个布尔值,true表示会触发重新渲染,false表示不会触发重新渲染,默认返回true,我们通常利⽤此⽣命周期来优化React程序性能;
    • render:更新阶段也会触发此⽣命周期;
    • getSnapshotBeforeUpdate:getSnapshotBeforeUpdate(prevProps, prevState),这个⽅法在render之后,componentDidUpdate之前调⽤,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有⼀个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此⽣命周期必须与componentDidUpdate搭配使⽤;
    • componentDidUpdate:componentDidUpdate(prevProps, prevState, snapshot),该⽅法在getSnapshotBeforeUpdate⽅法之后被调⽤,有三个参数prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,如果触发某些回调函数时需要⽤到DOM元素的状态,则将对⽐或计算的过程迁移⾄getSnapshotBeforeUpdate,然后在componentDidUpdate中统⼀触发回调或更新状态。

    卸载阶段:

    -componentWillUnmount:当我们的组件被卸载或者销毁了就会调⽤,我们可以在这个函数⾥去清除⼀些定时器,取消⽹络请求,清理⽆效的DOM元素等垃圾清理⼯作。

    总结:

    • componentWillMount:在渲染之前执行,用于根组件中的 App 级配置;
    • componentDidMount:在第一次渲染之后执行,可以在这里做AJAX请求,DOM的操作或状态更新以及设置事件监听器;
    • componentWillReceiveProps:在初始化render的时候不会执行,它会在组件接受到新的状态(Props)时被触发,一般用于父组件状态更新时子组件的重新渲染
    • shouldComponentUpdate:确定是否更新组件。默认情况下,它返回true。如果确定在state或props更新后组件不需要在重新渲染,则可以返回false,这是一个提高性能的方法;
    • componentWillUpdate:在shouldComponentUpdate返回true确定要更新组件之前件之前执行;
    • componentDidUpdate:它主要用于更新DOM以响应props或state更改;
    • componentWillUnmount:它用于取消任何的网络请求,或删除与组件关联的所有事件监听器。

    5、setState到底是异步还是同步?

    先给出答案: 有时表现出异步,有时表现出同步。

    • setState只在合成事件和钩⼦函数中是“异步”的,在原⽣事件和setTimeout中都是同步的;
    • setState的“异步”并不是说内部由异步代码实现,其实本身执⾏的过程和代码都是同步的,只是合成事件和钩⼦函数的调⽤顺序在更新之前,导致在合成事件和钩⼦函数中没法⽴⻢拿到更新后的值,形成了所谓的“异步”,当然可以通过第⼆个参数setState(partialState, callback)中的callback拿到更新后的结果;
    • setState的批量更新优化也是建⽴在“异步”(合成事件、钩⼦函数)之上的,在原⽣事件和setTimeout中不会批量更新,在“异步”中如果对同⼀个值进⾏多次 setState,setState的批量更新策略会对其进⾏覆盖,取最后⼀次的执⾏,如果是同时setState多个不同的值,在更新时会对其进⾏合并批量更新。

    6、React中keys的作用是什么?

    render () {
      return (
        <ul>
          {this.state.todoItems.map(({item,i}) => {
            return <li key={i}>{item}</li>
          })}
        </ul>
      )
    }
    

    在React Diff算法中React会借助元素的Key值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。

    7、受控组件和非受控组件区别是啥?

    • 受控组件是React控制中的组件,并且是表单数据真实的唯一来源。
    • 非受控组件是由DOM处理表单数据的地方,而不是在 React 组件中。

    尽管非受控组件通常更易于实现,因为只需使用refs即可从DOM中获取值,但通常建议优先选择受控制的组件,而不是非受控制的组件。

    这样做的主要原因是受控组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式。

    8、如何避免组件的重新渲染?

    React中最常见的问题之一是组件不必要地重新渲染。React提供了两个方法,在这些情况下非常有用:

    • React.memo():这可以防止不必要地重新渲染函数组件;
    • PureComponent:这可以防止不必要地重新渲染类组件。

    这两种方法都依赖于对传递给组件的props的浅比较,如果props没有改变,那么组件将不会重新渲染。虽然这两种工具都非常有用,但是浅比较会带来额外的性能损失,因此如果使用不当,这两种方法都会对性能产生负面影响。

    通过使用React Profiler,可以在使用这些方法前后对性能进行测量,从而确保通过进行给定的更改来实际改进性能。

    9、谈一下你对fiber的理解?

    参考:https://juejin.cn/post/6844903582622285831

    简单的来说就是,react的渲染和更新分为两个阶段:

    • reconciliation阶段:执行diff算法,纯JS计算;
    • commit阶段:将diff结果渲染DOM。

    但是这么操作,可能会造成性能问题,比如:

    • JS是单线程,且和DOM渲染共用一个线程;
    • 当组件足够复杂,组件更新时计算和渲染都压力大;
    • 同时再有DOM操作需求(动画,鼠标拖拽等),将卡顿。

    解决方案fiber:

    • 将reconciliation阶段进行任务拆分(commit无法拆分);
    • DOM需要渲染时暂停,空闲时恢复;
    • window.requestIdleCallback(方法将在浏览器的空闲时段内对要调用的函数进行排队)

    10、什么是高阶组件?

    高阶组件(HOC)是接受一个组件并返回一个新组件的函数。基本上,这是一个模式,是从React的组合特性中衍生出来的,称其为纯组件,因为它们可以接受任何动态提供的子组件,但不会修改或复制输入组件中的任何行为。

    const EnhancedComponent = higherOrderComponent(WrappedComponent);
    

    HOC 可以用于以下许多用例:

    1. 代码重用、逻辑和引导抽象
    2. 渲染劫持
    3. state抽象和操作
    4. props处理

    11、虚拟DOM是什么?

    虚拟DOM本质就是用一个原生的JavaScript对象去描述一个DOM节点,是对真实DOM的一层抽象。

    真实DOM节点:

    <div id="container">
      <ul>
        <li></li>
      </ul>
    </div>
    

    JS模拟虚拟DOM:

    const tree = Element('div', { id: 'container' }, {
      Element('ul', {}, [
        Element('li', {}, ['新节点值'])
      ]),
    });
    
    const root = tree.render();
    document.querySelector('#container').appendChild(root);
    

    可以看到虚拟DOM对象最基本的三个属性:

    • 标签类型
    • 标签元素的属性
    • 标签元素的子节点

    12、讲下redux的⼯作流程?

    ⾸先,我们看下⼏个核⼼概念:

    • Store:保存数据的地⽅,你可以把它看成⼀个容器,整个应⽤只能有⼀个Store;
    • State:Store对象包含所有数据,如果想得到某个时点的数据,就要对Store⽣成快照,这种时点的数据集合,就叫State;
    • Action: State的变化, 会导致View的变化。但是,⽤户接触不到State,只能接触到View。所以,State的变化必须是View导致的。Action就是View发出的通知,表示State应该要发⽣变化了;
    • Action Creator:View要发送多少种消息,就会有多少种Action。如果都⼿写,会很麻烦,所以我们定义⼀个函数来⽣成Action,这个函数就叫Action Creator;
    • Reducer:Store收到Action以后,必须给出⼀个新的State,这样View才会发⽣变化。这种State的计算过程就叫做Reducer。Reducer是⼀个函数,它接受Action和当前State作为参数,返回⼀个新的State;
    • dispatch:是View发出Action的唯⼀⽅法。

    然后我们过下整个⼯作流程:

    1. ⾸先,⽤户(通过View)发出Action,发出⽅式就⽤到了dispatch⽅法;
    2. 然后,Store⾃动调⽤Reducer,并且传⼊两个参数:当前State和收到的Action,Reducer会返回新的State;
    3. State⼀旦有变化,Store就会调⽤监听函数,来更新View。

    到这⼉为⽌,⼀次⽤户交互流程结束。可以看到,在整个流程中数据都是单向流动的,这种⽅式保证了流程的清晰。

    13、react-redux是如何⼯作的?

    • Provider: Provider的作⽤是从最外部封装了整个应⽤,并向connect模块传递store
    • connect: 负责连接React和Redux
      • 获取state: connect通过context获取Provider中的store, 通过store.getState()获取整个store tree 上所有state
      • 包装原组件: 将state和action通过props的⽅式传⼊到原组件内部wrapWithConnect返回⼀个ReactComponent对象Connect,Connect重新render外部传⼊的原组件WrappedComponent,并把connect中传⼊的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,通过属性的⽅式传给WrappedComponent
      • 监听store tree变化: connect缓存了store tree中state的状态,通过当前state状态和变更前state状态进⾏⽐较,从⽽确定是否调⽤ this.setState() ⽅法触发Connect及其⼦组件的重新渲染

    14、redux与mobx的区别?

    两者对⽐:

    • redux将数据保存在单⼀的store中,mobx将数据保存在分散的多个store中
    • redux使⽤plain object保存数据,需要⼿动处理变化后的操作;mobx适⽤observable保存数据,数据变化后⾃动处理响应的操作
    • redux使⽤不可变状态,这意味着状态是只读的,不能直接去修改它,⽽是应该返回⼀个新的状态,同时使⽤纯函数;mobx中的状态是可变的,可以直接对其进⾏修改

    mobx相对来说⽐较简单,在其中有很多的抽象,mobx更多的使⽤⾯向对象的编程思维;redux会⽐较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助⼀系列的中间件来处理异步和副作⽤

    • mobx中有更多的抽象和封装,调试会⽐较困难,同时结果也难以预测;⽽redux提供能够进⾏时间回溯的开发⼯具,同时其纯函数以及更少的抽象,让调试变得更加的容易

    场景辨析:

    • 基于以上区别,我们可以简单得分析⼀下两者的不同使⽤场景。
    • mobx更适合数据不复杂的应⽤:mobx难以调试,很多状态⽆法回溯,⾯对复杂度⾼的应⽤时,往往⼒不从⼼。
    • redux适合有回溯需求的应⽤:⽐如⼀个画板应⽤、⼀个表格应⽤,很多时候需要撤销、重做等操作,由于redux不可变的特性,天然⽀持这些操作。
    • mobx适合短平快的项⽬:mobx上⼿简单,样板代码少,可以很⼤程度上提⾼开发效率。
    • 当然mobx和redux也并不⼀定是⾮此即彼的关系,你也可以在项⽬中⽤redux作为全局状态管理,⽤mobx作为组件局部状态管理器来⽤。

    15、redux中如何进⾏异步操作?

    • 当然,我们可以在componentDidmount中直接进⾏请求⽆须借助redux。
    • 但是在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的管理,通常情况下我们会借助redux的异步中间件进⾏异步处理。
    • redux异步流中间件其实有很多,但是当下主流的异步中间件只有两种redux-thunk、redux-saga,当然redux-observable可能也有资格占据⼀席之地,其余的异步中间件不管是社区活跃度还是npm下载量都⽐较差了。

    16、redux异步中间件之间的优劣?

    redux-thunk优点:

    • 体积⼩:redux-thunk的实现⽅式很简单,只有不到20⾏代码;
    • 使⽤简单:redux-thunk没有引⼊像redux-saga或者redux-observable额外的范式,上⼿简单。

    redux-thunk缺陷:

    • 样板代码过多:与redux本身⼀样,通常⼀个请求需要⼤量的代码,⽽且很多都是重复性质的;
    • 耦合严重:异步操作与redux的action偶合在⼀起,不⽅便管理;
    • 功能孱弱:有⼀些实际开发中常⽤的功能需要⾃⼰进⾏封装。

    redux-saga优点:

    • 异步解耦:异步操作被被转移到单独saga.js中,不再是掺杂在action.js或component.js中;
    • action摆脱thunk function: dispatch的参数依然是⼀个纯粹的 action (FSA),⽽不是充满 “⿊魔法” thunk function;
    • 异常处理:受益于 generator function 的saga实现,代码异常/请求失败都可以直接通过try/catch语法直接捕获处理;
    • 功能强⼤:redux-saga提供了⼤量的Saga辅助函数和Effect创建器供开发者使⽤,开发者⽆须封装或者简单封装即可使⽤;
    • 灵活:redux-saga可以将多个Saga可以串⾏/并⾏组合起来,形成⼀个⾮常实⽤的异步flow;
    • 易测试,提供了各种case的测试⽅案,包括mock task,分⽀覆盖等等。

    redux-saga缺陷:

    • 额外的学习成本:redux-saga不仅在使⽤难以理解的generator function,⽽且有数⼗个API,学习成本远超reduxthunk,最重要的是你的额外学习成本是只服务于这个库的,与redux-observable不同,redux-observable虽然也有额外学习成本但是背后是rxjs和⼀整套思想;
    • 体积庞⼤:体积略⼤,代码近2000⾏,min版25KB左右;
    • 功能过剩:实际上并发控制等功能很难⽤到,但是我们依然需要引⼊这些代码;
    • ts⽀持不友好:yield⽆法返回TS类型。

    redux-observable优点:

    • 功能最强:由于背靠rxjs这个强⼤的响应式编程的库,借助rxjs的操作符,你可以⼏乎做任何你能想到的异步处理;
    • 背靠rxjs:由于有rxjs的加持,如果你已经学习了rxjs,redux-observable的学习成本并不⾼,⽽且随着rxjs的升级reduxobservable也会变得更强⼤。

    redux-observable缺陷:

    • 学习成本奇⾼:如果你不会rxjs,则需要额外学习两个复杂的库;
    • 社区⼀般:redux-observable的下载量只有redux-saga的1/5,社区也不够活跃,在复杂异步流中间件这个层⾯reduxsaga仍处于领导地位。

    起源地下载网 » 2021React面试精选——持续更新

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元