阅读视图

发现新文章,点击刷新页面。

React 的 setState 批量更新机制详解

React 的 setState 批量更新是 React 优化性能的重要机制,它通过减少不必要的渲染次数来提高应用性能。下面我将详细解释这一过程。

1. 批量更新的基本概念

批量更新(Batching)是指 React 将多个 setState 调用合并为单个更新,从而减少组件重新渲染的次数。

示例代码:

class MyComponent extends React.Component {
  state = { count: 0 };
  
  handleClick = () => {
    this.setState({ count: this.state.count + 1 }); // 不会立即更新
    this.setState({ count: this.state.count + 1 }); // 不会立即更新
    // React 会将这两个 setState 合并
  };
  
  render() {
    return <button onClick={this.handleClick}>Count: {this.state.count}</button>;
  }
}

2. 批量更新的实现原理

2.1 更新队列机制

React 维护一个待处理的 state 更新队列,而不是立即应用每个 setState

graph TD
    A[setState调用] --> B[将更新加入队列]
    B --> C[React事件循环]
    C --> D[批量处理队列中的所有更新]
    D --> E[合并state更新]
    E --> F[执行单一重新渲染]

2.2 具体过程

  1. 更新入队:每次调用 setState,更新会被加入一个待处理队列
  2. 批量处理:在事件处理函数执行结束时,React 会批量处理所有队列中的更新
  3. 合并更新:对于同一 state 键的多个更新,React 会进行浅合并
  4. 触发渲染:最终只进行一次重新渲染

3. 批量更新的触发时机

3.1 自动批处理场景

  • React 事件处理函数(如 onClick)
  • 生命周期方法
  • React 能控制的入口点

3.2 不会自动批处理的情况

  • 异步代码:setTimeout、Promise、原生事件处理等
  • React 18 之前:只有在 React 事件处理函数中才会批处理
// 不会批处理的例子(React 17及之前)
handleClick = () => {
  setTimeout(() => {
    this.setState({ count: this.state.count + 1 });
    this.setState({ count: this.state.count + 1 });
    // React 17中会触发两次渲染
  }, 0);
};

4. React 18 的自动批处理改进

React 18 引入了全自动批处理,覆盖更多场景:

// 在React 18中,这会批量处理
fetchData().then(() => {
  setState1();
  setState2();
  // 只会触发一次渲染
});

5. 强制同步更新的方法

如果需要立即获取更新后的状态,可以使用回调函数形式或 flushSync(React 18+):

// 回调函数形式
this.setState({ count: this.state.count + 1 }, () => {
  console.log('更新后的值:', this.state.count);
});

// React 18的flushSync
import { flushSync } from 'react-dom';

flushSync(() => {
  this.setState({ count: this.state.count + 1 });
});
// 这里state已经更新

6. 函数式组件的批量更新

函数式组件中 useState 也有类似的批量更新行为:

function MyComponent() {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    setCount(c => c + 1); // 更新1
    setCount(c => c + 1); // 更新2
    // React会批量处理,最终count增加2
  };
  
  return <button onClick={handleClick}>{count}</button>;
}

7. 源码层面的简要分析

React 内部通过 enqueueUpdate 函数将更新加入队列:

// 伪代码简化版
function enqueueUpdate(component, partialState) {
  if (!batchingStrategy.isBatchingUpdates) {
    // 如果不处于批量模式,立即更新
    batchingStrategy.batchedUpdates(enqueueUpdate, component, partialState);
    return;
  }
  // 否则加入队列
  dirtyComponents.push(component);
  component._pendingStateQueue.push(partialState);
}

8. 为什么需要批量更新?

  1. 性能优化:减少不必要的渲染次数
  2. 保证一致性:避免中间状态导致的UI不一致
  3. 提升用户体验:更流畅的界面更新

9. 注意事项

  1. 不要依赖 this.state 获取最新值,因为它可能还未更新
  2. 对于连续依赖前一次状态的更新,使用函数形式:
    this.setState(prevState => ({ count: prevState.count + 1 }));
    
  3. 在React 18之前,异步操作中的多个 setState 不会批量处理

React 的批量更新机制是其高效渲染的核心特性之一,理解这一机制有助于编写更高效的React代码和避免常见陷阱。

