React随笔

338 阅读22分钟

react随笔

本文主要记录一些关于react的API使用方法和一些底层的原理,供自己以后查漏补缺,也欢迎同道朋友交流学习。

react简介

React 是一个由 Facebook(现在称为 Meta)开发的开源 JavaScript 库,主要用于构建用户界面,特别是单页应用程序(SPA)的开发。React 不仅限于 Web 开发,通过React Native,开发者还可以使用几乎相同的组件化开发方式来构建原生移动应用程序,实现了跨平台的代码复用。由于其灵活性和高效性,React 已成为现代 Web 开发中最受欢迎的前端库之一。

核心特点

  • 组件化编程:React 将页面和功能分解为可复用的组件,每个组件可以管理自己的状态和渲染逻辑,大大提高了代码的可维护性和可重用性。
  • Virtual DOM:引入虚拟 DOM 的概念,它是一个树形数据结构,用来表示真实 DOM 的抽象。当状态发生改变时,在Render阶段会先计算VDOM的最小更新,然后在Commit阶段生成真实 DOM,减少了浏览器的重排和重绘,提高了性能。
  • 声明式编程:React 使用声明式的方式定义页面的 UI 和状态逻辑,让代码更容易理解。
  • JSX:允许开发者在 JS 中混写 HTML-like 的语法,这种语法糖被称为JSX,可以更直观和简洁的描述组件的结构。
  • 单向数据流:React 应用遵循单向数据流的原则,父组件向子组件传递状态(props)和回调函数,子组件通过调用这些回调来通知父组件的状态变更,这有助于保持数据流的清晰和可预测性。这点有别于 Vue 的双向绑定。

安装

在搭建 react 框架时,我们一般使用官网推荐的 create-react-app,我也写过基于此脚手架react自定义应用封装

如果你选择其他组件库去做都有对应的安装方式:

  • Ant Design Pro:使用@ant-design/pro-cli
  • UmiJS:使用create-umi

React版本变动

React 自发布以来经历了多个版本的更新,每个主要版本的变动都带来了新的特性和改进,同时也对旧有的API进行了调整或废弃。以下是React几个重要版本的主要变动概述:

React 15 (2016年)

  • 引入Fiber架构:在 React 15后期版本中引入了 Fiber, 提供了更灵活的渲染调度和更换的错误恢复机制。
  • 改进了服务器端渲染:提升了SSR(Server-Side Rendering)的性能和稳定性。
  • SVG和MathML的支持增强:更好地支持SVG和MathML元素,使其渲染更加一致和准确。

React 16 (2017年)

  • 全面实施Fiber:Fiber成为了React核心的更新算法,提供了更细粒度的任务调度和更强大的并发模式,使得React应用的性能和响应性有了显著提升。
  • Error Boundaries:引入了错误边界的概念,允许组件捕获其子组件树中的JavaScript错误,并优雅地降级,而不是让整个应用崩溃。
  • Portals:允许将子节点渲染到DOM树的其他位置,为模态框、弹出层等场景提供了更好的解决方案。
  • 支持返回数组的render方法:可以直接从组件的render方法返回多个元素,而不需要额外的包装元素。

React 17 (2020年)

  • 自动批处理更新:默认开启了自动批处理更新,即使开发者没有手动使用 React.startTransitionunstable_batchedUpdates,React也会尝试批处理状态更新,以减少渲染次数。
  • 事件委托改进:改变了事件处理的方式,将事件监听器绑定到 document 上,减少了委托层级,简化了第三方库的继成。
  • 更严格的 JSX 类型检查:增强了对JSX类型的检查,帮助开发者提前发现潜在的类型错误。
  • 无-breaking-change 版本:React 17被设计为一个过渡版本,尽量减少对现有代码的破坏,为未来更大的更新铺路。

React 18 (2022年)

  • 并发模式:进一步深化了Fiber架构的并发特性,通过新的 SuspenseUseTransition API,允许开发者更好地控制组件的加载和更新策略。
  • 自动 hydration:React 18引入了新的渲染模式,包括 Server ComponentsAutomatic Hydration,旨在减少初次加载时间和提高用户体验。
  • 改进的错误处理:增强了错误边界和错误报告的能力,使得调试和问题定位更加容易。
  • StartTransition API:允许开发者标记某些状态更新为低优先级,从而优化UI的响应性和流畅性。

生命周期

经典生命周期

