首页 > 其他分享 >React — 原理面试题-持续更新

React — 原理面试题-持续更新

时间:2024-03-28 20:13:19浏览次数:34  
标签:面试题 return 函数 DOM 更新 React 事件 组件

1.什么是React事件,什么是原生事件?两者的区别在哪儿?

  • React 事件: React 事件是经过封装和合成的,以保证在不同浏览器上的一致性。在使用 React 中的事件处理时,你会给 JSX 元素添加事件处理函数,比如 onClickonChange 等,然后在事件处理函数中处理相应的逻辑。React 事件的处理方式提供了一定程度的抽象,使得开发者可以更加方便地处理事件和跨浏览器的兼容性
function handleClick() {
  console.log('Button clicked');
}

<button onClick={handleClick}>Click me</button>
  • 原生事件是指由浏览器原生提供的事件,如 clickchangemouseover 等。在传统的 Web 开发中,我们通过 JavaScript 直接操作 DOM 元素并为其绑定原生事件处理程序来实现交互行为。
document.getElementById('myButton').addEventListener('click', function() {
  console.log('Button clicked');
});

区别:

  1. 合成事件系统:React 事件是经过合成的,React 通过事件委派(event delegation)的方式来管理事件,而不是直接将事件绑定到每个 DOM 元素上。这种方式可以提高性能,同时减少内存占用。
  2. 跨浏览器兼容性:React 事件处理封装了底层的浏览器差异,使得开发者无需关心不同浏览器的事件兼容性问题。
  3. 事件命名:React 使用驼峰式命名来定义事件名,如 onClickonChange,而原生事件则使用全小写的方式,如 clickchange
React 中的事件委派是指 React 将事件处理逻辑委托给组件的共同祖先(根组件),而不是直接在每个组件上添加事件监听器。这意味着 React 在整个组件树中只添加了一个事件监听器,而不是每个组件都有自己的事件监听器。
当用户触发事件时,React 会在 DOM 树中找到最近的共同祖先,并在该节点上触发事件处理函数。然后,React 会使用事件冒泡(event bubbling)的机制将事件传播到组件树中的每个组件,并调用相应的事件处理函数。
React 事件委派的优点包括:
性能优化:减少了事件监听器的数量,提高了性能。相比每个组件都添加事件监听器,只有一个共同祖先添加事件监听器的方式更加高效。
简化事件管理:不需要为每个组件都添加事件监听器,减少了事件管理的复杂性。当组件被添加、移除或更新时,不需要手动管理事件监听器。
更少的内存占用:只有一个事件监听器,减少了内存占用。

2.React的diff算法

React 的 Virtual DOM 和 diff 算法是其性能优势的关键所在。在 React 中,当 state 或 props 发生变化时,React 会通过 diff 算法比较新旧 Virtual DOM 树的差异,并只更新实际变化的部分,而不是重新渲染整个页面。

React 的 diff 算法主要包括以下几个步骤:

  1. 生成 Virtual DOM 树:当组件状态发生变化时,React 会重新构建 Virtual DOM 树。

  2. Diffing 算法:React 使用 diff 算法比较新旧 Virtual DOM 树的差异。这一过程包括两个阶段:

    • 深度优先搜索:React 会递归地遍历新旧节点,找出差异。如果节点类型不同,直接替换;如果节点类型相同,继续比较子节点。

    • 对比子节点:React 会对比新旧节点的子节点列表,找出需要更新、删除或新增的节点。

  3. 更新 DOM:根据 diff 的结果,React 会计算出最小的变更,然后批量更新 DOM,以尽量减少对实际 DOM 的操作次数。

React 的 diff 算法的优势在于减少了不必要的 DOM 操作,提高了性能。通过只更新变化的部分,React 能够更高效地处理大型应用程序中的 UI 变化,同时保持页面的响应速度。

需要注意的是,尽管 React 的 diff 算法在大多数情况下表现良好,但也有一些特殊情况可能会导致性能问题,比如列表中的子元素顺序变化较大时。针对特定场景,开发者也可以通过一些手动优化措施来提升性能。