在这里插入图片描述

React 开发全面指南:核心 API、方法函数及属性详解

React 作为当前最流行的前端框架之一,凭借其组件化、声明式编程和高效的虚拟 DOM 机制,成为构建复杂用户界面的首选工具。本文将深入解析 React 的核心 API、方法函数及属性,覆盖从基础到高级的各个方面,助你全面掌握 React 开发技巧。


1. React 核心概念

1.1 组件化开发

React 应用由组件构成,分为函数组件和类组件:

  • 函数组件:通过纯函数定义,无状态(Hooks 出现后可通过 useState 管理状态)。
  • 类组件:继承 React.Component,具有生命周期方法和状态管理。
// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

1.2 JSX 语法

JSX 是 JavaScript 的语法扩展,用于描述 UI 结构:

const element = <div className="container">Hello React</div>;
  • 表达式嵌入:使用 {} 包裹 JavaScript 表达式。
  • 属性命名:采用驼峰式(如 className 代替 class)。

1.3 虚拟 DOM

React 通过虚拟 DOM 实现高效更新:

  1. 每次状态变更生成新的虚拟 DOM 树。
  2. 通过 Diff 算法对比新旧树差异。
  3. 仅更新实际 DOM 中变化的部分。

2. 组件生命周期方法(类组件)

2.1 挂载阶段(Mounting)

  • constructor(props):初始化状态和绑定方法。
  • static getDerivedStateFromProps(props, state):根据 props 更新 state。
  • render():返回 JSX,必须为纯函数。
  • componentDidMount():组件挂载后执行,适合发起网络请求。

2.2 更新阶段(Updating)

  • shouldComponentUpdate(nextProps, nextState):决定是否重新渲染。
  • getSnapshotBeforeUpdate(prevProps, prevState):捕获 DOM 更新前的状态。
  • componentDidUpdate(prevProps, prevState, snapshot):更新完成后执行。

2.3 卸载阶段(Unmounting)

  • componentWillUnmount():清理定时器、取消订阅等。

2.4 错误处理

  • static getDerivedStateFromError(error):更新状态以显示错误 UI。
  • componentDidCatch(error, info):记录错误信息。

3. Hooks API 详解

3.1 基础 Hooks

  • useState(initialState):管理组件状态。
    const [count, setCount] = useState(0);
    
  • useEffect(effect, dependencies):处理副作用(数据获取、订阅等)。
    useEffect(() => {
      document.title = `Count: ${count}`;
    }, [count]); // 依赖项变化时重新执行
    
  • useContext(Context):访问 Context 值。
    const theme = useContext(ThemeContext);
    

3.2 高级 Hooks

  • useReducer(reducer, initialArg, init):复杂状态逻辑管理。
    const [state, dispatch] = useReducer(reducer, initialState);
    
  • useCallback(fn, dependencies):缓存回调函数。
  • useMemo(() => value, dependencies):缓存计算结果。
  • useRef(initialValue):访问 DOM 或保存可变值。
    const inputRef = useRef();
    <input ref={inputRef} />
    

3.3 自定义 Hook

封装可复用的逻辑:

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return width;
}

4. Context API 与状态管理

4.1 创建 Context

const ThemeContext = React.createContext('light');

4.2 提供 Context 值

<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>

4.3 消费 Context

  • 类组件:通过 static contextTypeConsumer
  • 函数组件:使用 useContext Hook。

5. Refs 与 DOM 操作

5.1 创建 Refs

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

5.2 访问 Refs

const node = this.myRef.current;

5.3 转发 Refs(Forwarding Refs)

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="fancy">
    {props.children}
  </button>
));

6. 事件处理与合成事件

6.1 事件绑定

<button onClick={handleClick}>Click</button>

6.2 合成事件(SyntheticEvent)

React 封装了跨浏览器的事件对象,支持冒泡机制:

function handleChange(e) {
  console.log(e.target.value); // 输入框的值
}

6.3 事件池(Event Pooling)

合成事件对象会被重用,需通过 e.persist() 保留事件。


7. 高阶组件(HOC)与 Render Props

7.1 高阶组件

接收组件返回新组件:

function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log('Component mounted');
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

7.2 Render Props

