Skip to content

React

什么是 React

React 是一个由 Facebook 开发的用于构建用户界面的 JavaScript 库。它被设计用于构建可复用的组件化 UI,并且以其简洁、高效的方式管理应用的状态而闻名。

以下是 React 的一些核心概念:

  1. 组件:React 应用由多个独立、可复用的组件构成。每个组件都封装了一部分 UI 及其相关的行为逻辑。

  2. 虚拟 DOM:React 使用虚拟 DOM(Virtual DOM)来实现高效的 UI 更新。虚拟 DOM 是 React 对真实 DOM 的抽象表示,它允许 React 在内存中维护一个 DOM 树,并将变化一次性批量更新到实际的 DOM 上,以提高性能。

  3. JSX:JSX 是一种 JavaScript 的语法扩展,它允许在 JavaScript 代码中编写类似于 XML 的结构,用来描述 UI 组件的结构。JSX 使得编写 React 组件更加直观和易读。

  4. 单向数据流:React 采用单向数据流的数据流管理模式,数据流从父组件流向子组件,子组件通过 props 接收数据并向父组件发送事件通知状态变化。

  5. 状态(State):状态是 React 组件中的数据源,用于描述组件的特定状态。状态是可变的,当状态发生变化时,React 会重新渲染相应的组件。

  6. 属性(Props):属性是从父组件传递给子组件的数据,用于定制和配置子组件的行为和样式。

  7. 生命周期方法:React 组件生命周期包括挂载(Mounting)、更新(Updating)、卸载(Unmounting)等阶段,在每个阶段 React 提供了一系列的生命周期方法,允许开发者在组件的不同生命周期阶段执行特定的操作。

  8. 事件处理:React 使用与原生 DOM 事件类似的方式来处理事件,但是采用了合成事件的方式,提供了跨浏览器兼容性,并且性能更高。

虚拟 DOM

虚拟 DOM(Virtual DOM)是 React 对真实 DOM 的一种轻量级表示。它是一个以 JavaScript 对象树的形式存在的虚拟副本,与实际的 DOM 结构一一对应。当应用状态发生变化时,React 会使用虚拟 DOM 来描述变化,然后将这些变化一次性批量更新到实际的 DOM 上,而不是直接操作 DOM。虚拟 DOM 的存在使得 React 能够在内存中高效地进行 DOM 操作,而不必每次状态变化都去直接操作实际的 DOM,从而提高了性能。

作用:

  • 提高性能:通过使用虚拟 DOM,React 能够最小化对实际 DOM 的操作次数,以及减少了浏览器重排(Reflow)和重绘(Repaint)的次数,从而提高了应用的性能。
  • 简化开发:开发者可以通过直接操作虚拟 DOM 来描述 UI 的变化,而不必关心底层的 DOM 操作,使得开发过程更加简洁和易于理解。
  • 跨平台兼容性:虚拟 DOM 的抽象使得 React 应用可以轻松地在不同平台(如 Web、移动端、服务器端)上运行,并且提供了一致的开发体验。

JSX

JSX 是一种 JavaScript 的语法扩展,它允许开发者在 JavaScript 代码中直接编写类似于 XML 的结构,用来描述 React 组件的 UI。

以下是 JSX 与普通 JavaScript 的一些区别:

  1. XML-like 语法:JSX 的语法类似于 XML,允许开发者使用标签和属性来描述 UI 结构,使得代码更加直观和易读。例如:

    jsx
    const element = <h1>Hello, world!</h1>;
  2. 组件的嵌套:在 JSX 中,可以直接嵌套组件,并且以类似 HTML 标签的形式进行书写,提高了组件的可读性和可维护性。例如:

    jsx
    const App = () => (
      <div>
        <Header />
        <MainContent />
        <Footer />
      </div>
    );
  3. 表达式插值:在 JSX 中可以直接使用花括号{}来插入 JavaScript 表达式,用于动态地生成 UI 内容。这使得在 UI 中使用变量、函数调用等变得非常方便。例如:

    jsx
    const name = 'John';
    const element = <h1>Hello, {name}!</h1>;
  4. 类名属性:在 JSX 中,用于指定 HTML 元素的类名属性应该使用className,而不是普通的 JavaScript 中的class,以避免与 JavaScript 的关键字冲突。例如:

    jsx
    const element = <div className="container">Hello, world!</div>;
  5. HTML 属性命名:在 JSX 中,HTML 元素的属性名应该使用驼峰式命名法,与普通的 HTML 保持一致,例如onClickonChange等。而在普通的 JavaScript 中,HTML 属性名通常使用小写字母和短横线进行分隔,例如onclickonchange

