TODO

  • 为什么要用React

    思路:讲概念、说用途、理思路、列优缺点的方式。

1、React是一个网页UI框架,通过组件化的方式解决视图层开发复用的问题,本质是一个组件化框架。

  • jQuery诞生于2005年,浏览器兼容性是当时最大的问题,jquery封装了dom操作,ajax等。本质上它是一个工具函数合集,并没有解决代码如何组织的问题。
  • 随着互联网的发展,如何组织代码结构,有效提高复用率成为问题。angualarJS提供了一揽子全家桶解决方案,从底层开始深度封装,向上提供了路由、双向绑定、指令、组件等框架特性。
  • 但是庞大复杂的概念,学习成本高。前端和后端不同,以UI为基础,需要一个可以使组件复用的开发方案,前端工程越来越复杂,组件作为基本单位应该是可以通过编写单元测试来维持稳定性的。React解决了这个问题。React中只有组件,没有页面,没有控制器,也没有模型,也由于虚拟Dom的关系,适用场景比传统框架更加广泛,不再局限于Web开发。

2、Rect的核心设计思路有三点:声明式、组件化、通用性

  • 声明式的优势在于直观和组合。 ```typescript //命令式 // html

// js const block = $(‘.block’) block.css(‘color’,’red’)

// 声明式: const Block = (props)=>

  1. - 组件化的优势在于视图的拆分和模块复用,可以更容易做到高内聚低耦合。
  2. - 通用性在于一次学习,随处编写。比如React NativeReact 360等,主要靠虚拟Dom来保证实现。
  3. 3、缺点:**React并不是一个一揽子框架**,像router、状态解决方案,导致在技术选型和学习使用上有比较高的成本。
  4. <a name="dU9zC"></a>
  5. # JSX
  6. <a name="PR6Qc"></a>
  7. ## React为什么要用JSX
  8. > 破题:面试官的为什么不用ABC其他解决方案?
  9. > 考察两个方面:
  10. > 1、技术广度:深挖知识面涉猎度,对流行框架的模板方案是否知悉料及
  11. > 2、技术方案调研能力
  12. > 答题三步走:一句话解释JSX,核心概念,方案对比。
  13. 1JSX是一个**JavaScript的语法拓展**,结构类似于XML
  14. - 用于声明React中的元素,但React不强制使用JSX,使用了,也会构建过程中,通过babel插件编译为React.createElement
  15. React并**不想引入JavaScript以外的开发体系**,更希望通过合理的**关注度分离**,保持组件开发的纯粹性。<br />引入JSX主要是XML在树结构的描述上具有**可读性**强的优势。<br />2、与JSX同等的其他替代方案对比:
  16. - **模板**:React认为模板并不是开发过程中的关注点,因为引入模板和指令是一种不佳的实现方案:因为模板分离了技术栈(弱关注度分离),引入的概念多(新的模板语法,模板指令)。
  17. - **模板字符串**:模板字符串编写的结构会造成多次内部嵌套,使整个结构描述复杂,优化语法代码提示也会变得困难。
  18. - JXON:和JSX类似,但是最终放弃的原因是语法提示困难。
  19. 综上所述:JSXReact的设计思想贴合,不需要引入过多的概念,编辑器的代码提示也友好。
  20. <a name="wLNk7"></a>
  21. ## Babel插件如何实现JSX到JS的编译?
  22. 过程:babel读取代码并解析成AST,再讲AST传入插件层进行转换,把JSX的结构转换成React.createElement的函数。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/282427/1652604951716-1c16de15-1f06-458e-a58a-489dfee759d7.png#clientId=u4708363f-fcf0-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown%20error&from=paste&height=378&id=u6c29eca2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=756&originWidth=1310&originalType=binary&ratio=1&rotation=0&showTitle=false&size=361131&status=error&style=none&taskId=ud7f9d7cf-9f9c-448c-9490-c355e578765&title=&width=655)
  23. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/282427/1652605057199-14905bf8-0bbd-48ef-a562-d231b3aa9404.png#clientId=u4708363f-fcf0-4&crop=0&crop=0&crop=1&crop=1&errorMessage=unknown%20error&from=paste&height=387&id=ueb40aa05&margin=%5Bobject%20Object%5D&name=image.png&originHeight=774&originWidth=1322&originalType=binary&ratio=1&rotation=0&showTitle=false&size=422144&status=error&style=none&taskId=u878366c6-896c-4a85-906d-fa787e4cebd&title=&width=661)
  24. <a name="NdFMx"></a>
  25. # 生命周期
  26. <a name="MCzfn"></a>
  27. ## 如何避免生命周期的坑?
  28. > 一、为什么会有坑?
  29. > 1、在不恰当的时机调用了不合适的代码
  30. > 2、在需要调用时,却忘记调用了
  31. > 二、生命周期讨论的对象
  32. > 讨论React生命周期时,讨论的是类组件。
  33. > 函数式组件没有生命周期的概念,本身就是个函数只会从头到尾执行。
  34. > 生命周期讨论的挂载、更新、卸载组件的完整流程。
  35. 1、基于周期的梳理,确认生命周期函数的使用方式<br />2、基于职责的梳理,确认生命周期函数的适用范围<br />![](https://cdn.nlark.com/yuque/0/2022/jpeg/282427/1652610568370-4241426b-4f9d-4d66-970b-8f877a76415f.jpeg)
  36. 一、以下三种情况下会触发重新渲染
  37. - 函数组件:**任何情况下都会重新渲染**,没有生命周期,官方提供`React.memo`优化手段,不是阻断渲染,而是跳过渲染组件的操作,并直接复用最后一次的渲染结果,与`shouldComponentUpdate`完全不同。
  38. ```typescript
  39. const MyComonent = React.memo(function MyComonent(props),{
  40. // 使用props渲染
  41. })
  • React.Component组件:不实现shouldComponentUpdate有两种情况会触发重新渲染
    1. 当state变化时
    2. 当父级组件的props传入时
  • React.PureConponet组件:默认实现了shouldConponentUpdate函数,仅在props与state进行浅比较后,确认有变更时才会触发重新渲染。(浅比较:仅对比外层)