3.React的diff算法跟Vue的diff算法的区别?

React 的 diff 算法是基于 Fiber 架构的 Reconciliation(对账) 算法,采用深度优先的递归比较方式,使用 Key 属性和双端比较来处理 Virtual DOM 树的差异。

Vue 使用双端渲染和 Virtual DOM 的差异比较算法,通过节点索引和差异比较实现快速更新操作。

React 更灵活和精确,Vue 更简洁高效。选择取决于具体需求和偏好

4.React的生命周期?废弃了哪些?可替代的有哪些?生命周期到现在的一个历程?

在 React 16.3 版本之前,React 组件的生命周期方法包括以下几个阶段:

  1. Mounting(挂载阶段):

    • constructor()
    • componentWillMount()(已废弃)
    • render()
    • componentDidMount()
  2. Updating(更新阶段):

    • componentWillReceiveProps()(已废弃)
    • shouldComponentUpdate()
    • componentWillUpdate()(已废弃)
    • render()
    • componentDidUpdate()
  3. Unmounting(卸载阶段):

    • componentWillUnmount()
  4. Error Handling(错误处理阶段):

    • componentDidCatch()

在 React 16.3 版本中,一些生命周期方法被标记为废弃,主要是因为它们可能会导致一些潜在的问题,或者在未来版本中可能会被移除。这些废弃的生命周期方法包括:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

替代方案包括:

  • getDerivedStateFromProps():用于替代 componentWillReceiveProps 和 componentWillUpdate,允许根据 props 的变化来更新组件的 state。
  • getSnapshotBeforeUpdate():用于替代 componentWillUpdate,允许在更新前获取 DOM 的快照信息。

React 16.8(引入了 Hooks)

  • 引入了 useEffect 这一 Hook,用于替代类组件中的生命周期方法,以处理组件的副作用操作。
  • useEffect 可以在函数组件中执行副作用操作,并且可以在组件渲染后进行一些操作,类似于类组件中的生命周期方法。
  • useEffect 也可以返回一个清除函数,用于清理副作用操作,避免内存泄漏和其他问题。

useEffect 主要替代了类组件中的 componentDidMount, componentDidUpdate, 和 componentWillUnmount 等生命周期方法,使得组件的副作用操作更加灵活和易于管理。

5.React常用的hook函数有哪些?如何自定义hook函数?

  • useState:用于在函数组件中添加状态管理能力。
const [count, setCount] = useState(0);
 <button onClick={() => setCount(count + 1)}>{count}</button>
  • useEffect:处理副作用操作,比如数据获取、订阅等。
useEffect(() => {
  // 这里的代码在组件挂载时执行

  return () => {
    // 这里的代码在组件卸载时执行
  };
}, []); // 空数组意味着没有依赖,只在组件挂载和卸载时执行
  • useContext:让你在函数组件中使用 React 的 Context。
在 React 中,Context 是一种用于在组件之间共享数据的方法,而无需通过组件树的逐层传递 props。它可以帮助我们避免在多层嵌套的组件中通过 props 一层层地传递数据,特别是对于应用中许多组件都需要某些相同的数据时很有用。
使用 Context,可以创建一个“全局”数据存储,使得所有子组件都可以直接访问这些数据,而不必通过中间组件来传递。这在某些情况下能够简化组件之间的通信并提高代码的可维护性。
const ThemeContext = React.createContext('light');
// 在根组件中提供共享的数据
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// 在子组件中使用共享的数据
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme }}>I am styled by theme context!</button>;
}
  • useReducer:类似于 Redux 中的 reducer,用于复杂的状态管理。
// 定义 reducer 函数
const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
};

// 初始状态
const initialState = { count: 0 };

// 使用 useReducer
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>
  );
}
  • useRef:获取 DOM 元素的引用或者保存任意可变值。
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
  • useMemo:用于性能优化,避免不必要的计算。