通过函数 prop 共享代码:

<Mouse render={mouse => (
  <Cat position={mouse} />
)} />

8. 性能优化 API

8.1 React.memo()

缓存函数组件,避免不必要的渲染:

const MemoComponent = React.memo(MyComponent);

8.2 useMemouseCallback

缓存值和函数:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);

8.3 PureComponent

类组件自动浅比较 props 和 state:

class MyComponent extends React.PureComponent { ... }

9. 错误边界与调试工具

9.1 错误边界组件

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  componentDidCatch(error, info) {
    logErrorToService(error, info);
  }
  render() {
    if (this.state.hasError) {
      return <FallbackUI />;
    }
    return this.props.children;
  }
}

9.2 React Developer Tools

Chrome/Firefox 扩展,用于审查组件树、状态和性能。


10. React Router 核心 API

10.1 路由配置

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/users" element={<Users />} />
  </Routes>
</BrowserRouter>

10.2 导航

<Link to="/about">About</Link>
const navigate = useNavigate();
navigate('/profile');

11. 服务端渲染与 ReactDOMServer

11.1 renderToString()

将组件渲染为 HTML 字符串:

ReactDOMServer.renderToString(<App />);

11.2 renderToStaticMarkup()

生成静态 HTML(无额外 DOM 属性)。


12. TypeScript 与 React 集成

12.1 组件 Props 类型

interface ButtonProps {
  label: string;
  onClick: () => void;
}
const Button: React.FC<ButtonProps> = ({ label, onClick }) => (
  <button onClick={onClick}>{label}</button>
);

13. 常见问题与最佳实践

13.1 避免不必要的渲染

  • 使用 React.memoPureComponent
  • 合理设置依赖项数组(useEffect, useMemo)。

13.2 状态管理选择

  • 简单应用使用 Context + useReducer
  • 复杂场景采用 Redux 或 MobX。

13.3 代码分割

const LazyComponent = React.lazy(() => import('./Component'));
<Suspense fallback={<Spinner />}>
  <LazyComponent />
</Suspense>

结语

React 的 API 生态庞大而灵活,本文涵盖了从基础到高级的核心知识点。掌握这些内容后,你将能够高效构建可维护的 React 应用。持续关注官方文档和社区动态,保持技术敏感度,是提升开发能力的关键。

React 性能优化十大总结

1.memo memo允许组件在 props 没有改变的情况下跳过重新渲染默认通过Object.is比较每个prop,可通过第二个参数,传入自定义函数来控制对比过程

const Chart = memo(function Chart({ dataPoints }) {
  // ...
}, arePropsEqual);

function arePropsEqual(oldProps, newProps) {
  return (
    oldProps.dataPoints.length === newProps.dataPoints.length &&
    oldProps.dataPoints.every((oldPoint, index) => {
      const newPoint = newProps.dataPoints[index];
      return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
    })
  );
}

2.useMemo 在每次重新渲染的时候能够缓存计算的结果

import { useState, useMemo } from "react";

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

  const memoizedValue = useMemo(() => {
    //创建1000位数组
    const list = new Array(1000).fill(null).map((_, i) => i);

    //对数组求和
    const total = list.reduce((res, cur) => (res += cur), 0);

    //返回计算的结果
    return count + total;

    //添加依赖项,只有count改变时,才会重新计算
  }, [count]);

  return (
    <div>
      {memoizedValue}
      <button onClick={() => setCount((prev) => prev + 1)}>按钮</button>
    </div>
  );
}

export default App;

3.useMemo 缓存函数的引用地址,仅在依赖项改变时才会更新

import { useState, memo } from 'react';

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

  const handleClick = () => {
    setCount((prev) => prev + 1);
  };

  return (
    <div>
      {count}
      <MyButton handleClick={handleClick} />
    </div>
  );
};

const MyButton = memo(function MyButton({ handleClick }: { handleClick: () => void }) {
  console.log('子组件渲染');
  return <button onClick={handleClick}>按钮</button>;
});

export default App;

点击按钮,可以发现即使子组件使用memo包裹了,但还是更新了,控制台打印出“子组件渲染”。这是因为父组件App每次更新时,函数handleClick每次都返回了新的引用地址,因此对于子组件来说每次传入的都是不一样的值,从而触发重渲染。