在React 16.3之后的生命周期可以分为三个阶段:

挂载阶段(Mounting):

  • constructor: 组件实例化时调用,初始化 state 和绑定 this
  • getDerivedStateFromProps: (React 16.3新增)在组件实例被创建后续更新时被调用,用于根据 props 来计算 state
  • render: 根据 state 和 props 渲染UI到虚拟 DOM。
  • componentDidMount: 组件已经被渲染到 DOM 后调用,常用于发起网络请求、设置定时器等。

更新阶段(Updating):

  • getDerivedStateFromProps: 同挂载阶段。
  • shouldComponentUpdate: 判断是否需要更新 DOM,返回 true/false。
  • render: 状态或props改变时再次渲染 UI。
  • getSnapshotBeforeUpdate: (React 16.3新增)在 DOM 更新前调用,可以获取一些信息用于在 componentDidUpdate 中使用。
  • componentDidUpdate: 组件更新后立即调用,可以进行 DOM 操作或网络请求。

卸载阶段(Unmounting):

  • componentWillUnmount: 组件将要卸载时调用,清理工作如取消网络请求、清除定时器等。

从React 16.3开始,componentWillMount, componentWillReceiveProps, 和 componentWillUpdate 被标记为不安全的,并最终在React 17中被废弃。React推荐使用getDerivedStateFromProps和useState、useEffect等Hooks来替代。详见下图: 经典生命周期图

Hooks生命周期

对于 React 函数组件,现在的实践更倾向于使用如下的 Hooks 生命周期:

  • useState: 用于组件内部状态管理。
  • useEffect: 用于处理副作用,如数据获取、订阅或者手动修改DOM等,可替代 componentDidMount, componentDidUpdate, 和 componentWillUnmount 的组合。
  • useContext: 用于从上下文中消费值。
  • useRef: 用于持久化一个可变的引用对象,不会引起组件重新渲染。
  • useReducer: 用于有复杂状态逻辑的组件,替代某些 useState 的使用场景。
  • useCallbackuseMemo: 用于优化性能,避免不必要的函数或计算的重新创建。

组件类API

PureComponent

PureComponentComponent 的子类,是基于 shouldComponentUpdate 的一种优化方式。使用 PureComponent 的主要优点在于它自动执行了浅比较来检查 props 和 state 是否有变化,没有变化的时候不会重新渲染,从而提高了性能,减少了不必要的计算和 DOM 操作。

import React, { PureComponent } from 'react';

class MyComponent extends PureComponent {
  render() {
    return (
      <div>
        {this.props.text}
      </div>
    );
  }
}

export default MyComponent;

memo

React.memo 是 React 中用于函数组件的性能优化手段,它是一个高阶函数,用来包装一个函数组件,并利用引用地址比较(浅比较)来决定是否重新渲染该组件。当组件的 props 没有发生变化时(基于浅比较),则跳过重新渲染,从而提高性能。

import React, { memo } from 'react';

const MyComponent = memo((props) => {
  // 组件逻辑...
  return <div>{props.text}</div>;
});

// 自定义比较函数:可以通过传递第二个参数给memo来自定义比较逻辑,这允许你实现深度比较或其他定制化的比较策略。
const MyComponent = memo((props) => {...}, (prevProps, nextProps) => {
  // 自定义比较逻辑
  // 返回true如果 props 没有变化,无需重新渲染
  // 返回false如果 props 有变化,需要重新渲染
  return prevProps.text === nextProps.text;
});

createRef

createRef 是 React 中管理 DOM 元素或组件实例引用的一个现代、灵活的方法,有助于处理表单、动画交互、原生DOM操作等场景。

class MyComponent extends React.Component {
  myInputRef = React.createRef();

  componentDidMount() {
    // 在组件挂载后访问DOM元素
    this.myInputRef?.current?.focus();
  }

  render() {
    return <input type="text" ref={this.myInputRef} />;
  }
}

forwardRef

forwardRef 是 React 中的一个高阶组件(HOC),它允许我们将 React 的 refs 转发到被包裹的组件中,即使这个组件是一个函数组件。这在需要访问子组件的 DOM 节点或者想要从父组件传递一些引用到子组件的场景下非常有用。极大地增强了函数组件的能力,使得它们在处理需要直接操作DOM或传递引用的场景下更加灵活和强大。

import React, { forwardRef } from 'react';

