1.React Hooks带来了什么便利?
React Hooks是React16.8版本中引入的新特性,它带来了许多便利。
-
更简单的状态管理
使用useState Hook可以在函数组件中方便地管理状态,避免了使用类组件时需要继承React.Component的繁琐操作。 -
避免使用类组件:函数式组件的书写方式更加简单、直观,避免了类组件中this指针的混乱问题。
-
更少的重复代码:使用useEffect Hook可以方便地实现数据获取、DOM操作等副作用相关的操作,避免了在不同的生命周期函数中重复编写相似的代码。
-
更好的代码复用:自定义Hook可以将一些可复用的逻辑封装起来,方便在不同的组件中复用。
-
更好的逻辑分离:使用useContext、useReducer和自定义Hook等可以帮助将逻辑分离到独立的模块中,提高代码的可维护性和可扩展性。
总之,React Hooks带来了更加简单、直观、高效的编程方式,可以让开发者更加专注于组件的逻辑实现。使得React的函数组件也具备了类组件的一些特性。
2. 列举几个常见的 Hook?
-
useState Hook:用于在函数组件中管理状态,可以通过调用useState Hook来声明一个状态变量和更新函数,例如:
const [count, setCount] = useState(0);
-
useEffect Hook:用于在函数组件中处理副作用,可以传入一个回调函数和一个依赖数组,例如:
useEffect(() => { // 处理副作用 }, [dependency]);
-
useContext Hook:用于在函数组件中访问React Context中的值,例如:
3.1. 在MyContext.js中定义MyContext上下文对象:
import { createContext } from 'react';
const MyContext = createContext();
export default MyContext;
3.2. 在App.js中使用MyContext.Provider包裹Child组件,传递要共享的数据
import MyContext from './MyContext';
import Child from './Child';
function App() {
const data = 'hello world';
return (
<MyContext.Provider value={data}>
<Child />
</MyContext.Provider>
);
}
3.3 在Child.js中使用useContext函数获取到MyContext传递的值:
import MyContext from './MyContext';
function Child() {
const data = useContext(MyContext);
return <h1>{data}</h1>;
}
export default Child;
- useReducer Hook:用于在函数组件中管理复杂状态,可以将一个reducer函数和初始状态传入useReducer Hook,返回一个状态和派发更新的函数,例如:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
- useCallback Hook:用于在函数组件中缓存回调函数,避免因为每次渲染都重新创建回调函数导致子组件重复渲染,例如:
import React, { useState, useCallback } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment Count</button>
</div>
);
}
在这个示例中,我们使用了 useCallback Hook 缓存了 handleClick 函数。handleClick 会在点击按钮时被调用,并将 count 的值加 1。我们将 count 作为依赖数组传入 useCallback 中,确保每次 count 发生变化时,handleClick 函数都会被更新。
使用 useCallback Hook 可以避免在每次渲染时都创建新的函数引用,从而提高性能。这对于传递给子组件的回调函数尤其有用,确保子组件不会不必要地重新渲染。同时,也可以使用 useMemo Hook 缓存计算结果,从而进一步提高性能。
- useMemo Hook:提供的一个 Hook,用于缓存计算结果,避免在每次渲染时都重新计算,从而提高性能。它接收一个计算函数和一个依赖数组作为参数,返回缓存的计算结果。
例如:
import React, { useMemo, useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const expensiveCalculation = useMemo(() => {
console.log('Calculating...');
let result = 0;
for (let i = 0; i < count * 1000000; i++) {
result += i;
}
return result;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Expensive Calculation: {expensiveCalculation}</p>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
</div>
);
}
- useRef: 获取组件的真实节点或存储一些不常更新的数据,这些数据不受组件重新渲染影响。
- 获取真实节点
const ref = useRef(null)
<div ref ={ref} > </div>
// 设置真实节点属性 .current为固定用法
ref.current.style.color = 'red'
- 存储数据
const ref_obj = useRef({
name:'icy',
age:23
})
// 改变存储的数据
ref.obj.current.name = 'icy-godlike'
还有其他常用的Hook函数,如useImperativeHandle、useLayoutEffect等,开发者可以根据具体的需求选择不同的Hook函数。
3. 使用React Hooks有什么优势?
简化组件的复杂度:React Hooks可以帮助组件更加简洁明了,避免类组件中的一些复杂的生命周期函数。
更容易共享逻辑:React Hooks可以将组件中的状态和逻辑提取出来,通过自定义Hook在不同的组件中进行共享。
更容易测试:React Hooks可以将状态和逻辑的处理分离,使得测试变得更容易。
更好的性能:React Hooks可以避免类组件中因为响应式更新造成的额外渲染,从而提高应用的性能。
更快的开发速度:React Hooks能够帮助开发者更快地构建出复杂的UI组件,从而提高开发效率。
4. 简单介绍下React中diff算法?
在 React 中,当组件的状态发生变化时,React 会重新渲染组件。为了提高渲染效率,React 会使用一种叫做 diff 算法(也称为协调算法)来计算出哪些部分需要更新,哪些部分不需要更新。
简单来说,diff 算法就是比较两棵树的差异,然后将差异应用到真实的 DOM 树上,从而实现只更新必要的部分,避免全量更新。
React 中的 diff 算法具体实现如下:
对比两棵树的根节点,如果类型不同,则直接替换整个节点及其子节点,不再进行进一步比较;如果类型相同,则进入下一步。
对比节点的属性,如果发生变化,则更新节点的属性;如果没有变化,则进入下一步。
对比子节点,React 使用一种叫做 key 的机制来判断哪些节点需要更新,哪些节点需要删除,哪些节点需要新增。如果节点的 key 相同,则认为是同一节点,进行进一步比较;如果节点的 key 不同,则直接替换整个节点及其子节点,不再进行进一步比较。
对比完成后,React 会根据计算出的差异,生成一份更新计划(也称为变更记录),然后根据这份更新计划,进行 DOM 操作,将变更应用到真实的 DOM 树上。
通过使用 diff 算法,React 可以避免全量更新,从而提高渲染效率,使得 React 在大规模数据渲染的场景下仍然能够保持流畅的性能。
5. React中,能否直接将 props 的值复制给 state?
可以,但是应该避免这种写法:
constructor(props) {
super(props);
// 不要这样做
this.state = { color: props.color };
}
只有在初始化组件状态时才能这样做。在组件的生命周期中,props 的值是不会自动更新到 state 中的,因此如果需要在组件运行时更新 state,需要使用setState()方法。
6. React Hooks当中的useEffect是如何区分生命周期钩子的
useEffect 钩子函数可以接收两个参数,第一个参数是一个函数,称为 effect 函数,第二个参数是一个数组,称为依赖项数组。
在 React 中,每一个组件都有不同的生命周期钩子,例如 componentDidMount,componentDidUpdate,componentWillUnmount 等。useEffect 钩子函数可以在一个函数中处理这些不同的生命周期钩子。
当使用 useEffect 钩子函数时,React 会自动根据参数来判断当前需要使用哪个生命周期钩子。
当依赖项数组为空时,useEffect 钩子函数的行为类似于 componentDidMount 和 componentWillUnmount 的结合体,即只在组件挂载时执行一次,并在组件卸载时执行清理操作。
当依赖项数组不为空时,useEffect 钩子函数的行为类似于 componentDidUpdate,即在组件挂载时执行一次,之后每次依赖项发生变化时都会执行一次,最后在组件卸载时执行清理操作。
因此,useEffect 钩子函数的行为会根据传入的依赖项数组的变化而变化,从而实现了不同的生命周期钩子的功能。
useEffect(() => {
console.log('mounted'); // 依赖数组为[]时,componentDidMount | 依赖数组不为空,且发生变化时 componentDidUpdate
return () => {
console.log('willUnmount'); // 依赖数组为[]时,componentWillUnmount
}
}, [source]);
7. 为什么不能用数组下标来作为react组件中的key?
在 React 中,key 属性用于标识组件在列表中的顺序,并用于优化组件的重新渲染。key 必须是唯一的,并且在组件列表中始终保持不变。
不能使用数组下标作为 key 属性是因为数组下标与组件的实际内容没有关系。例如,如果你在一个列表中删除第一个元素,React 将重新生成列表中所有元素的 key,因为数组下标已经改变,导致 React 需要重新渲染所有组件。
此外,使用数组下标作为 key 属性可能会导致错误的渲染结果。例如,如果你有两个组件,它们的 key 属性分别为 0 和 1,并且你想交换它们的顺序,则交换它们在数组中的位置可能会导致 key 属性被错误地匹配到错误的组件上,从而导致渲染错误。
因此,为了避免以上问题,key 属性应该是与组件的实际内容相关的唯一标识符,例如组件在数据库或服务端的 ID。
8. React Fiber是什么?
React Fiber 是 React 16 中引入的新的协调引擎。React Fiber 是一种新的、可中断的调度算法,可以更好地支持渐进式渲染、处理大型组件树以及优化代码的可维护性。
React Fiber 的主要目标是提高 React 应用程序的性能和交互性。具体来说,React Fiber 可以:
实现异步渲染,将渲染任务分割成多个小任务,并调度它们的执行顺序,以提高应用程序的响应性和流畅性。
支持可中断的渲染,允许 React 在执行长时间的计算或等待异步数据时中断渲染,并在后台执行其他任务,以避免应用程序的停顿或卡顿。
支持渐进式渲染,允许 React 逐步地将组件树渲染到屏幕上,从而更快地响应用户输入,并提高应用程序的交互性。
改进错误处理和调试功能,使得开发人员可以更轻松地调试和修复错误。
总之,React Fiber 是 React 16 中的一个重要特性,可以显著提高 React 应用程序的性能、交互性和可维护性。
9. 虚拟DOM一定更快吗?
虚拟 DOM 并不一定比直接操作 DOM 更快,因为在生成虚拟 DOM 的过程中也需要进行计算和操作。但是,使用虚拟 DOM 有以下几个优点:
减少不必要的操作:由于 React 使用了虚拟 DOM,可以将多次 DOM 操作合并为一次,从而减少了不必要的 DOM 操作,提高了性能。
避免重复渲染:React 会比较新旧虚拟 DOM 树的差异,只更新差异部分对应的真实 DOM,避免了重复渲染整个组件,提高了性能。
跨平台支持:虚拟 DOM 是 React 在 Web、Native 等多个平台都可以使用的重要原因之一。
当然,在某些场景下,直接操作 DOM 也可能比使用虚拟 DOM 更快,例如非常简单的组件或需要频繁更新的组件。但总的来说,在大多数情况下,使用虚拟 DOM 可以提高 React 应用程序的性能和可维护性。
10. 不同版本的 React 都做过哪些优化?
React 16 引入了 Fiber 架构,以提高应用程序的性能和交互性。
React 16.3 引入了新的 Context API,以更方便地共享数据和状态。
React 16.6 引入了 memo 和 lazy 函数,以更有效地处理组件性能和代码分离。
React 16.8 引入了 Hooks,以更方便地处理组件之间的状态和逻辑。
React 17 引入了新的事件系统以及一些其他的优化,例如对异常的处理方式等等。
除此之外,React 还对一些其他的细节进行了优化,例如对组件生命周期方法的改进、对 Virtual DOM 的优化、对 React Native 的优化等等。
React渲染页面的两个阶段:
调度阶段(reconciliation):在这个阶段 React 会更新数据生成新的 Virtual DOM,然后通过Diff算法,快速找出需要更新的元素,放到更新队列中去,得到新的更新队列。
渲染阶段(commit):这个阶段 React 会遍历更新队列,将其所有的变更一次性更新到DOM上。
React 15 架构
React15架构可以分为两层:
Reconciler(协调器)—— 负责找出变化的组件;
Renderer(渲染器)—— 负责将变化的组件渲染到页面上;
在React15及以前,Reconciler采用递归的方式创建虚拟DOM,递归过程是不能中断的。如果组件树的层级很深,递归会占用线程很多时间,递归更新时间超过了16ms,用户交互就会卡顿。
为了解决这个问题,React16将递归的无法中断的更新重构为异步的可中断更新,由于曾经用于递归的虚拟DOM数据结构已经无法满足需要。于是,全新的Fiber架构应运而生。
React 16 架构
为了解决同步更新长时间占用线程导致页面卡顿的问题,也为了探索运行时优化的更多可能,React开始重构并一直持续至今。重构的目标是实现Concurrent Mode(并发模式)。
从v15到v16,React团队花了两年时间将源码架构中的Stack Reconciler重构为Fiber Reconciler。
React16架构可以分为三层:
Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler;
Reconciler(协调器)—— 负责找出变化的组件:更新工作从递归变成了可以中断的循环过程。Reconciler内部采用了Fiber的架构;
Renderer(渲染器)—— 负责将变化的组件渲染到页面上。
React 17 优化
React16的expirationTimes模型只能区分是否>=expirationTimes决定节点是否更新。React17的lanes模型可以选定一个更新区间,并且动态的向区间中增减优先级,可以处理更细粒度的更新。
Lane用二进制位表示任务的优先级,方便优先级的计算(位运算),不同优先级占用不同位置的“赛道”,而且存在批的概念,优先级越低,“赛道”越多。高优先级打断低优先级,新建的任务需要赋予什么优先级等问题都是Lane所要解决的问题。
Concurrent Mode的目的是实现一套可中断/恢复的更新机制。其由两部分组成:文章来源:https://www.toymoban.com/news/detail-647812.html
一套协程架构:Fiber Reconciler
基于协程架构的启发式更新算法:控制协程架构工作方式的算法文章来源地址https://www.toymoban.com/news/detail-647812.html
到了这里,关于【React】精选10题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!