组件

在 React 中,组件是构建用户界面的基本单元,它们可以是独立的、可复用的模块,用于封装 UI 及其相关的行为逻辑。组件在 React 应用中负责渲染特定的 UI,并且可以接受输入数据(称为属性 props)并输出界面显示。

React 中的组件可以分为两种主要类型:

  1. 函数组件(Functional Components): 函数组件是一种纯 JavaScript 函数,接收一个 props 对象作为参数,并返回一个 React 元素来描述组件的 UI。函数组件是 React 中定义组件最简单的方式,通常用于呈现静态 UI。示例:

    jsx
    function MyComponent(props) {
      return <div>Hello, {props.name}!</div>;
    }
  2. 类组件(Class Components): 类组件是使用 ES6 的类语法定义的组件,继承自 React.Component 类,它可以包含状态(state)和生命周期方法等特性。类组件通常用于有状态的组件和复杂的交互逻辑。示例:

    jsx
    class MyComponent extends React.Component {
      render() {
        return <div>Hello, {this.props.name}!</div>;
      }
    }

除了这两种主要类型的组件之外,还有一些其他类型的组件,例如:

  • 纯组件(Pure Components):纯组件是一种优化的组件,其渲染结果仅由 props 和 state 决定,不会依赖其他外部状态。React 提供了 PureComponent 类来实现这种优化。
  • 高阶组件(Higher Order Components,HOC):高阶组件是一种用于复用组件逻辑的模式,它是一个函数,接收一个组件作为参数,并返回一个新的增强过的组件。
  • 无状态组件(Stateless Components):无状态组件是一种没有内部状态(state)的函数组件,它只依赖于外部传入的 props 来渲染 UI。

单向数据流

React 的单向数据流是指数据在 React 应用中的流动方向是单向的,从父组件流向子组件,子组件通过 props 接收数据,并通过事件通知父组件状态的变化。这种数据流模式有助于应用的数据流动变得可预测、易于理解和调试。

具体来说,React 的单向数据流包含以下几个方面:

  1. 数据源于父组件:在 React 中,数据通常存储在组件的状态(state)中,或者通过 props 从父组件传递给子组件。父组件负责管理数据的状态,并将数据通过 props 传递给子组件。

  2. 子组件通过 props 接收数据:子组件通过 props 接收来自父组件的数据,这些数据作为子组件的属性传递给子组件。子组件不会直接修改这些 props 中的数据,而是通过回调函数或其他事件机制来通知父组件数据的变化。

  3. 数据的更新通过事件向上传递:当子组件需要修改数据时,它通常会通过事件的方式向上传递数据的变化。父组件接收到事件后,可以更新自己的状态,并将新的数据通过 props 再次传递给子组件,从而实现数据的更新。

  4. 数据的更新是单向的:在 React 中,数据的更新是单向的,即数据只能从父组件流向子组件,而不能反向流动。这种单向数据流的设计使得应用的数据流动变得可控和可预测,降低了组件之间的耦合度,同时也便于调试和维护。

状态和属性

在 React 中,状态(state)和属性(props)是两种用于管理组件数据的概念,它们在组件中具有不同的作用和使用方式。

状态(State)

  • 状态是组件内部管理的数据,用于描述组件的特定状态。
  • 每个 React 组件都可以拥有自己的状态,并且可以通过调用setState()方法来更新状态。
  • 状态通常用于描述组件内部的变化,例如用户交互、组件的生命周期等。
  • 状态是可变的,当状态发生变化时,React 会重新渲染相应的组件以反映新的状态。

属性(Props)

  • 属性是从父组件传递给子组件的数据,用于定制和配置子组件的行为和样式。
  • 属性是只读的,子组件不能直接修改它们,只能通过父组件传递的 props 来获取数据。
  • 属性通常用于在组件之间传递数据,例如传递配置信息、回调函数等。
  • 属性是不可变的,只能由父组件传递给子组件,子组件无法更改自己的属性。

生命周期

React 组件的生命周期主要分为三个阶段:挂载(Mounting)、更新(Updating)、卸载(Unmounting)