useMemo 的作用
1.性能优化:useMemo 可以缓存计算结果,在依赖项不变的情况下避免重复计算,从而提高性能。
2.避免不必要的渲染:当组件重新渲染时,如果某个值是通过 useMemo 缓存的,且依赖项没有发生变化,那么这个值不会重新计算,避免不必要的渲染。
function ExpensiveCalculation({ value }) {
  // 使用 useMemo 缓存计算结果
  const result = useMemo(() => {
    console.log('Calculating...');
    return value * 2;
  }, [value]); // 依赖项为 value

  return <div>Result: {result}</div>;
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <ExpensiveCalculation value={count} />
    </div>
  );
}
  • useCallback:用于性能优化,避免不必要的回调函数重复创建。
function ClickCounter({ onClick }) {
  return <button onClick={onClick}>Click Me</button>;
}

function App() {
  const [count, setCount] = useState(0);

  // 使用 useCallback 缓存回调函数
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]); // 依赖项为 count

  return (
    <div>
      <p>Count: {count}</p>
      <ClickCounter onClick={handleClick} />
    </div>
  );
}
  • useLayoutEffect:类似于 useEffect,但会在 DOM 变更之后同步触发效果。
useLayoutEffect 是 React 提供的一个 Hook,与 useEffect 类似,用于在组件渲染到 DOM 之后执行副作用。但是,useLayoutEffect 会在所有 DOM 变更之后同步调用副作用函数,以确保 DOM 变更同步更新。
适合使用场景
1.需要在 DOM 更新后立即执行操作,如测量 DOM 元素的尺寸、位置等。
2.需要在布局计算之后立即执行操作,以避免闪烁或布局问题。
function WidthMeasure() {
  const [width, setWidth] = useState(0);
  const divRef = useRef();

  useLayoutEffect(() => {
    setWidth(divRef.current.clientWidth);
  }, []); // 空依赖项,只在组件挂载和更新时执行

  return (
    <div>
      <div ref={divRef}>Measure my width</div>
      <p>Width: {width}px</p>
    </div>
  );
}

6.hook函数的优缺点?

优点:

  1. 代码复用:Hook 函数使得在函数组件中重用状态逻辑变得更容易。

  2. 组合性:Hook 函数之间可以自由组合,使得逻辑组织更加灵活。

  3. 清晰简洁:将相关逻辑分离到不同的函数中,使得代码清晰易懂。

  4. 无需关注 this 指针:避免了类组件中常见的 this 指针问题。

  5. 轻量化:相比类组件,函数组件通常更轻量,性能更好。

缺点:

  1. 学习曲线:需要一定时间来理解和掌握 Hook 函数的概念。

  2. 破坏性改变:改变了传统的 React 编程习惯,可能需要适应和转变思维。

  3. 限制:一些生命周期方法和特性在函数组件中无法直接使用。

  4. 闭包陷阱:需要注意闭包问题,避免意外行为。

7.React组件怎么做事件代理?