二、渲染中报错后会怎么样?该如何处理?
错误边界:捕获错误,并可以自定义降级UI渲染,渲染时的报错,只能通过componentDidCatch捕获。

React的请求应该放在那里?

对于异步请求,应该放在componentDidMount中操作。
从时间顺序来看,除了componentDidMount之外还可以有以下选择:

  • constructor: 可以放,但从设计而言不推荐,它主要用于初始化state和函数绑定
  • componentWillMount: 已经被标记弃用,在新的异步渲染架构下会触发多次渲染,易引发Bug,不利于未来react升级后的代码维护。

    Component组件的执行顺序

    1. contructor
    2. componentWillMount
    3. render
    4. constructor
    5. componentWillMount
    6. render
    7. componentDidMoun
    8. componentDidMount
    1. render
    2. render
    3. useEffect
    4. useEffect

    组件

    类组件和函数组件有什么区别?

    有什么区别的问题应该如何回答? 求同存异的过程:确定共性的基础上才能找到它独特的个性,通过具体场景逐个阐述它的个性。 面试官想知道的是,你对组件的两种编写模式是否了解?是否具备合适场景选用合适技术栈的能力? 1、共同点:从组件使用方式和表达效果总结相同点 2、不同点:从代码实现,独有特性、具体场景、设计模式、未来趋势、等细分领域描述不同点。

相同
类组件和函数组件在使用和呈现上完全一致,性能上也不会有明显的差异。
不同
1、代码实现:类组件和函数组件代表两种不同的设计思想与心智模式

  • 类组件的根基是OOP,面向对象编程,主打继承、生命周期等核心概念。
  • 函数式组件的根基是FP,主打函数式编程,引用透明。

所以,相较于类组件,函数组件更纯粹、简单、易测试。
2、独有特性:

  • 类组件通过生命周期包装业务逻辑。
  • 函数组件提供新的开发模式让组件渲染与业务逻辑更分离

3、使用场景:

  • 在不适用Recompose或者Hooks的情况下,如果需要使用生命周期,就用类组件。
  • 在recompose和Hooks的加持下,类组件和函数组件的能力边界完全相同,都可以提供类似生命周期和状态的能力。

4、设计模式:

  • 类组件可以实现继承
  • 函数组件缺少继承能力,继承并不是最佳的组件设计模式,官方更推荐组合,所以类组件继承的优势在淡出。

5、性能优化:

  • 类组件优化依靠shuldComponentUpdate函数取阻断渲染
  • 函数组件靠React.memo来优化。

6、未来趋势
函数组件成为未来社区主推的方案,原因是类组件的缺点:

  • this的模糊性
  • 业务逻辑散落在生命周期中
  • react组件代码缺少标准的代码拆分的方式,使用Hooks的方式可以提供更细粒度的组件复用,也能更好的适用于时间切片和并发模式。

    如何设计React组件

    考察是否了解React组件的设计模式。 如果缺乏指导性的设计模式,直接开发,代码会非常混乱。 破题: 1、如何将组件更好地组合 2、围绕“如何组合”通过列举场景的方式展示设计模式的分类及用途。

1、React组件设计分类

  • 展示组件:无状态组件,复用性强,展示组件受制于外部的props控制
  • 灵巧组件:有状态组件,专注业务本身。采用了关注点分离的思想。

React - 图2
2、组件的工程实践:目录划分,引入工程管理(storybook)

  • basic : 最基本的展示组件
  • container: 容器组件
  • hoc: 高阶组件
  • pages:页面组件

image.png
如何在渲染劫持中为原本的渲染结果添加新的样式

如何面向组件跨层级通信

试探是否有经手大型前端项目经验,跨层级通信是所有现代前端框架都会遇到的场景 解题思路:一个基本,多个场景 先确定主题,在根据场景列举。 1、父与子 2、子与父 3、兄弟 4、无直接关系

React - 图4

React Hook