// 第一个参数是React.forwardRef接收的render函数,它接收两个参数:props和ref
const MyForwardedComponent = forwardRef((props, ref) => {
  // 现在你可以在这个函数组件内部使用ref了
  return <input type="text" ref={ref} {...props} />;
});

// 使用forwardRef的组件时,可以像普通组件那样使用ref
class ParentComponent extends React.Component {
  myInputRef = React.createRef();

  focusInput = () => {
    this.myInputRef?.current?.focus();
  };

  render() {
    return (
      <>
        <MyForwardedComponent ref={this.myInputRef} />
        <button onClick={this.focusInput}>Focus Input</button>
      </>
    );
  }
}

createContext

createContext 是 React 中的一个API,用于创建一个“context”对象。Context 提供了一种在组件树中传递数据的方式,而不必显式地通过每一个层级手动传递 props。这使得在不同层级的组件中共享数据变得简单且高效,特别适合管理如主题语言设置认证信息等全局状态。

基本用法:

  • 创建 Context: createContext(defaultValue)
import React from 'react';
// 创建一个context
const MyContext = React.createContext('light');
  • Provider组件: 注入值上下文
class App extends React.Component {
  state = {
    theme: 'light',
  };

  render() {
    return (
      // 通过Provider组件向上下文中注入值
      <MyContext.Provider value={this.state.theme}>
        <ComponentThatNeedsTheContext />
      </MyContext.Provider>
    );
  }
}
  • Consumer组件: 读取上下文的方法
function ComponentThatNeedsTheContext() {
  return (
    <MyContext.Consumer>
      {theme => /* 使用theme值 */}
    </MyContext.Consumer>
  );
}

或者使用 useContext Hook:

import React, { useContext } from 'react';

function ComponentThatNeedsTheContext() {
  const theme = useContext(MyContext);
  // 现在可以使用theme值
  return <div>{theme}</div>;
}

注意事项:

Context 会随着组件树的遍历而传递,无论组件是否使用了这个 Context。因此,应当谨慎使用,避免创建过多的 Context,尤其是嵌套使用时。

createElement

createElement 在我们的平时使用中较少,它用于创建 React 元素,是构成用户界面的基本单位,我们常写的 JSX 就是 createElement 的语法糖,所以还是很有必要了解这个 API 的。

基本语法:

React.createElement(
  type, // 通常是一个字符串(对应HTML标签名)或一个React组件(函数组件或类组件的构造函数)。
  [props], // 一个对象,用于传递给组件的属性。它可以包含事件处理器、样式等。
  [...children] // 代表组件的子元素,可以是一个React元素、字符串或数字,也可以是这些类型的数组。
)

示例:

const element = React.createElement(
  'div',
  { id: 'example', className: 'box' },
  'Hello, world!'
);

这段代码等同于下面的JSX写法:

<div id="example" className="box">
  Hello, world!
</div>

cloneElement

cloneElement 是 React 提供的一个方法,用于克隆并返回一个新的 React 元素,同时可以修改传入元素的 props,甚至可以添加或替换子元素。这个方法常用于在高阶组件中,或者任何需要基于现有元素创建一个具有额外 props 或不同子元素的新元素的场景。

基本语法:

React.cloneElement(
  element, // 要克隆的React元素
  [props], // 一个对象,包含了要添加或覆盖到原始元素props上的新属性
  [...children] // 可选的,用于替换或追加子元素到克隆后的元素中
)

自定义HOC

高阶组件(Higher-Order Components, HOC)是 React 中用于重用组件逻辑的一种高级技术。HOC 本身不是 React API 的一部分,而是一种从函数式编程原则中借来的模式。一个 HOC 是一个接受组件作为参数并返回一个新的增强组件的函数。

function withEnhancement(WrappedComponent) {
  return function EnhancedComponent(props) {
    // 添加额外的props或逻辑
    const newProps = { ...props, enhancedProp: "Enhanced Value" };
    
    // 渲染被包装的组件,并传递新的props
    return <WrappedComponent {...newProps} />;
  };
}

注意事项:

  • 不要修改传入组件的props: 最好是通过组合新的 props 而不是修改原有的 props来保持纯净性。
  • 命名约定: 通常 HOC 函数名以 with 开头,以表明它是一个 HOC。
  • 文档和测试: 编写清晰的文档说明 HOC 的功能和用法,并确保充分测试,以防止引入bug。

Hooks