1. 挂载阶段(组件创建 & 渲染)

执行顺序:

  1. constructor(初始化 state 和绑定方法)
  2. static getDerivedStateFromProps(根据 props 更新 state,少用)
  3. render(渲染 UI)
  4. componentDidMount(组件挂载后执行,如请求数据、订阅事件)

2. 更新阶段(组件状态或 props 变化)

执行顺序:

  1. static getDerivedStateFromProps(再次更新 state)
  2. shouldComponentUpdate(控制是否重新渲染)
  3. render(重新渲染 UI)
  4. getSnapshotBeforeUpdate(捕获 DOM 变化)
  5. componentDidUpdate(更新完成,可执行副作用操作)

3. 卸载阶段(组件销毁)

执行顺序:

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

Hooks 时代的替代方案:

  • useEffect(() => {...}, []) 代替 componentDidMount
  • useEffect(() => {...}, [deps]) 代替 componentDidUpdate
  • useEffect(() => { return () => {...} }, []) 代替 componentWillUnmount

高阶组件

React 中的高阶组件(Higher Order Component,HOC)是一个函数,它接受一个组件作为参数,并返回一个新的组件。这个新组件通过某种方式增强了传入的组件,例如添加新的行为、处理逻辑、提供额外的数据等。

高阶组件的作用类似于其他编程语言中的高阶函数,它可以用来提取重复的逻辑,增强组件的功能,以及促进组件之间的复用。

下面是一个简单的例子来说明高阶组件的概念:

假设我们有一个需要添加鼠标悬停效果的组件:

jsx
import React from 'react';

class HoverComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hovering: false
    };
    this.handleMouseOver = this.handleMouseOver.bind(this);
    this.handleMouseOut = this.handleMouseOut.bind(this);
  }

  handleMouseOver() {
    this.setState({ hovering: true });
  }

  handleMouseOut() {
    this.setState({ hovering: false });
  }

  render() {
    return (
      <div
        onMouseOver={this.handleMouseOver}
        onMouseOut={this.handleMouseOut}
      >
        {this.props.children(this.state.hovering)}
      </div>
    );
  }
}

export default HoverComponent;

上面的组件 HoverComponent 接受一个函数作为 children,该函数接受一个参数 hovering,表示当前是否有鼠标悬停。但是每当我们需要添加鼠标悬停效果时,都要写相似的逻辑,这显然不够优雅。

我们可以使用高阶组件来提取这个重复的逻辑:

jsx
import React from 'react';

const withHover = (Component) => {
  return class WithHover extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        hovering: false
      };
      this.handleMouseOver = this.handleMouseOver.bind(this);
      this.handleMouseOut = this.handleMouseOut.bind(this);
    }

    handleMouseOver() {
      this.setState({ hovering: true });
    }

    handleMouseOut() {
      this.setState({ hovering: false });
    }

    render() {
      return (
        <div
          onMouseOver={this.handleMouseOver}
          onMouseOut={this.handleMouseOut}
        >
          <Component {...this.props} hovering={this.state.hovering} />
        </div>
      );
    }
  };
};

export default withHover;

现在我们可以像这样使用 withHover 高阶组件来增强我们的组件:

jsx
import React from 'react';
import withHover from './withHover';

class MyComponent extends React.Component {
  render() {
    const { hovering } = this.props;
    return (
      <div>
        {hovering ? 'Hovering' : 'Not hovering'}
      </div>
    );
  }
}

const HoverComponent = withHover(MyComponent);

export default HoverComponent;

这样,MyComponent 组件就拥有了鼠标悬停效果,而且我们的逻辑被提取到了 withHover 高阶组件中,使得代码更加清晰和易于维护。

React Hooks

React Hooks 是 React 16.8 版本引入的一种新特性,它们可以让你在函数式组件中使用 React 的特性,例如状态(state)、生命周期方法等,而无需编写类组件。

Hooks 提供了一种在不编写类的情况下使用状态和其他 React 特性的方式,它们可以让你更方便地重用逻辑代码、更容易地理解和测试组件,并且有助于减少组件之间的耦合性。

