react相关

3次阅读
没有评论

React 的核心特性

  • 组件化:将 UI 拆分为独立可复用的组件
  • 虚拟 DOM:提高渲染性能
  • 单向数据流:数据流动更可预测
  • JSX:声明式 UI 编写方式

React 组件的生命周期

  • 挂载阶段:constructor → getDerivedStateFromProps → render → componentDidMount
  • 更新阶段:getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate
  • 卸载阶段:componentWillUnmount

在函数组件中使用 useEffect 替代 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途

控制 useEffect 的执行时机:

  • 空数组[]:只在组件挂载和卸载时执行
  • 有依赖项:依赖项变化时执行
  • 无依赖项:每次渲染后都执行

为什么不能在循环、条件或嵌套函数中调用 Hook

React 依赖 Hook 的调用顺序来正确关联状态,在条件或循环中使用会破坏调用顺序的一致性

什么是高阶组件

就是一个函数组件接收一个组件作为参数,并且返回值是一个组件。不修改原组件,而是组合生成新组件,是纯函数,没有副作用,通过 props 传递数据,与渲染逻辑解耦。常用的场景是

  • 代码复用与逻辑抽象,多个组件需要共享相同逻辑时
  • 条件渲染与权限控制,权限控制 HOC
  • 数据获取与状态注入,数据获取 HOC
  • 性能优化,避免不必要的重新渲染,只在特定 props 变化时渲染
  • 表单处理增强,为表单组件添加统一处理逻辑

受控组件与非受控组件

  • 受控组件:数据由 React 组件的  state 完全控制 ,输入值的变化必须通过 React 的状态更新来驱动。 符合 React 单向数据流,易于调试和追踪状态变化,支持即时验证、禁用提交按钮等动态交互。
  • 非受控组件:数据由  DOM 自身管理 ,而不是 React state,通过  ref  直接访问 DOM 元素的值(类似原生 JavaScript) 更接近原生 HTML,代码更简洁。避免频繁更新 state 带来的性能开销(如大表单输入)

React 官方推荐优先使用受控组件,但在特定场景下非受控组件更合适。

SPA 应用都会提供一个 hash 路由好处是什么?

它的核心好处可以概括为:简单、兼容性好,且无需服务器端配合。

  • 不会触发页面刷新和服务器请求
  • 极高的兼容性
  • 无需服务器端配置

React Hook 的闭包陷阱的理解有哪些解决方案?

  函数组件的每次渲染都是一个独立的“快照”,它拥有自己独立的 Props、State 和事件处理函数。这些函数(包括  useEffectuseCallback、事件处理函数等)通过闭包“捕获”了定义它们时那个快照中的状态值。

当某个 Effect 或回调函数依赖于某个状态,但又没有在其依赖数组中正确声明时,这个函数内部引用的状态就永远是它被创建时的那个旧值,而不是最新的值。这就形成了“闭包陷阱”或“过时闭包”。

import {useState, useEffect} from 'react';

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

  useEffect(() => {
    // 设置一个定时器,意图每秒 count + 1
    const timer = setInterval(() => {console.log(`Current count: ${count}`); // 这里永远输出 0
      setCount(count + 1); // 这里等价于 setCount(0 + 1)
    }, 1000);

    return () => clearInterval(timer);
  }, []); // 🔴 依赖数组为空,Effect 只在挂载时执行一次

  return <div>Count: {count}</div>; // 界面上会一直显示 1
}
// 解决办法:// ✅ 将 count 添加到依赖项

React 中,怎么给 children 添加额外的属性?

将父组件封装,在父组件内部渲染子组件时自定义添加属性

import React from 'react';

function ParentComponent({children}) {
  // 使用 React.cloneElement 克隆子元素并添加新的属性
  const childWithExtraProp = React.cloneElement(children, {customProp: 'Hello from Parent'});
  
  return <div>{childWithExtraProp}</div>;
}

// 使用 ParentComponent
function App() {
  return (<ParentComponent>
      <ChildComponent />
    </ParentComponent>
  );
}

function ChildComponent({customProp}) {return <div>Received: {customProp}</div>; // 输出: Received: Hello from Parent
}