React Hooks 是React 16.8版本引入的一个新特性,在不编写类的情况下使用 React 的状态和其他生命周期特性。Hooks 使函数组件的功能更加丰富,使得函数组件逻辑更易于理解和重用。

useState

允许在函数组件中添加状态(state)。它返回一个状态变量和一个用来更新这个状态的函数。

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

useEffect

useEffect 是 React Hooks 系统中的一个重要成员,它主要用于执行副作用操作,比如数据获取、订阅或者手动修改 DOM 等。此 Hook 允许你同步副作用与 React 组件的生命周期,替代了类组件中的一些生命周期方法,如 componentDidMountcomponentDidUpdatecomponentWillUnmount

// useEffect 接收两个参数:一个包含副作用操作的函数,和一个依赖项数组
useEffect(() => {
  // 副作用操作:订阅或数据获取等
  document.title = `You clicked ${count} times`;

  // 可选的清理函数,用于在下次effect执行前或组件卸载时清理副作用
  return () => {
    // 清理操作,例如取消网络请求或移除事件监听器
  };
}, [count]); // 依赖项数组,当这些值变化时触发effect重新执行

useContext

useContext 是 React Hooks 系统中的一个 API,它使你能够在组件树中无需通过 props 逐层传递,就能访问到全局状态或其他组件上下文中的值。这对于管理如主题、语言、认证信息等跨多个组件共享的数据尤为有用。

import React, { useContext } from 'react';

function ComponentThatNeedsTheContext() {
  const theme = useContext(MyContext);
  // 现在可以使用theme值
  return <div>{theme}</div>;
}

useRef

useRef 是 React Hooks 系统中的一个API,它用于创建一个可变的引用对象(ref),这个对象的.current属性被初始化为传递的参数(initialValue)。useRef的主要用途是在渲染之间持久化一个可变的值,并且可以用来直接访问 DOM 元素或在函数组件之间保持一些状态。

import React, { useRef } from 'react';

function TextInputWithFocusButton() {
  // 初始化一个ref,用来存放input元素的引用
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // 当按钮被点击时,让input元素获取焦点
    inputEl?.current?.focus();
  };

  return (
    <>
      {/* 将input元素的引用赋给useRef返回的对象 */}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useReducer

useReducer 是React中的一个Hook,它用于管理组件中的状态,特别适用于状态更新逻辑较复杂的场景。

基本用法:

import React, { useReducer } from 'react';

// 定义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 };

function Counter() {
  // 使用useReducer,传入reducer函数和初始状态
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

注意事项:

  • 确保reducer函数是纯函数,即给定相同输入始终产生相同输出,不产生副作用。
  • 选择合适的状态管理方式,对于简单的状态管理,useState可能更直观易用。
  • 利用useCallback来记忆化dispatch函数,避免在每个渲染周期都创建新的函数引用,进而减少不必要的子组件重渲染。

useMemo

useMemo 是React中的一个Hook,用于优化性能,避免在每次渲染时都进行复杂的计算。它让你能够 memoize(记忆化)一个值,这个值是基于某些依赖项计算出来的,只有当这些依赖项改变时,才会重新计算这个值。

基本用法:

import React, { useMemo } from 'react';

function MyComponent({ list }) {
  // 使用useMemo进行性能优化
  const sortedList = useMemo(() => {
    console.log('Sorting list');
    return list.sort((a, b) => a - b);
  }, [list]); // 依赖项数组,当list变化时才重新计算sortedList

  return (
    <div>
      {sortedList.map(item => (
        <div key={item}>{item}</div>
      ))}
    </div>
  );
}

注意事项:

  • 不要过度使用: 虽然useMemo可以帮助优化性能,但是不必要的使用反而可能导致额外的性能开销,特别是在计算简单或频繁变化的值时。
  • 理解其限制: useMemo不会阻止其依赖项内的对象或数组的内部变化触发重渲染。只有当依赖项的引用本身发生变化时,才会触发重计算。
  • 与React.memo区别: React.memo是一个高阶组件,用于记忆化整个组件,防止不必要的渲染,而useMemo是记忆化组件内部的某个值或计算结果。

useCallback

useCallback 是 React中 的另一个性能优化 Hook,它用于记忆化函数。与 useMemo 相似,useCallback 也用于避免在每次渲染时都创建新的函数引用,但它的主要应用场景是当这些函数作为 props 传递给子组件时,帮助子组件避免不必要的重新渲染。

import React, { useCallback, useState } from 'react';

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

  // 使用useCallback记忆化increment函数
  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count, setCount]); // 依赖项数组,当这些值变化时,才会生成新的increment函数

  return <ChildComponent onClick={increment} />;
}