同样的,减少使用通过内联函数绑定事件。每次父组件更新时,匿名函数都会返回一个新的引用地址,从而触发子组件的重渲染.

<MyButton handleClick={() => setCount((prev) => prev + 1)} />

使用useCallback可以缓存函数的引用地址,将handleClick改为

const handleClick = useCallback(()=>{
  setCount(prev=>prev+1)
},[])

再点击按钮,会发现子组件不会再重新渲染。

4.useTransition 使用useTransition提供的startTransition来标记一个更新作为不紧急的更新。这段任务可以接受延迟或被打断渲染,进而去优先考虑更重要的任务执行页面会先显示list2的内容,之后再显示list1的内容

import { useState, useEffect, useTransition } from "react";

const App = () => {
  const [list1, setList1] = useState<null[]>([]);
  const [list2, setList2] = useState<null[]>([]);
  const [isPending, startTransition] = useTransition();
  useEffect(() => {
    startTransition(() => {
       //将状态更新标记为 transition  
      setList1(new Array(10000).fill(null));
    });
  }, []);
  useEffect(()=>{
    setList2(new Array(10000).fill(null));
  },[])
  return (
    <>
      {isPending ? "pending" : "nopending"}
      {list1.map((_, i) => (
        <div key={i}>{i}</div>
      ))}
      -----------------list2
      {list2.map((_, i) => (
        <div key={i}>6666</div>
      ))}
    </>
  );
};

export default App;

5、useDeferredValue

可以让我们延迟渲染不紧急的部分,类似于防抖但没有固定的延迟时间

import { useState, useDeferredValue } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  // ...
}

6、Fragment

当呈现多个元素而不需要额外的容器元素时,使用React.Fragment可以减少DOM节点的数量,从而提高呈现性能

const MyComponent = () => {
  return (
    <React.Fragment>
      <div>Element 1</div>
      <div>Element 2</div>
      <div>Element 3</div>
    </React.Fragment>
  );
};

7、合理使用Context Context 能够在组件树间跨层级数据传递,正因其这一独特机制,Context 可以绕过 React.memo 或 shouldComponentUpdate 设定的比较过程。也就是说,一旦 Context 的 Value 变动,所有使用 useContext 获取该 Context 的组件会全部 forceUpdate。即使该组件使用了memo,且 Context 更新的部分 Value 与其无关

为了使组件仅在 context 与其相关的value发生更改时重新渲染,将组件分为两个部分。在外层组件中从 context 中读取所需内容,并将其作为 props 传递给使用memo优化的子组件。

8、尽量避免使用index作为key

在渲染元素列表时,尽量避免将数组索引作为组件的key。如果列表项有添加、删除及重新排序的操作,使用index作为key,可能会使节点复用率变低,进而影响性能使用数据源的id作为key

const MyComponent = () => {
  const items = [{ id: 1, name: "Item 1" }, { id: 2, name: "Item 2" }, { id: 3, name: "Item 3" }];

  return (
      <ul>
        {items.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    );
};

9、懒加载

通过React.lazy和React.Suspense实施代码分割策略,将React应用细分为更小的模块,确保在具体需求出现时才按需加载相应的部分

定义路由

import { lazy } from 'react';
import { createBrowserRouter } from 'react-router-dom';

const Login = lazy(() => import('../pages/login'));

const routes = [
  {
    path: '/login',
    element: <Login />,
  },
];

//可传第二个参数,配置base路径 { basename: "/app"}
const router = createBrowserRouter(routes);

export default router;

引用路由

import { Suspense } from 'react';
import { RouterProvider } from 'react-router-dom';

import ReactDOM from 'react-dom/client';

import router from './router';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <Suspense fallback={<div>Loading...</div>}>
    <RouterProvider router={router} />
  </Suspense>,
);

10、组件卸载时的清理

在组件卸载时清理全局监听器、定时器等。防止内存泄漏影响性能