setState同步还是异步?什么有时连续多次 setState只有一次生效

“是A还是B”是常被问的迷惑性问题,需要根据不同场景去进行探讨。 setState用于变更状态,触发组件重新渲染,更新UI。

1、有时候是同步的,有时候是异步的,所谓的异步,指的是批量更新的操作。
setState函数的实现中,会根据一个变量isBatchingUpdates判断这个更新是直接更新还是放在队列。 在React可以控制的场景isBatchingUpdatestrue,比如声明周期和合成事件,在不能更新的场景只能同步更新。
异步更新是为了性能优化和保持一致性,如果同步更新,state更新了,但props不是。
2、同步场景:DOM 原生事件、定时器

  • 同步事件不会通过合成事件的方式处理,进入不了react更新事务的流程,所以是直接更新
  • 定时器也一样,回调执行的时候,react已经完成了原组件的事务更新流程,其结果也是同步的。

3、批量更新场景:合成事件、钩子函数里面
在React生命周期和合成事件中,可以拿到isBatchingUpdates控制权,将队列放进队列,控制执行节奏。
队列对象有个过期时间,过期时间接近,就会把操作合并起来,一次更新。
如果内存中多个setState, 如果传入的是对象,会被合并,如果是函数,不会。

为什么要有react hook (虎牙)

1、function componet比class component好的地方

  • 逻辑复用困难,高阶组件可能存在props覆盖等问题
  • 没有this指向的问题、编译后的代码更轻量
  • class组件大型组件难以拆分,重构,单元测试
  • 对于UI框架而言,组合优于继承。Class Component会继承Reat.Component,编译后会产生多余的代码,比如你不需要那么多的生命周期函数。然后,也不利于做treeShaking。

2、React之前的解决方案—高阶组件的缺点

  • props来源不清晰。和vue的mixins一样的。
  • 高阶组件需要实例化一个父组件来实现,性能上不如hooks。👉🏻例子
  • 高阶组件太多,容易命名冲突。

3、react hook解决的问题:

  • 组件间状态相关的逻辑共享: 解决难以复用的共享组件中与状态相关的逻辑
  • 复杂组件的逻辑解耦:复杂组件中不相关的逻辑可能需要放在通过生命周期里面,这样难以开发维护。
  • 业务变动,原本的函数组件可能需要改成类组件
    react hook解决了状态相关的重用问题,每次useHook都会生成一份独立的状态,通过自定义hook能够更好地封装我们的功能。

3、react hook依然存在的问题
1、异步场景,有闭包陷阱问题,用useRef解决
2、必须清楚useEffect和useCallback的依赖数组的改变时机,useEffect依赖一个函数,形成一条依赖链,一旦某个依赖意外触发,有可能带来useEffect意外触发
3、不要再循环嵌套中使用hook。

useEffect和useLayoutEffect的区别?

简单来说就是同步和异步的区别,useEffectLayout浏览器渲染前,useEffect 在渲染后。

  • uesEffect和useLayoutEffect最终都在Fiber的updateQueue中,但是他们的effect tag不同,执行的时机不同。
  • useEffect 会在commit阶段,在执行dom操作前被调度,执行Dom操作后被异步执行,useEffectLayout是在执行Dom操作后被同步渲染,此时渲染流水线还没完成绘制合成,所以useEffectLayout里面的回调发生在浏览器渲染前,useEffect 在渲染后。


React 底层设计

Fiber干了什么事情?requestIdleCallback了解多少?

React Fiber是React内部实现的一套更新机制。支持任务不同优先级,可中断与恢复,并且恢复后可以复用之前的中间态。
Fiber就是这套更新机制的工作单元,执行更新任务的整个过程就是在反复寻找工作单元并且运行他们,实现了任务拆分。
它存储了很多上下文的信息,Fiber有3层含义:
(1)架构层面:保存了Fiber节点的递归信息,fiber链表和fiber的连接构成fiber树。称为fiber Reconciler。
(2)静态数据结构层面:每个Fiber节点对应一个React Element,保存了该组件的类型(函数组件/类组件/原生组件)、对应的DOM节点信息,
(3)动态的工作单元层面:每个Fiber节点保存了本次更新中该组件改变的状态,要执行的工作(删除/新增/更新/移动)、调度优先级。

为什么react是双缓存队列的设计(虎牙)

  • 避免白屏或者避免屏幕出现更新一半的树(异步可中断更新)。
  • 复用之前的树

    状态管理

    列举一种你了解的React状态管理框架

    破题:Context存储的变量难以追溯数据源以及确认变动点,也会提升组件的耦合度。 状态管理框架解决了这个问题

React - 图5
1Flux

MVC:双向数据流架构

image.png

单向数据流

  • view : 视图层,即代码中的React组件
  • Store:数据层,维护了数据和数据处理的逻辑
  • Dispathcer:管理数据流动的中央枢纽
  • Action:一种事件通知,通常用type标记

image.png

React 如何做性能优化

  1. React dev tools 判断哪个组件有问题

    2、把不变的从变的中分离