React 的生命周期,主要可分为 初始化阶段、挂载阶段、更新阶段、卸载阶段。
初始化阶段
发生在 constructor 中的内容,在 constructor 中进行 state、props 的初始化,在这个阶段修改 state,不会执行更新阶段的生命周期,可以直接对 state 赋值。
挂载阶段
对应的生命周期为:
* 1.componentWillMount 发生在 render 函数之前,还没有挂载 Dom* 2.render* 3.componentDidMount 发生在 render 函数之后,已经挂载 Dom
更新阶段
更新阶段分为由 state 更新引起和 props 更新引起
* props *1. componentWillReceiveProps(nextProps,nextState) 这个生命周期主要为我们提供对 props 发生改变的监听,如果你需要在 props 发生改变后,相应改变组件的一些state。在这个方法中改变state不会二次渲染,而是直接合并state。 *2. shouldComponentUpdate(nextProps,nextState) 这个生命周期需要返回一个 Boolean 类型的值,判断是否需要更新渲染组件,优化 react 应用的主要手段之一,当返回 false 就不会再向下执行生命周期了,在这个阶段不可以setState(),会导致循环调用。 *3. componentWillUpdate(nextProps,nextState) 这个生命周期主要是给我们一个时机能够处理一些在 Dom 发生更新之前的事情,如获得 Dom 更新前某些元素的坐标、大小等,在这个阶段不可以setState(),会导致循环调用。 **一直到这里 this.props 和 this.state都还未发生更新** *4. render 执行 render 函数。 *5. componentDidUpdate(prevProps, prevState) 在此时已经完成渲染,Dom 已经发生变化,State 已经发生更新,prevProps、prevState 均为上一个状态的值。*state(具体同上) *1. shouldComponentUpdate *2. componentWillUpdate *3. render *4. componentDidUpdate
卸载阶段
对应的生命周期为
* componentWillUnmountcomponentWillUnmount 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除timer,取消网络请求或清除在 componentDidMount 中创建的订阅等。componentWillUnmount 中不应调用 setState,因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。
1.什么是React?
React 是 Facebook 在 2011 年开发的前端 JavaScript 库。
它遵循基于组件的方法,有助于构建可重用的UI组件。
它用于开发复杂和交互式的 Web 和移动 UI。
尽管它仅在 2015 年开源,但有一个很大的支持社区。
2.React有什么特点?
React的主要功能如下:
它使用**虚拟DOM **而不是真正的DOM。
它可以用服务器端渲染。
它遵循单向数据流或数据绑定。
3.React的一些主要优点是:
它提高了应用的性能
可以方便地在客户端和服务器端使用
由于 JSX,代码的可读性很好
React 很容易与 Meteor,Angular 等其他框架集成
使用React,编写UI测试用例变得非常容易
4.React有哪些限制?
React的限制如下:
React 只是一个库,而不是一个完整的框架
它的库非常庞大,需要时间来理解
新手程序员可能很难理解
编码变得复杂,因为它使用内联模板和 JSX
5.你了解 Virtual DOM 吗?解释一下它的工作原理。
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM 工作过程有三个简单的步骤。
1.每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
2.然后计算之前 DOM 表示与新表示的之间的差异。
3.完成计算后,将只用实际更改的内容更新 real DOM。
6.解释 React 中 render() 的目的。
每个React组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如 <form>、<group>、<div> 等。此函数必须保持纯净,即必须每次调用时都返回相同的结果。
7.什么是 Props?
Props 是 React 中属性的简写。它们是只读组件,必须保持纯,即不可变。它们总是在整个应用中从父组件传递到子组件。子组件永远不能将 prop 送回父组件。这有助于维护单向数据流,通常用于呈现动态生成的数据。
8. React中的状态是什么?它是如何使用的?
状态是 React 组件的核心,是数据的来源,必须尽可能简单。基本上状态是确定组件呈现和行为的对象。与props 不同,它们是可变的,并创建动态和交互式组件。可以通过 this.state() 访问它们。
9.如何更新组件的状态?
可以用 this.setState()更新组件的状态。
10.区分有状态和无状态组件。
有状态组件 无状态组件
1. 在内存中存储有关组件状态变化的信息 1. 计算组件的内部的状态
2. 有权改变状态 2. 无权改变状态
3. 包含过去、现在和未来可能的状态变化情况 3. 不包含过去,现在和未来可能发生的状态变化情况
4. 接受无状态组件状态变化要求的通知,然后将 props 发送给他们。 4.从有状态组件接收 props 并将其视为回调函数。
11.React组件生命周期的阶段是什么?
React组件的生命周期分为四个不同阶段:
初始化:在此阶段,react组件准备设置初始状态和默认道具。
挂载:react组件已准备好挂载在浏览器DOM中。此阶段涵盖componentWillMount和componentDidMount生命周期方法。
更新:在此阶段,组件以两种方式进行更新,即发送新道具和更新状态。此阶段涵盖了shouldComponentUpdate,componentWillUpdate和componentDidUpdate生命周期方法。
卸载:在最后一个阶段,不需要该组件,并且可以从浏览器DOM上卸载该组件。此阶段包括componentWillUnmount生命周期方法。
12.详细解释 React 组件的生命周期方法。
componentWillMount:在渲染之前执行,用于根组件中的应用程序级别配置。
componentDidMount:仅在客户端的第一次渲染之后执行。 这是AJAX请求和DOM或状态更新应该发生的地方。此方法也用于与其他JavaScript框架以及任何延迟执行的函数(如setTimeout或setInterval)进行集成,在这里使用它来更新状态,以便我们可以触发其他生命周期方法。
componentWillReceiveProps:只要在另一个渲染被调用之前更新props就被调用。 当我们更新状态时,从setNewNumber触发它。
shouldComponentUpdate:确定是否将更新组件。默认情况下,它返回true。如果您确定组件在状态或道具更新后不需要渲染,则可以返回false值。这是提高性能的好地方,因为如果组件收到新的道具,它可以防止重新渲染。
componentWillUpdate:在由shouldComponentUpdate确认返回正值的优点和状态更改时,在重新渲染组件之前执行。
componentDidUpdate:通常用于更新DOM以响应属性或状态更改。
componentWillUnmount:它将用于取消任何传出的网络请求,或删除与该组件关联的所有事件侦听器。
13. React 的 refs 有什么了解?
Refs 是 React 中引用的简写。它是一个有助于存储对特定的 React 元素或组件的引用的属性,它将由组件渲染配置函数返回。用于对 render() 返回的特定元素或组件的引用。当需要进行 DOM 测量或向组件添加方法时,它们会派上用场。
使用场景
需要管理焦点、选择文本或媒体播放时
触发式动画
与第三方 DOM 库集成
14.如何模块化 React 中的代码?
可以使用 export 和 import 属性来模块化代码。它们有助于在不同的文件中单独编写组件。
15.你对受控组件和非受控组件了解多少?
受控组件 非受控组件
1. 没有维持自己的状态 1. 保持着自己的状态
2.数据由父组件控制 2.数据由 DOM 控制
3. 通过 props 获取当前值,然后通过回调通知更改3. Refs 用于获取其当前值
16.React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代
这三者是目前react解决代码复用的主要方式:
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
render props是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,更具体的说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。
通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个使用场景更加简单。这两种模式仍有用武之地,(例如,一个虚拟滚动条组件或许会有一个 renderltem 属性,或是一个可见的容器组件或许会有它自己的 DOM 结构)。但在大部分场景下,Hook 足够了,并且能够帮助减少嵌套。
17.什么是高阶组件(HOC)?
高阶组件是重用组件逻辑的高级方法,是一种源于 React 的组件模式。 HOC 是自定义组件,在它之内包含另一个组件。它们可以接受子组件提供的任何动态,但不会修改或复制其输入组件中的任何行为。你可以认为 HOC 是“纯(Pure)”组件。
HOC就是一个函数,且该函数接受一个组件作为参数,并返回一个新组件。
实现高阶组件的两种方式:
属性代理。高阶组件通过包裹的React组件来操作props
反向继承。高阶组件继承于被包裹的React组件
属性代理
a. 什么是属性代理
属性代理组件继承自React.Component,通过传递给被包装的组件props得名
b. 属性代理的用途
更改 props,可以对传递的包裹组件的WrappedComponent的props进行控制
通过 refs 获取组件实例
反向继承
反向继承允许高阶组件通过 this 关键词获取 WrappedComponent,意味着它可以获取到 state,props,组件生命周期(component lifecycle)钩子,以及渲染方法(render),所以我们主要用它来做渲染劫持,比如在渲染方法中读取或更改 React Elements tree,或者有条件的渲染等。
使用场景
操纵 props
通过 ref 访问组件实例
组件状态提升
用其他元素包装组件
你的项目中怎么使用的高阶组件
a. 项目中经常存在在配置系统中配置开关/全局常量,然后在页面需要请求配置来做控制,如果在每个需要调用全局设置的地方都去请求一下接口,就会有一种不优雅的感觉,这个时候我就想到利用高阶组件抽象一下。
b. 项目开发过程中,经常也会遇到需要对当前页面的一些事件的默认执行做阻止,我们也可以写一个高阶组件等。
17.hooks和class的区别和优缺点?
class相比函数式组件的优势:
class组件内部可以定义自己的state,用来保存组件自己内部的状态;函数式组件不可以,因为函数每次调用都会产生新的临时变量。
class组件有自己的生命周期,可以在对应的生命周期中完成自己的逻辑;比如在componentDidMount中发送网络请求,并且该生命周期函数只会执行一次;函数式组件在学习hooks之前,如果在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求。
class组件可以在状态改变时只重新执行render函数以及我们希望重新调用的生命周期函数componentDidUpdate等;函数式组件在重新渲染时,整个函数都会被执行,似乎没有什么地方可以只让它们调用一次;
在Hook之前,以上情况通常都用class。
Class组件存在的问题:
复杂组件变得难以理解:
最初编写class组件时,往往逻辑比较简单,但是业务增多,class组件就会越来越复杂;
比如componentDidMount中,可能就会有大量逻辑代码,包括网络请求,一些事件的监听(还需要在componentWillUnmount中移除);
而对于这样的class实际上很难拆分,因为这些逻辑往往混在一起,强行拆分反而会造成过度设计,增加代码的复杂度。
难以理解的class:
ES6中class相当于React的一个障碍;
在class中,我们必须搞清楚this的指向到底是谁,所以需要花很多的精力去学习this;
虽然掌握this是必要,但是处理起来依然很麻烦
组件复用状态很难:
在之前为了一些状态的复用,我们需要通过高阶组件或render props;
像redux中connect或者react-route中的withRouter,这些高阶组件设计的目的就是为了状态的复用。
或者类似于Provider,Consumer来共享一些状态,但是多次使用Consumer时,就会有很多嵌套;
Hook优点
1.代码可读性更强
2.组件树层级变浅。
3.this指向性的问题
hooks缺点和坑
1.useState 初始化只初始化一次
2.useEffect 内部不能修改 state
3.useEffect 依赖引用类型会出现死循环
Hook使用场景:
Hook的出现基本可以替代class组件(除了个别场景);
若项目比较旧,并不需要直接将所有代码重构为Hooks,因为它完全向下兼容,可以渐进式地来使用;
Hook只能在函数组件中使用,不能在类组件,或者函数组件之外的地方使用;
18.对React-Fiber的理解,它解决了什么问题?
React Fiber 并不是所谓的纤程(微线程、协程),而是一种基于浏览器的单线程调度算法
Fiberl是一种将 recocilation (递归 diff),拆分成无数个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算。
React V15 在渲染时,会递归比对 VirtualDOM 树,找出需要变动的节点,然后同步更新它们, 一气呵成。这个过程期间, React 会占据浏览器资源,这会导致用户触发的事件得不到响应,并且会导致掉帧,导致用户感觉到卡顿。
为了给用户制造一种应用很快的“假象”,不能让一个任务长期霸占着资源。可以将浏览器的渲染、布局、绘制、资源加载(例如 HTML 解析)、事件响应、脚本执行视作操作系统的“进程”,需要通过某些调度策略合理地分配 CPU 资源,从而提高浏览器的用户响应速率, 同时兼顾任务执行效率。
所以 React 通过Fiber 架构,让这个执行过程变成可被中断。“适时”地让出 CPU 执行权,除了可以让浏览器及时地响应用户的交互,还有其他好处:
分批延时对DOM进行操作,避免一次性操作大量 DOM 节点,可以得到更好的用户体验;
给浏览器一点喘息的机会,它会对代码进行编译优化(JIT)及进行热代码优化,或者对 reflow 进行修正。
**核心思想:**Fiber 也称协程或者纤程。它和线程并不一样,协程本身是没有并发或者并行能力的(需要配合线程),它只是一种控制流程的让出机制。让出 CPU 的执行权,让 CPU 能在这段时间执行其他的操作。渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。
19.Component, Element, Instance 之间有什么区别和联系?
**元素:**一个元素element是一个普通对象(plain object),描述了对于一个DOM节点或者其他组件component,你想让它在屏幕上呈现成什么样子。元素element可以在它的属性props中包含其他元素(译注:用于形成元素树)。创建一个React元素element成本很低。元素element创建之后是不可变的。
**组件:**一个组件component可以通过多种方式声明。可以是带有一个render()方法的类,简单点也可以定义为一个函数。这两种情况下,它都把属性props作为输入,把返回的一棵元素树作为输出。
**实例:**一个实例instance是你在所写的组件类component class中使用关键字this所指向的东西(译注:组件实例)。它用来存储本地状态和响应生命周期事件很有用。
函数式组件(Functional component)根本没有实例instance。类组件(Class component)有实例instance,但是永远也不需要直接创建一个组件的实例,因为React帮我们做了这些。
20.React如何获取组件对应的DOM元素?
可以用ref来获取某个子节点的实例,然后通过当前class组件实例的一些特定属性来直接获取子节点实例。ref有三种实现方法:
字符串格式:字符串格式,这是React16版本之前用得最多的,例如:<p ref="info">span</p>
函数格式:ref对应一个方法,该方法有一个参数,也就是对应的节点实例,例如:<p ref={ele => this.info = ele}></p>
createRef方法:React 16提供的一个API,使用React.createRef()来实现
21.React中可以在render访问refs吗?为什么?
不可以,render 阶段 DOM 还没有生成,无法获取 DOM。DOM 的获取需要在 pre-commit 阶段和 commit 阶段
22. React 中 key 的重要性是什么?
key 用于识别唯一的 Virtual DOM 元素及其驱动 UI 的相应数据。它们通过回收 DOM 中当前所有的元素来帮助 React 优化渲染。这些 key 必须是唯一的数字或字符串,React 只是重新排序元素而不是重新渲染它们。这可以提高应用程序的性能。
23.useEffect和useLayoutEffect区别?
useEffect是render结束后,callback函数执行,但是不会阻断浏览器的渲染,算是某种异步的方式吧。但是class的componentDidMount 和componentDidUpdate是同步的,在render结束后就运行,useEffect在大部分场景下都比class的方式性能更好.
useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.
24.useState为什么返回是一个数组而不是对象
对象中的内容是没有顺序的, 所以对象无法通过顺序对应来进行解构赋值, 所以只能通过键名,
所以useState使用数组进行解构赋值, 可以对变量进行命名, 只需要保持顺序对应好即可
25.什么是flux?
Flux 是一种强制单向数据流的架构模式。它控制派生数据,并使用具有所有数据权限的中心 store 实现多个组件之间的通信。整个应用中的数据更新必须只能在此处进行。 Flux 为应用提供稳定性并减少运行时的错误。
26.什么是Redux?
Redux 是当今最热门的前端开发库之一。它是 JavaScript 程序的可预测状态容器,用于整个应用的状态管理。使用 Redux 开发的应用易于测试,可以在不同环境中运行,并显示一致的行为。
27.Redux遵循的三个原则是什么?
***单一事实来源:***整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。
***状态是只读的:***改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。
***使用纯函数进行更改:***为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。
28.你对“单一事实来源”有什么理解?
Redux 使用 “Store” 将程序的整个状态存储在同一个地方。因此所有组件的状态都存储在 Store 中,并且它们从 Store 本身接收更新。单一状态树可以更容易地跟踪随时间的变化,并调试或检查程序。
29.列出 Redux 的组件。
Redux 由以下组件组成:
Action– 这是一个用来描述发生了什么事情的对象。
Reducer– 这是一个确定状态将如何变化的地方。
Store– 整个程序的状态/对象树保存在Store中。
View– 只显示 Store 提供的数据。
30.Redux与Flux有何不同?
Flux Redux
1. Store 包含状态和更改逻辑 1. Store 和更改逻辑是分开的
2. 有多个 Store 2. 只有一个 Store
3. 所有 Store 都互不影响且是平级的 3. 带有分层 reducer 的单一 Store
4. 有单一调度器 4. 没有调度器的概念
5. React 组件订阅 store 5. 容器组件是有联系的
6. 状态是可变的 6. 状态是不可改变的
31.Redux 有哪些优点?
Redux 的优点如下:
结果的可预测性 - 由于总是存在一个真实来源,即 store ,因此不存在如何将当前状态与动作和应用的其他部分同步的问题。
可维护性 - 代码变得更容易维护,具有可预测的结果和严格的结构。
服务器端渲染 - 你只需将服务器上创建的 store 传到客户端即可。这对初始渲染非常有用,并且可以优化应用性能,从而提供更好的用户体验。
开发人员工具 - 从操作到状态更改,开发人员可以实时跟踪应用中发生的所有事情。
社区和生态系统 - Redux 背后有一个巨大的社区,这使得它更加迷人。一个由才华横溢的人组成的大型社区为库的改进做出了贡献,并开发了各种应用。
易于测试 - Redux 的代码主要是小巧、纯粹和独立的功能。这使代码可测试且独立。
组织 - Redux 准确地说明了代码的组织方式,这使得代码在团队使用时更加一致和简单。
32.什么是React 路由?
React 路由是一个构建在 React 之上的强大的路由库,它有助于向应用程序添加新的屏幕和流。这使 URL 与网页上显示的数据保持同步。它负责维护标准化的结构和行为,并用于开发单页 Web 应用。 React 路由有一个简单的API。
33.为什么React Router v4中使用 switch 关键字 ?
虽然 <div> ** 用于封装 Router 中的多个路由,当你想要仅显示要在多个定义的路线中呈现的单个路线时,可以使用 “switch” 关键字。使用时,<switch>** 标记会按顺序将已定义的 URL 与已定义的路由进行匹配。找到第一个匹配项后,它将渲染指定的路径。从而绕过其它路线。
34.为什么需要 React 中的路由?
Router 用于定义多个路由,当用户定义特定的 URL 时,如果此 URL 与 Router 内定义的任何 “路由” 的路径匹配,则用户将重定向到该特定路由。所以基本上我们需要在自己的应用中添加一个 Router 库,允许创建多个路由,每个路由都会向我们提供一个独特的视图
35.列出 React Router 的优点。
几个优点是:
就像 React 基于组件一样,在 React Router v4 中,API 是 ‘All About Components’。可以将 Router 可视化为单个根组件(<BrowserRouter>),其中我们将特定的子路由(<route>)包起来。
无需手动设置历史值:在 React Router v4 中,我们要做的就是将路由包装在 <BrowserRouter> 组件中。
包是分开的:共有三个包,分别用于 Web、Native 和 Core。这使我们应用更加紧凑。基于类似的编码风格很容易进行切换。
36.state 和 props有什么区别?
rops是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的props来重新渲染子组件,否则子组件的props以及展现形式不会改变。
state的主要作用是用于组件保存、控制以及修改自己的状态,它只能在constructor中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的this.setState来修改,修改state属性会导致组件的重新渲染。
37.React context是什么?
简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。
38.事件处理
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
40.key 一般使用明确的ID之类的。如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。万不得已用index。