React 提供了一些常用的 Hooks,包括:

  1. useState:用于在函数式组件中添加状态。它接受一个初始状态,并返回一个包含状态和更新状态的函数的数组。

    jsx
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
      );
    }
  2. useEffect:用于在函数式组件中执行副作用操作,例如数据获取、订阅、DOM 操作等。它在组件渲染完成后执行。

    jsx
    import React, { useState, useEffect } from 'react';
    
    function DataFetcher() {
      const [data, setData] = useState(null);
    
      useEffect(() => {
        fetch('https://api.example.com/data')
          .then(response => response.json())
          .then(data => setData(data));
      }, []);
    
      return <div>{data ? <p>Data: {data}</p> : <p>Loading...</p>}</div>;
    }
  3. useContext:用于在函数式组件中使用 React 上下文(Context)。

    jsx
    import React, { useContext } from 'react';
    import MyContext from './MyContext';
    
    function MyComponent() {
      const value = useContext(MyContext);
    
      return <p>Context value: {value}</p>;
    }
  4. useReducer:类似于 Redux 中的 reducer,用于管理复杂的状态逻辑。

    jsx
    import React, { useReducer } from 'react';
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          return state;
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, { count: 0 });
    
      return (
        <div>
          <p>Count: {state.count}</p>
          <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
        </div>
      );
    }

除了上述常用的 Hooks 外,React 还提供了其他一些 Hooks,如 useCallbackuseMemouseRef 等,它们都有助于更方便地管理组件状态、逻辑和副作用。

条件渲染

在 React 中进行条件渲染可以通过多种方式实现,以下是其中一些常见的方法:

  1. 使用 JavaScript 的条件语句:你可以在 JSX 中使用 JavaScript 的条件语句(如 ifelseelse if)来进行条件渲染。

    jsx
    import React from 'react';
    
    function MyComponent({ isLoggedIn }) {
      if (isLoggedIn) {
        return <p>Welcome, User!</p>;
      } else {
        return <p>Please log in.</p>;
      }
    }
  2. 使用三元表达式:你可以使用 JavaScript 中的三元表达式来进行简单的条件渲染。

    jsx
    import React from 'react';
    
    function MyComponent({ isLoggedIn }) {
      return isLoggedIn ? <p>Welcome, User!</p> : <p>Please log in.</p>;
    }
  3. 使用逻辑 && 运算符:当你只需要根据单个条件来决定是否渲染某个组件时,可以使用 && 运算符。

    jsx
    import React from 'react';
    
    function MyComponent({ isLoggedIn }) {
      return isLoggedIn && <p>Welcome, User!</p>;
    }
  4. 使用条件运算符:在 JSX 中,你也可以使用条件运算符 ? :

    jsx
    import React from 'react';
    
    function MyComponent({ isLoggedIn }) {
      return (
        <div>
          {isLoggedIn ? <p>Welcome, User!</p> : <p>Please log in.</p>}
        </div>
      );
    }
  5. 使用变量或函数:你也可以在组件中定义变量或函数来存储条件,然后根据条件来渲染不同的内容。

    jsx
    import React from 'react';
    
    function MyComponent({ isLoggedIn }) {
      let message;
      if (isLoggedIn) {
        message = <p>Welcome, User!</p>;
      } else {
        message = <p>Please log in.</p>;
      }
    
      return <div>{message}</div>;
    }

列表渲染

在 React 中,列表渲染是指在组件中动态生成并渲染一个包含多个元素的列表。这种列表通常是基于一个数组或类似的数据结构来构建的。

React 提供了多种方法来进行列表渲染,其中最常用的方法包括使用 map() 函数和使用 JSX 中的花括号 {} 来动态生成元素。

以下是使用 map() 函数进行列表渲染的基本示例:

jsx
import React from 'react';