import { useState, useEffect, useRef } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  const timer = useRef<NodeJS.Timeout>();

  useEffect(() => {
    // 定义定时器
    timer.current = setInterval(() => {
      setCount((count) => count + 1);
    }, 1000);

    const handleOnResize = () => {
      console.log('Window resized');
    };

    // 定义监听器
    window.addEventListener('resize', handleOnResize);

    // 在组件卸载时清除定时器和监听器
    return () => {
      clearInterval(timer.current);
      window.removeEventListener('resize', handleOnResize);
    };
  }, []);

  return (
    <div>
      <p>{count}</p>
    </div>
  );
}

export default MyComponent;

附:

React 性能优化十大总结

@[toc]

1. 引言

为什么需要 React 性能优化?

React 是一个高效的前端框架,但在复杂应用中,性能问题仍然可能出现。通过性能优化,可以提升应用的响应速度和用户体验。

React 性能优化的基本概念

React 性能优化主要关注减少不必要的渲染、优化 DOM 操作、减少内存占用等方面。


2. React 性能优化的十大方法

1. 使用 React.memo 优化组件渲染

React.memo 是一个高阶组件,用于缓存组件的渲染结果,避免不必要的重新渲染。

const MyComponent = React.memo(function MyComponent(props) {
  // 组件逻辑
});

2. 使用 useMemouseCallback 缓存计算结果和函数

useMemo 用于缓存计算结果,useCallback 用于缓存函数,避免在每次渲染时重新计算或创建。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

3. 使用 React.lazySuspense 实现代码分割

React.lazySuspense 可以实现组件的懒加载,减少初始加载时间。

const LazyComponent = React.lazy(() => import('./LazyComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

4. 使用 shouldComponentUpdatePureComponent 避免不必要的渲染

shouldComponentUpdatePureComponent 可以避免组件在 props 或 state 未变化时重新渲染。

class MyComponent extends React.PureComponent {
  render() {
    // 组件逻辑
  }
}

5. 使用 key 优化列表渲染

为列表项设置唯一的 key,可以帮助 React 识别哪些项发生了变化,减少不必要的 DOM 操作。

const listItems = items.map(item => (
  <li key={item.id}>{item.name}</li>
));

6. 使用 React.Fragment 减少不必要的 DOM 节点

React.Fragment 可以避免在渲染时添加额外的 DOM 节点。

function MyComponent() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
    </React.Fragment>
  );
}

7. 使用 useReducer 替代 useState 管理复杂状态

useReducer 可以更好地管理复杂的状态逻辑,减少状态更新的次数。

const [state, dispatch] = useReducer(reducer, initialState);

8. 使用 React.memouseContext 优化上下文传递

通过 React.memouseContext,可以避免在上下文变化时重新渲染所有子组件。

const MyComponent = React.memo(function MyComponent() {
  const value = useContext(MyContext);
  // 组件逻辑
});

9. 使用 React.memouseRef 优化 DOM 操作

useRef 可以保存 DOM 引用,避免在每次渲染时重新获取 DOM 元素。

const myRef = useRef(null);

useEffect(() => {
  myRef.current.focus();
}, []);

10. 使用 React.memouseEffect 优化副作用

通过 React.memouseEffect,可以避免在每次渲染时执行不必要的副作用。

const MyComponent = React.memo(function MyComponent() {
  useEffect(() => {
    // 副作用逻辑
  }, [dependency]);
  // 组件逻辑
});

3. 实战:在 React 项目中应用性能优化

项目初始化

使用 Create React App 创建一个新的 React 项目:

npx create-react-app my-react-app
cd my-react-app
npm start

使用 React.memo 优化组件渲染

src/components/MyComponent.js 中使用 React.memo 优化组件渲染:

import React from 'react';

const MyComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
});

export default MyComponent;

使用 useMemouseCallback 缓存计算结果和函数

src/components/MyComponent.js 中使用 useMemouseCallback

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

function MyComponent({ a, b }) {
  const memoizedValue = useMemo(() => a + b, [a, b]);
  const memoizedCallback = useCallback(() => {
    console.log(a, b);
  }, [a, b]);

  return (
    <div>
      <p>{memoizedValue}</p>
      <button onClick={memoizedCallback}>Click me</button>
    </div>
  );
}

export default MyComponent;

使用 React.lazySuspense 实现代码分割

src/App.js 中使用 React.lazySuspense

import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./components/LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

export default App;

使用 shouldComponentUpdatePureComponent 避免不必要的渲染