事件代理(Event Delegation是一种利用事件冒泡机制来处理事件的技术。在浏览器中,当一个元素上触发了某个事件时(比如点击事件),这个事件会沿着 DOM 树向上传播,直到根节点。利用事件冒泡,我们可以在父元素上监听事件,然后通过判断具体触发事件的子元素来执行相应的逻辑,从而实现事件代理。

在 React 中,事件代理的原理与浏览器中的事件冒泡机制是一致的。当子组件上的事件被触发时,事件会一层层向上传播,直至父组件或更高层级的祖先组件。通过在父组件上设置事件监听,并在事件处理函数中判断触发事件的具体元素,就能实现事件代理。

具体步骤如下:

  1. 在父组件上添加事件监听函数。
  2. 事件触发后,事件会沿着 DOM 树向上传播。
  3. 在事件处理函数中,可以通过 event.target 获取触发事件的具体元素。
  4. 根据 event.target 的信息,执行相应的逻辑。

通过这种方式,可以减少在子组件上编写大量重复的事件处理函数,提高代码的可维护性和性能表现。同时,利用事件代理还可以动态地管理子组件中的事件,更灵活地处理用户交互。

8.React的高阶组件(HOC)是什么?

React 的高阶组件(Higher-Order Component,HOC)是一种用于复用组件逻辑的高级技术。它本质上是一个函数,接受一个组件作为输入,并返回一个新的组件。通过使用高阶组件,可以在不修改现有组件代码的情况下,添加额外的功能、状态或逻辑。

高阶组件的基本特点和用法:

  1. 接受组件作为参数:高阶组件接受一个组件作为参数,通常以组件作为参数的函数形式实现。

  2. 返回新的组件:高阶组件内部会对传入的组件进行包装或者修改,最终返回一个新的组件。

  3. 复用逻辑:通过高阶组件,可以将一些通用的逻辑、状态管理、数据获取等功能提取出来,并应用到多个组件中,实现逻辑的复用。

  4. 可组合:可以通过组合多个高阶组件,实现更复杂的功能扩展和逻辑复用。

  5. 与原始组件解耦:通过高阶组件,可以将与组件逻辑无关的功能(例如数据获取、权限控制等)与原始组件逻辑分离,提高了组件的复用性和可维护性。

使用高阶组件的常见场景包括但不限于:状态管理、数据获取、条件渲染、权限控制、事件处理等

9.React.createClass和 extends Component的区别?

  1. React.createClass:
    • React.createClass 是 React 早期版本提供的一种创建组件的方式。
    • 使用 React.createClass 创建组件时,可以直接定义组件的配置对象,其中包括组件的状态、生命周期方法等。
    • 不需要手动绑定 this,因为在 React.createClass 中,函数会自动绑定到实例上。
    • 不支持 ES6 类的特性,比如无法使用类属性语法(class properties)等新特性。
const MyComponent = React.createClass({
  render: function() {
    return <div>Hello, World!</div>;
  }
});
  1. extends Component:
    • extends Component 是 ES6 类的语法,通过继承 React.Component 类来创建 React 组件。
    • 使用 ES6 类语法创建组件更符合现代 JavaScript 的标准,也更容易理解和维护。
    • 需要手动绑定 this,或者使用箭头函数来避免手动绑定问题。
    • 支持 ES6 类的特性,可以使用更多现代 JavaScript 的语法特性。
class MyComponent extends React.Component {
  render() {
    return <div>Hello, World!</div>;
  }
}

10.React根据什么判断什么时候重新渲染组件?

在 React 中,组件何时重新渲染是由 React 的 Virtual DOM 和 Reconciliation(对账) 来决定的。React 使用 Virtual DOM 来跟踪页面上的真实 DOM 树,并使用协调算法来确定何时以及如何更新组件。

当组件的状态(state)或属性(props)发生变化时,React 会触发组件的重新渲染。具体来说,React 对比前后两次渲染所生成的 Virtual DOM 树,找出两者之间的差异(Diff),然后只更新必要的部分到真实 DOM 中,以尽量提高性能和效率。

以下是一些触发组件重新渲染的情况:

  1. setState() 方法被调用:当调用组件的 setState() 方法时,React 会重新计算组件的 Virtual DOM 树,并与之前的 Virtual DOM 进行对比,从而确定需要更新的部分。

  2. props 改变:当父组件传递给子组件的 props 发生变化时,子组件会重新渲染以反映最新的 props 值。

  3. forceUpdate() 方法被调用:通过调用组件的 forceUpdate() 方法可以强制组件重新渲染,不管组件的状态或属性是否有变化。

  4. 父组件重新渲染:如果一个组件的父组件重新渲染,那么子组件也会相应地进行重新渲染。

值得注意的是,React 会尽量减少不必要的重新渲染,因此在实际开发中,我们可以通过 shouldComponentUpdate 生命周期方法或 PureComponent、React.memo 等方式来优化组件的性能,避免不必要的渲染。

11.React可以在哪个生命周期访问DOM,在哪个时机访问Ref?

12.Prop是什么?state是什么?state和Prop的区别?

13.setState做了什么操作?异步还是同步?

14.React为什么要校验Prop?

15.Redux是什么?描述一下Redux的工作流程?

16.Redux和VueX的区别?

17.React组件中的key如何理解?

18.React组件通讯的方式有哪些?

标签:面试题,return,函数,DOM,更新,React,事件,组件
From: https://www.cnblogs.com/qinlinkun/p/18102518

相关文章

  • 禁用edge、chrome浏览器自动更新
    禁用edge、chrome浏览器自动更新进入C:\ProgramFiles(x86)\Microsoft或C:\ProgramFiles(x86)\Google路径以edge为例:第一步:打开属性第二步:点击编辑第三步:修改权限......
  • 经典的八个PHP高级工程面试题(附答案)
    更多:https://www.shanhubei.com/archives/55139.html1.php如何实现不用自带的cookie函数为客户端下发cookie。对于分布式系统,如何来保存session值。这个题有点绕。考的还是COOKIE和SESSION的基础知识。服务端通过set-cookie命令来通知客户端保存cookie。只要按照......
  • SQL更新执行流程
    SQL的更新执行流程跟查询流程差不多,假设现在有一条更新语句:UPDATETSETc=1WHEREID=1客户端连接到MySQL服务,通过连接器创建连接,权限验证在更新语句时候,会清空该表的所有查询缓存器数据之后就是通过分析器进行词法分析和语法分析,查看更新语句是否存在问题在通过优化......
  • react useState
    useState是React提供的一个Hook,用于在函数式组件中添加状态管理。它使您可以在函数式组件中添加内部状态,而无需将组件转换为类组件。useState返回一个状态值和一个更新该状态值的函数。当状态值更新时,React会重新渲染组件,以便显示更新后的状态。以下是useState的基本......
  • 面试题——为什么vite打包速度比webpack快
    vite采用了不同的开发模式, 相较于webpack的先打包再启动服务器, vite则是直接启动, 在请求模块时再进行实时编译, 在大型项目中, 这种按需动态编译的模式极大地缩短了时间vite充分利用了现代浏览器对ESModules的支持, 将开发环境下的模块文件直接作为浏览器要执......
  • [转帖]Arm更新Neoverse产品路线图:N3/V3内核曝光,能效及AI性能大涨
    https://new.qq.com/rain/a/20240222A0495O00 +关注2月22日,半导体IP大厂Arm宣布推出新一代ArmNeoverse技术。其中包括,通过性能效率更优异的N系列新IP扩展ArmNeoverse计算子系统(CSS)产品路线图。与NeoverseCSSN2相比,NeoverseCSSN3的每瓦性能可提高......
  • React — 通用hooks封装
    1.UseLocalStorage:该Hook用于在本地存储中存储和检索数据。在组件之间共享和保持状态,并且在页面重新加载时保持数据的持久性。import{useState}from'react';constuseLocalStorage=(key,initialValue)=>{const[storedValue,setStoredValue]=useState(()......
  • React— React面试题按照学习顺序持续更新
    1.React的特点采用组件化模式,命名式编码,提高代码复用率;在ReactNative中可以使用react语法进行移动端开发使用虚拟DOM(v-dom)和diff算法,减少DOM和浏览器的交互2.babel在React的作用ES6语法转ES5,如箭头函数、模板字符串、解构赋值等。Babel可以将这些高级语法转换为浏览器能......
  • Ant Design 设置必填属性的一个坑!Objects are not valid as a React child (found: ob
    1、刚开始,我是用第一种方式。通过一个变量,来设置必填属性的提示值 显示是没有问题的。但是点击ModalForm确认按钮时,报错ObjectsarenotvalidasaReactchild(found:objectwithkeys{requiredMsg}).Ifyoumeanttorenderacollectionofchildren,useanarray......
  • 【wpf】ListBoxItemIndexConverter转换器listbox序号自更新
    publicclassListBoxItemIndexConverter:IMultiValueConverter{publicobjectConvert(object[]values,TypetargetType,objectparameter,CultureInfoculture){stringindexPrefix=null;if(parameter!=null&&parameter......