function ChildComponent({ onClick }) {
  // ...
}

注意事项:

  • 与useMemo的区别: useMemo 适用于记忆化计算值或对象,而 useCallback 专门用于记忆化函数。
  • 避免闭包陷阱: 在使用 useCallback 时,需要注意函数内部引用的外部变量也应包含在依赖项数组中,以确保正确的重渲染逻辑。
  • 性能考量: 不必要的使用可能会增加代码复杂度。

自定义hooks

自定义 Hooks 是 React 的一个特性,它允许你将组件中与状态逻辑相关的代码提取到可复用的函数中。自定义 Hooks 以 use 开头命名,遵循一定的规则和模式,可以在任何函数组件中使用,使得状态逻辑和副作用逻辑的复用变得更加容易。

创建自定义Hook:

// useCounter.js
import { useState } from 'react';

export default function useCounter(initialCount = 0) {
  const [count, setCount] = useState(initialCount);
  const increment = () => setCount(count + 1)
  const decrement = () => setCount(count - 1)

  return { count, increment, decrement };
}

使用自定义Hook:

import React from 'react';
import useCounter from './useCounter';

function CounterComponent() {
  const { count, increment, decrement } = useCounter(10);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

export default CounterComponent;

react的底层原理

虚拟 Dom

React 中,组件的渲染结果会被转换成一个轻量级的内存中数据结构 —— 虚拟 DOM树。当组件的状态或属性(props)发生改变时,React 会重新生成新的虚拟DOM树,然后通过高效的算法(如 Diff 算法)比较新旧虚拟 DOM 树的差异,最后仅将变化的部分应用到实际的 DOM 上,而不是重新渲染整个页面。这大大减少了实际 DOM 操作的次数,提升了页面更新的性能。

Fiber

Fiber 的设计目标主要是解决两个关键问题:提高 UI 更新的可预测性和响应性,以及支持异步渲染和中断处理,使得复杂的 UI 更新过程更加平滑和可控。

核心概念:

  1. 可中断的任务执行: Fiber 通过将渲染过程分解成一系列小任务,使得渲染过程可以在任何时候暂停(例如为了响应用户输入),然后从上次暂停的地方继续执行,提高了交互的流畅性。

  2. 优先级调度: Fiber 引入了任务优先级的概念,不同类型的更新(如用户输入、动画、数据获取等)可以根据其紧急程度被赋予不同的优先级。

  3. Reconciliation: Fiber重构了React的diff算法,使其更加灵活和高效。在对比新旧虚拟 DOM 树时,Fiber可以更加细粒度地控制遍历过程,选择性地深入比较或跳过某些部分,这不仅提高了比较速度,也使得中断和恢复成为可能。

  4. 工作循环: 由几个关键步骤组成:render, commit, 和在两者之间的多次可中断的 reconciliation。这个循环确保React可以逐步推进渲染任务,同时保持对新任务(如高优先级更新)的快速响应。

render

Render(渲染)阶段,主要负责计算出React组件树的新状态(也就是新的虚拟 DOM 树)。这个阶段包括:

  • Reconciliation(协调):当组件的状态或属性发生变化时,React 会启动一个更新过程。在这个过程中,React 会使用一种高效的算法(称为 "Diffing" 或 "Reconciliation")来比较当前的虚拟 DOM 树和即将更新后的虚拟DOM树的差异。
  • Virtual DOM 更新:基于差异,React 会创建一个新的虚拟 DOM 树,这个树反映了组件树应有的最新状态。在这个过程中,React会遍历组件树,调用每个组件的 render 方法来获取它们的新虚拟节点表示。

commit

Commit(提交)阶段,是在Render阶段之后,负责将虚拟 DOM 的变化应用到实际的 DOM 上,使用户能看到 UI 的更新。这个阶段主要包括:

  • 布局效应(Layout Effects):在实际更新 DOM 之前,React 会执行在此阶段注册的任何布局效应(通过useLayoutEffect Hook)。这些效应可以在更新 DOM 之前读取或修改 DOM,适用于需要同步操作的情况。
  • DOM 更新:根据 Render 阶段产生的差异,React 会最小化地更新实际的 DOM 元素,只修改那些真正发生变化的部分,这个过程称为 "Commit"。这一步骤确保了高效的 DOM 操作,避免了不必要的重渲染。
  • 副作用(Side Effects):在 DOM 更新完成后,React 会执行组件的生命周期方法或 Hooks 中的副作用(通过useEffect Hook注册)。这些副作用可以包括网络请求、订阅或者 DOM 操作等,它们不会阻塞UI的更新,而是异步执行。
  • 状态更新完成:最后,React会标记这次更新已经完成,此时可能触发新的渲染循环,特别是如果在副作用中调度了新的状态更新。

Diff

React 的 Diff 算法是其虚拟 DOM 机制的核心组成部分,旨在高效地比较新旧两棵虚拟DOM树的差异,并最小化实际DOM的操作,以此提高UI更新的性能。

同层比较:

  1. 节点类型比较: 如果类型不同,说明发生了较大变更,React 会直接替换整个节点及其子树,不再进行深层次的比较。

  2. 键(Key)的作用: 当列表中的元素顺序或数量发生变化时,React 通过元素的唯一键(key)来跟踪每个元素的身份,确保即使位置改变,也能正确地识别出哪些元素是新增的、删除的或是移动的,而非全部重新渲染。

递归比较:

对于类型相同的节点,React 会继续递归比较它们的属性(props)和子元素。如果发现属性有变化,React 会更新对应的DOM属性。对于子元素,也会应用上述的比较策略。

Web平台优化:

React Diff 算法还利用了一些 Web 平台的特性来进一步优化性能,例如:

  • 文本节点的直接更新: 如果只是文本内容变化,React 可以直接设置 innerText 属性,避免创建新的文本节点。
  • 样式属性的合并: 对于样式更改,React 尽量减少 style 属性的更新次数,通过计算差异后一次性设置。

同等节点的复用:

对于同一层级相同类型的节点,如果两个虚拟 DOM 节点具有相同的类型和 key,且它们的子节点没有变化,React就会尝试复用旧的DOM节点,仅更新必要的属性或子节点。

合成事件(Synthetic Events)

合成事件是 React 为了实现跨浏览器兼容性和提供统一的事件处理机制而设计的一套事件系统。为开发者提供了一个抽象层,隐藏了不同浏览器事件处理的差异,使得事件处理变得更加简洁和一致。

原理和特点:

  • 封装和标准化: 在原生DOM事件之上构建,它捕获浏览器的原生事件,并对其进行封装,无需担心兼容性问题。
  • 事件委托: 采用事件委托的模式,将事件监听器绑定到最上层的容器元素上(通常是 document ),然后通过事件冒泡来捕获和分发事件。这种方式可以提高性能、减少了内存消耗和事件处理器的数量。
  • 事件池: 为了进一步优化性能和内存使用,React 使用事件池来复用事件对象,这避免了频繁创建和销毁事件对象的开销。
  • 自动绑定this: 在 React 类组件中,当你在事件处理函数中使用 this 关键字时,React 会自动为你绑定正确的上下文。
  • 阻止默认行为和事件传播: 通过 event.preventDefault() 可以阻止事件的默认行为,而 event.stopPropagation() 可以阻止事件向上冒泡。这些API与原生DOM事件的行为一致。

性能优化

在写React的应用时,对于性能优化,一般从下面几个方面着手:

  1. 减少组件不必要的渲染: 可以使用PureComponentmemoshouldComponentUpdateuseCallbackuseMemo
  2. 优化 state 和 props: 避免直接修改 state,减少 props 传递。
  3. 使用按需加载和懒加载: 使用按需加载动态导入组件,对于大的图片列表可以进行懒加载。
  4. 使用 React.Suspense和React.lazy: React.lazy 用于代码分割,懒加载组件。React.Suspense可以用来等待异步组件或数据加载完成,同时显示加载标识符,避免页面闪烁或白屏。
  5. 虚拟列表: 对于长列表,使用虚拟列表(如react-windowreact-virtualized)来仅渲染可视区域内的列表项,大幅减少DOM节点数量,提高滚动性能。
  6. 合理使用Context: 确保仅在必要时使用Context,并考虑使用 useContextuseReducer 来优化。
  7. 性能监控和分析: 使用开发者工具(如 Chrome DevToolsPerformance tab)来分析和定位性能瓶颈。