src/components/MyComponent.js 中使用 PureComponent

import React, { PureComponent } from 'react';

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

export default MyComponent;

使用 key 优化列表渲染

src/components/MyList.js 中使用 key 优化列表渲染:

import React from 'react';

function MyList({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

export default MyList;

使用 React.Fragment 减少不必要的 DOM 节点

src/components/MyComponent.js 中使用 React.Fragment

import React from 'react';

function MyComponent() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
    </React.Fragment>
  );
}

export default MyComponent;

使用 useReducer 替代 useState 管理复杂状态

src/components/MyComponent.js 中使用 useReducer

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 MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

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

export default MyComponent;

使用 React.memouseContext 优化上下文传递

src/components/MyComponent.js 中使用 React.memouseContext

import React, { useContext } from 'react';
import MyContext from './MyContext';

const MyComponent = React.memo(function MyComponent() {
  const value = useContext(MyContext);
  return <div>{value}</div>;
});

export default MyComponent;

使用 React.memouseRef 优化 DOM 操作

src/components/MyComponent.js 中使用 React.memouseRef

import React, { useRef, useEffect } from 'react';

const MyComponent = React.memo(function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    myRef.current.focus();
  }, []);

  return <input ref={myRef} />;
});

export default MyComponent;

使用 React.memouseEffect 优化副作用

src/components/MyComponent.js 中使用 React.memouseEffect

import React, { useEffect } from 'react';

const MyComponent = React.memo(function MyComponent({ dependency }) {
  useEffect(() => {
    console.log('Effect triggered');
  }, [dependency]);

  return <div>{dependency}</div>;
});

export default MyComponent;

4. 进阶:React 性能优化的策略

使用 React.memo 优化组件渲染

通过 React.memo 缓存组件的渲染结果,避免不必要的重新渲染。

使用 useMemouseCallback 缓存计算结果和函数

通过 useMemouseCallback 缓存计算结果和函数,避免在每次渲染时重新计算或创建。

使用 React.lazySuspense 实现代码分割

通过 React.lazySuspense 实现组件的懒加载,减少初始加载时间。

使用 shouldComponentUpdatePureComponent 避免不必要的渲染

通过 shouldComponentUpdatePureComponent 避免组件在 props 或 state 未变化时重新渲染。

使用 key 优化列表渲染

为列表项设置唯一的 key,帮助 React 识别哪些项发生了变化,减少不必要的 DOM 操作。

使用 React.Fragment 减少不必要的 DOM 节点

通过 React.Fragment 避免在渲染时添加额外的 DOM 节点。

使用 useReducer 替代 useState 管理复杂状态

通过 useReducer 更好地管理复杂的状态逻辑,减少状态更新的次数。

使用 React.memouseContext 优化上下文传递

通过 React.memouseContext 避免在上下文变化时重新渲染所有子组件。

使用 React.memouseRef 优化 DOM 操作

通过 useRef 保存 DOM 引用,避免在每次渲染时重新获取 DOM 元素。

使用 React.memouseEffect 优化副作用

通过 React.memouseEffect 避免在每次渲染时执行不必要的副作用。


5. 常见问题与解决方案

性能优化的兼容性问题

  • 问题:某些旧版浏览器可能不支持 React 的某些功能。
  • 解决方案:确保浏览器兼容性,或使用兼容性更好的方法。

性能优化的性能问题

  • 问题:频繁操作可能导致性能问题。
  • 解决方案:优化操作逻辑,减少不必要的操作。

性能优化的使用误区

  • 问题:误用性能优化可能导致逻辑混乱。
  • 解决方案:理解性能优化的原理,避免误用。

6. 总结与展望

React 性能优化的最佳实践

  • 明确使用场景:根据需求选择合适的性能优化方法。
  • 优化性能:合理使用性能优化,避免频繁操作。
  • 确保兼容性:确保性能优化在不同浏览器和环境中兼容。

未来发展方向

  • 更强大的性能优化:支持更复杂的开发场景。
  • 更好的性能优化:提供更高效的实现方式。

通过本文的学习,你应该已经掌握了 React 性能优化的十大方法及实战应用。希望这些内容能帮助你在实际项目中更好地提升应用性能,提升用户体验!

❌