function MyComponent({ items }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

在上面的示例中,items 是一个数组,我们通过调用 map() 函数遍历该数组,并为数组中的每个元素生成一个 <li> 元素。注意,我们给每个 <li> 元素设置了一个 key 属性,以帮助 React 识别列表中的每个元素。通常,key 属性应该是唯一的且稳定的,一般使用元素在列表中的索引来作为 key

另一种常见的列表渲染方法是使用 JSX 中的花括号 {} 直接生成元素,例如:

jsx
import React from 'react';

function MyComponent({ items }) {
  return (
    <ul>
      {items.length > 0 ? (
        items.map((item, index) => (
          <li key={index}>{item}</li>
        ))
      ) : (
        <li>No items</li>
      )}
    </ul>
  );
}

在这个示例中,我们首先检查 items 数组是否为空,如果不为空,则动态生成 <li> 元素;如果数组为空,则渲染一个包含文本内容的单独 <li> 元素。

组件通信

  1. Props(属性)传递: 在 React 中,通过将数据作为 props(属性)传递给子组件,从而实现父组件向子组件传递信息的方式。

    jsx
    // ParentComponent.js
    import React from 'react';
    import ChildComponent from './ChildComponent';
    
    function ParentComponent() {
      const data = 'Hello from ParentComponent';
    
      return (
        <div>
          <ChildComponent data={data} />
        </div>
      );
    }
    
    export default ParentComponent;
    
    // ChildComponent.js
    import React from 'react';
    
    function ChildComponent(props) {
      return <div>{props.data}</div>;
    }
    
    export default ChildComponent;
  2. State(状态)管理: 通过在父组件中定义状态,并将状态及其更新函数作为 props 传递给子组件,从而实现组件之间的通信。

    jsx
    // ParentComponent.js
    import React, { useState } from 'react';
    import ChildComponent from './ChildComponent';
    
    function ParentComponent() {
      const [count, setCount] = useState(0);
    
      const increment = () => {
        setCount(count + 1);
      };
    
      return (
        <div>
          <ChildComponent count={count} increment={increment} />
        </div>
      );
    }
    
    export default ParentComponent;
    
    // ChildComponent.js
    import React from 'react';
    
    function ChildComponent(props) {
      return (
        <div>
          <p>Count: {props.count}</p>
          <button onClick={props.increment}>Increment</button>
        </div>
      );
    }
    
    export default ChildComponent;
  3. Context(上下文): 使用 React 的 Context API 全局管理状态,并允许多个组件在不必通过 props 层层传递的情况下访问共享状态。

    jsx
    // MyContext.js
    import React from 'react';
    
    const MyContext = React.createContext();
    
    export default MyContext;
    
    // ParentComponent.js
    import React, { useState } from 'react';
    import ChildComponent from './ChildComponent';
    import MyContext from './MyContext';
    
    function ParentComponent() {
      const [count, setCount] = useState(0);
    
      const increment = () => {
        setCount(count + 1);
      };
    
      return (
        <MyContext.Provider value={{ count, increment }}>
          <div>
            <ChildComponent />
          </div>
        </MyContext.Provider>
      );
    }
    
    export default ParentComponent;
    
    // ChildComponent.js
    import React, { useContext } from 'react';
    import MyContext from './MyContext';
    
    function ChildComponent() {
      const { count, increment } = useContext(MyContext);
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={increment}>Increment</button>
        </div>
      );
    }
    
    export default ChildComponent;
  4. Refs(引用): 使用 React 的 ref 属性来引用子组件,并直接调用子组件中的方法或访问其属性。

    jsx
    // ParentComponent.js
    import React, { useRef } from 'react';
    import ChildComponent from './ChildComponent';
    
    function ParentComponent() {
      const childRef = useRef();
    
      const handleClick = () => {
        childRef.current.increment();
      };
    
      return (
        <div>
          <ChildComponent ref={childRef} />
          <button onClick={handleClick}>Increment from Parent</button>
        </div>
      );
    }
    
    export default ParentComponent;
    
    // ChildComponent.js
    import React, { forwardRef, useImperativeHandle, useState } from 'react';
    
    const ChildComponent = forwardRef((props, ref) => {
      const [count, setCount] = useState(0);
    
      const increment = () => {
        setCount(count + 1);
      };
    
      useImperativeHandle(ref, () => ({
        increment: increment
      }));
    
      return (
        <div>
          <p>Count: {count}</p>
        </div>
      );
    });
    
    export default ChildComponent;

React Router

React Router 是 React 应用程序中用于处理路由的一种库。它允许你在单页应用(Single Page Application,SPA)中实现路由功能,使得用户在应用程序中不同的页面之间进行导航和切换时能够得到良好的用户体验。