React 中为什么不直接使用 requestldleCallback?

  • 浏览器兼容性问题
  • 时间控制精度不足
    • requestIdleCallback 的执行时机由浏览器决定,React 需要更精确的控制:
    • 无法保证在关键更新时及时执行
    • 空闲时间可能很短,不适合复杂的渲染任务
    • 无法预测下一次空闲时间
  • 任务调度的复杂性
    • 任务优先级(Immediate, UserBlocking, Normal, Low, Idle)
    • 任务中断和恢复
    • 任务超时处理
    • 多个任务之间的协调
  • 更好的控制权和一致性:自定义调度器让 React 能够
    • 在所有浏览器中保持一致的行为
    • 实现时间切片(Time Slicing)
    • 支持并发模式(Concurrent Mode)
    • 提供更细粒度的优先级控制
  • 性能优化
    • 批量更新,减少不必要的渲染
    • 在合适的时机执行任务,避免阻塞用户交互
    • 支持任务的暂停和恢复

说说 Reactcommit 阶段的执行过程

在 Commit 阶段之前是 Render 阶段 (Reconciliation/ 协调阶段),它通过 beginWork 和 completeWork 构建出了一棵完整的 Fiber 树 和一个  副作用链表(effectList)。Commit 阶段的任务就是将这些 ” 副作用 ” 实际应用到 DOM 上。

  • 修改 DOM 前
  • 修改 DOM:实际更新 DOM
  • 修改 DOM 后(同步)
    • useLayoutEffect 的销毁函数(在 Mutation 阶段,对于删除的组件)和执行函数(在 Layout 阶段)。
    • componentDidMount / componentDidUpdate
    • ref 更新
  • 浏览器绘制
  • 浏览器绘制后(异步)useEffect 的销毁函数和执行函数

说说 React render 阶段的执行过程

是 React 渲染流程中 可中断的、异步的 核心阶段。主要任务是:通过 Diff 算法找出需要更新的组件,构建一棵新的 Fiber 树,并收集副作用(effects)

  • 副作用链表(effectList)的构建: 在 completeWork 过程中,React 会构建一个副作用链表
  • Concurrent Mode 下的可中断渲染: 在并发模式下,Render 阶段是可中断的, 执行优先级高的
  • 纯计算:不执行任何实际的 DOM 操作
  • 深度优先遍历:采用 ” 递 ” 和 ” 归 ” 的遍历方式
  • 副作用收集:构建 effectList 供 Commit 阶段使用
  • 双缓存机制:同时维护 current 和 workInProgress 两棵树

React 中,怎么实现父组件调用子组件中的方法?

  • 使用 ref + useImperativeHandle(推荐)
import React, {forwardRef, useImperativeHandle} from 'react';

const ChildComponent = forwardRef((props, ref) => {const [count, setCount] = React.useState(0);

  // 暴露给父组件的方法
  useImperativeHandle(ref, () => ({getCount: () => {return count;}
  }));

  return <div>子组件计数: {count}</div>;
});
export default ChildComponent;

import React, {useRef} from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {const childRef = useRef();

  const handleGetCount = () => {const count = childRef.current?.getCount();
    console.log('当前计数:', count);
  };

  return (<div>
      <ChildComponent ref={childRef} />
      <button onClick={handleGetCount}>获取计数 </button>
    </div>
  );
};

export default ParentComponent;
  • 使用 ref + 类组件
import React from 'react';

class ChildComponent extends React.Component {constructor(props) {super(props);
    this.state = {count: 0};
  }

  getCount = () => {return this.state.count;};

  render() {return <div>计数: {this.state.count}</div>;
  }
}
export default ChildComponent;

import React, {useRef} from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {const childRef = useRef();

  const handleIncrement = () => {childRef.current?.increment();
  };

  return (<div>
      <ChildComponent ref={childRef} />
      <button onClick={handleIncrement}>调用子组件方法 </button>
    </div>
  );
};

export default ParentComponent;

[更多学习参考](https://blog.huixiangwuyou.com/react/#/react/)

正文完
 0
评论(没有评论)
验证码