以下是 React Router 的一些关键概念和组件:

  1. BrowserRouter:BrowserRouter 是 React Router 中最常用的组件之一。它使用 HTML5 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。

  2. Route:Route 组件是 React Router 中定义路由的主要方式之一。它用于在 URL 匹配时渲染对应的组件。Route 可以接受 path 属性来指定匹配的 URL,以及 component 属性来指定匹配成功时渲染的组件。

  3. Link:Link 组件用于在应用程序中创建链接。与传统的 <a> 标签不同,Link 使用了 React Router 的导航系统,当用户点击链接时,React Router 会阻止默认的页面跳转行为,而是通过 history API 来更新 URL,从而实现单页应用的路由导航。

  4. Switch:Switch 组件用于包裹 Route 组件,它在匹配路由时只会渲染第一个匹配成功的 Route 组件,这样可以避免渲染多个匹配的路由组件。

  5. Redirect:Redirect 组件用于在用户访问某个 URL 时自动重定向到另一个 URL。它可以用于处理未登录用户跳转到登录页面、或者处理 404 页面等场景。

React Router 还有其他一些有用的功能,比如路由参数、嵌套路由、路由守卫等,这些功能都使得在 React 应用中管理页面导航变得更加灵活和便捷。

下面是一个简单的代码示例:

jsx
// App.jsx
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
import NotFound from './NotFound';

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/contact">Contact</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
          <Route path="/404" component={NotFound} />
          <Redirect to="/404" />
        </Switch>
      </div>
    </Router>
  );
}

export default App;

在这个示例中,我们使用了 BrowserRouter 作为路由器,并定义了三个路由:"/"、"/about" 和 "/contact",分别对应着 Home、About 和 Contact 组件。NotFound 组件用于处理未匹配到的路由,如果用户访问了未定义的路由,则会自动重定向到 "/404" 页面。在导航栏中使用了 Link 组件来创建链接,当用户点击链接时,会触发路由导航,渲染相应的页面。

每个页面组件(比如 Home、About、Contact)可以是普通的函数组件或者类组件,它们不需要关心路由的匹配逻辑,只需要专注于页面的展示和逻辑即可。

Flux 架构

Flux 是一种应用程序架构模式,旨在帮助管理应用程序中的数据流。它最初由 Facebook 提出,用于解决传统 MVC 模式在处理复杂数据流时导致的问题。

在 Flux 架构中,数据流是单向的,这意味着数据只能从一个地方流向另一个地方,而不能反向流动。Flux 将应用程序分为四个核心部分:

  1. View(视图):负责渲染用户界面,并依赖于 Store 中的数据。

  2. Action(动作):表示用户在视图上执行的操作,例如点击按钮或提交表单。当用户执行一个动作时,会触发相应的 Action。

  3. Dispatcher(分发器):负责接收来自 Action 的请求,并将这些请求分发给注册在它上面的回调函数(即 Store)。

  4. Store(数据存储):负责存储应用程序的状态和数据,并在收到来自 Dispatcher 的更新请求时更新自己。Store 中的数据是唯一可变的状态,视图只能通过订阅 Store 来获取数据的变化。

Flux 通过这种单向数据流的设计来提供更可控、可预测的应用程序状态管理,特别适用于大型复杂的前端应用程序。

在 React 应用中,通常会使用 Flux 架构或者基于 Flux 的状态管理库(如 Redux)来管理应用程序的状态和数据流。React 组件作为视图层,可以订阅 Store 中的数据,并根据数据的变化来更新 UI,同时也可以派发 Action 来改变应用程序的状态,触发数据流的更新。因此,React 可以看作是 Flux 架构中的 View 层的实现。

错误边界

在 React 中,错误边界(Error Boundaries)是一种用于捕获并处理组件树中子组件中 JavaScript 错误的机制。错误边界是一个 React 组件,它能够捕获在其子组件树中任何位置抛出的 JavaScript 错误,并渲染备用 UI,而不是整个组件树崩溃。

错误边界的主要目的是增强应用程序的稳定性,使其在出现错误时能够优雅地处理问题而不是整个应用程序崩溃。错误边界不会捕获以下类型的错误:

  1. 在事件处理器中抛出的错误(因为它们不会通过组件树传递)
  2. 在异步代码中抛出的错误(例如 setTimeout 或 requestAnimationFrame 回调函数)
  3. 服务端渲染期间抛出的错误
  4. 错误边界本身抛出的错误(如果错误边界自身抛出错误,则错误会被上层组件的错误边界捕获)

使用错误边界的步骤如下:

  1. 创建一个错误边界组件,通常命名为 ErrorBoundary
  2. ErrorBoundary 组件中实现 componentDidCatch(error, info) 生命周期方法。该方法会捕获任何子组件抛出的错误。
  3. componentDidCatch 方法中,可以处理错误并设置状态,以渲染备用 UI。
  4. 在需要错误边界保护的地方,将 ErrorBoundary 组件包裹在子组件的周围。

示例代码如下:

javascript
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // 将错误记录到日志
    console.error("Error caught by ErrorBoundary:", error, info);
    // 更新状态以渲染备用 UI
    this.setState({ hasError: true });
  }

  render() {
    if (this.state.hasError) {
      // 渲染备用 UI
      return <h1>Something went wrong.</h1>;
    }
    // 如果没有错误,则渲染子组件
    return this.props.children;
  }
}

// 使用错误边界保护组件
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

在上面的示例中,ErrorBoundary 组件捕获其子组件 MyComponent 抛出的任何错误,并在发生错误时渲染了一段备用 UI。

React 性能优化

优化 React 应用的性能是一个持续不断的过程,涉及多个方面,包括组件设计、状态管理、渲染优化等。以下是一些优化 React 应用性能的常见方法:

  1. 使用生命周期方法和钩子函数

    • 合理使用 componentDidMountcomponentDidUpdatecomponentWillUnmount 等生命周期方法,以及 useStateuseEffect 等钩子函数,确保组件的生命周期管理得当,避免不必要的渲染和资源浪费。
  2. 避免不必要的渲染

    • 使用 shouldComponentUpdate 或 React Hooks 中的 useMemouseCallback 来避免不必要的组件重新渲染。
    • 使用 React DevTools 来检查组件的渲染情况,找出造成不必要渲染的原因。
  3. 使用组件分割和懒加载

    • 将大型组件分割成小型组件,并使用 React.lazy 和 Suspense 进行懒加载,减少初始加载时间和页面大小。
  4. 优化列表渲染

    • 对于长列表,使用虚拟滚动技术,例如 react-virtualized 或 react-window,以减少 DOM 元素数量,提高渲染性能。
    • 使用 key 属性来帮助 React 识别列表中的每个元素,避免不必要的重新渲染。
  5. 减少内存占用

    • 避免内存泄漏,及时清理不再使用的资源和订阅,如在 componentWillUnmount 中取消订阅或清除定时器。
    • 使用 React Profiler 来检测内存泄漏和性能问题。
  6. 使用 React.memo 和 PureComponent

    • 对于纯函数组件,使用 React.memo 或 PureComponent 进行性能优化,避免不必要的渲染。
  7. 使用 CDN 和缓存

    • 使用 CDN 加速 React 库和其他依赖库的加载。
    • 启用浏览器缓存,减少资源加载时间。
  8. 代码优化

    • 避免在渲染过程中执行复杂的计算或操作,可以将这些操作移至异步任务或使用 Web Workers。
    • 优化 JavaScript 代码,减少不必要的循环和计算,提高执行效率。
  9. 使用生产环境构建

    • 在生产环境中使用优化后的打包工具(如 Webpack、Parcel 等),启用代码压缩、代码分割、懒加载等功能,减少资源加载时间。

React 和 Vue 的区别

1. 核心理念

  • Vue:渐进式框架,使用模板语法,内置响应式数据绑定,易上手。
  • React:UI 库,使用 JSX 语法,单向数据流,强调组件化和函数式编程,更灵活。

2. 组件开发

  • Vue:单文件组件(SFC),模板、逻辑、样式分离,直观易用。
  • React:推荐函数式组件+Hooks,JSX 让逻辑与 UI 结合更紧密。

3. 状态管理

  • Vue:内置响应式系统,可用 Vuex、Pinia 进行全局状态管理。
  • React:状态管理方案多样(useState、useReducer、Redux、Recoil)。

4. 生态系统

  • Vue:官方提供 Vue Router、Vuex/Pinia,生态完善,中文资源丰富。
  • React:社区庞大,工具多样(Next.js、Redux、MobX 等),适合大型应用。

5. 性能优化

  • Vue:虚拟 DOM + 响应式系统,自动依赖追踪,减少不必要渲染。
  • React:虚拟 DOM + Hooks + Memoization(useMemo、useCallback)优化渲染。

6. 适用场景

  • Vue:适合中小型项目、快速开发、国内市场占有率高。
  • React:适合大型复杂应用,灵活度高,企业级项目常用。

Released under the AGPL-3.0 License