React 快速手册 :构建高效Web应用的指南

443 阅读10分钟

在数字化浪潮的推动下,React.js以其革命性的组件化思维和卓越的性能,已经成为前端开发的新标杆。它不仅极大地丰富了用户界面的交互性,更以其简洁、高效的开发模式,引领了Web开发的新潮流。本文精心编纂了一份全面的React.js速查表,旨在帮助开发者从基础概念到高级技巧,一步步掌握React.js的精髓。通过生动的示例和深入的解析,这份指南将带你领略React.js的奥妙,助你在构建高性能Web应用程序的道路上游刃有余。无论你是刚刚踏入React.js世界的新手,还是渴望进一步提升技能的资深开发者,这份速查表都将是你探索未知、解锁潜能的得力伙伴。让我们一起启程,深入探索React.js的奇妙世界,开启一段充满挑战与发现的学习之旅。


1. React简介

React.js,通常简称为React,是一个用于构建用户界面的开源JavaScript库,特别适用于需要快速且交互性强的用户体验的单页应用程序。由Facebook开发,React允许开发者创建能够高效更新和渲染的大型Web应用程序,以响应数据变化。

React的核心概念是组件,它是一个自包含的模块,用于渲染一些输出。组件可以嵌套、管理和独立处理,使开发过程高效且易于维护。

2. React入门

设置环境

开始使用React之前,你需要设置开发环境。以下是步骤:

  1. 安装Node.js和npm:React依赖于Node.js和npm(Node包管理器)来管理依赖。

    • official website下载并安装Node.js。

    • 通过运行以下命令验证安装:

      node -v
      npm -v
      
  2. 安装Create React App:Create React App是学习React的舒适环境,也是开始React单页应用程序的好方法。

    npm install -g create-react-app
    

创建新的React应用程序

环境设置完成后,你可以创建一个新的React应用程序。

  1. 创建新项目

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

此命令创建一个具有指定名称(my-app)的新目录,设置一个新的React项目,并启动开发服务器。你可以打开浏览器并访问[http://localhost:3000]来查看你的新React应用程序。

3. React组件

组件是任何React应用程序的构建块。它们允许你将UI分割成独立的、可重用的部件。

函数组件

函数组件是接受props作为参数并返回React元素的JavaScript函数。它们比类组件更简单、更易于编写。

import React from 'react';

const Welcome = ({ name }) => {
  return <h1>Welcome, {name}!</h1>;
};

export default Welcome;

类组件

类组件是扩展了React.Component的ES6类,并有一个返回React元素的render方法。

import React, { Component } from 'react';

class Welcome extends Component {
  render() {
    return <h1>Welcome, {this.props.name}!</h1>;
  }
}

export default Welcome;

函数组件与类组件的区别

  • 状态管理:函数组件使用hooks(useStateuseEffect等)进行状态管理,而类组件使用this.state和生命周期方法。
  • 生命周期方法:类组件具有生命周期方法,如componentDidMountcomponentDidUpdatecomponentWillUnmount。函数组件使用useEffect hook来处理副作用。
  • 简洁性:函数组件更简单、更简洁,使它们更容易阅读和维护。

4. JSX

JSX是一种语法扩展,允许你直接在JavaScript中编写HTML。它产生React“元素”。

JSX语法

JSX看起来像HTML,但会被转换成JavaScript。

const element = <h1>Hello, world!</h1>;

嵌入表达式

你可以通过在花括号中包裹任何JavaScript表达式来在JSX中嵌入它。

const name = 'John';
const element = <h1>Hello, {name}!</h1>;

JSX属性

JSX允许你使用类似于HTML的属性。

const element = <img src={user.avatarUrl} alt={user.name} />;

5. 状态和Props

理解状态

状态是一个内置对象,用于存储属于组件的属性值。当状态对象变化时,组件会重新渲染。

使用useState Hook管理状态

useState hook用于向函数组件添加状态。

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
};

export default Counter;

理解Props

Props是传递给React组件的参数。Props通过HTML属性传递给组件。

传递Props

Props是只读且不可变的。

const Greeting = (props) => {
  return <h1>Hello, {props.name}!</h1>;
};

const App = () => {
  return <Greeting name="Alice" />;
};

Prop类型和默认Props

PropTypes允许你定义组件应该接收的Props类型。可以定义默认Props以确保如果未指定Props,则Props将具有一个值。

import React from 'react';
import PropTypes from 'prop-types';

const Greeting = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

Greeting.propTypes = {
  name: PropTypes.string.isRequired,
};

Greeting.defaultProps = {
  name: 'Guest',
};

export default Greeting;

6. 组件生命周期

类组件中的生命周期方法

生命周期方法是在类组件中在特定生命周期点运行的特殊方法。

  • componentDidMount:组件渲染后执行。
  • componentDidUpdate:组件更新并刷新到DOM后执行。
  • componentWillUnmount:组件从DOM中移除前执行。
class MyComponent extends React.Component {
  componentDidMount() {
    // 组件挂载后运行
  }

  componentDidUpdate(prevProps, prevState) {
    // 组件更新后运行
  }

  componentWillUnmount() {
    // 组件卸载前运行
  }

  render() {
    return <div>My Component</div>;
  }
}

使用useEffect Hook

useEffect hook结合了componentDidMountcomponentDidUpdatecomponentWillUnmount的功能。

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

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

  useEffect(() => {
    // 挂载和更新时运行
    document.title = `You clicked ${count} times`;

    // 清理函数(卸载时运行)
    return () => {
      console.log('Cleanup');
    };
  }, [count]); // 依赖数组

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
};

export default MyComponent;

7. 处理事件

React中的事件处理

React事件使用驼峰命名法,而不是小写。在JSX中,你将函数作为事件处理器传递,而不是字符串。

const handleClick = () => {
  console.log('Button clicked');
};

const MyComponent = () => {
  return <button onClick={handleClick}>Click me</button>;
};

合成事件

React的事件系统被称为合成事件。它是浏览器原生事件系统的跨浏览器包装。

处理表单

在React中处理表单涉及控制输入元素和管理状态。

import React, { useState } from 'react';

const MyForm = () => {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    alert('A name was submitted: ' + value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
};

export default MyForm;

事件处理器最佳实践

  • 避免内联事件处理器:在JSX外部定义事件处理器,以提高可读性和性能。
  • 使用箭头函数:使用箭头函数以避免this绑定问题。
  • 防抖昂贵操作:对API调用等昂贵操作进行防抖,以避免性能问题。

8. 条件渲染

if-else语句

你可以在render方法中使用JavaScript if-else语句。

const MyComponent = ({ isLoggedIn }) => {
  if (isLoggedIn) {
    return <h1>Welcome back!</h1>;
  } else {
    return <h1>Please sign in.</h1>;
  }
};

三元操作符

三元操作符是一种简洁的条件渲染方式。

const MyComponent = ({ isLoggedIn }) => {
  return (
    <div>
      {isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please sign in.</h1>}
    </div>
  );
};

逻辑&&操作符

你可以使用逻辑&&操作符有条件地包含元素。

const MyComponent = ({ isLoggedIn }) => {
  return (
    <div>
      {isLoggedIn && <h1>Welcome back!</h1>}
    </div>
  );
};

使用逻辑&&操作符的内联

使用逻辑&&操作符的内联if允许你在输出中有条件地包含一个元素。

const Mailbox = ({ unreadMessages }) => {
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
};

9. 列表和Keys

渲染列表

你可以使用花括号{}在JSX中构建元素集合并包含它们。

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

const NumberList = () => {
  return (
    <ul>{listItems}</ul>
  );
};

使用Keys

Keys帮助React识别哪些项目发生了变化、被添加或被移除。应该在数组内部的元素上赋予Keys,以给元素一个稳定的标识。

const NumberList = (props) => {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
};

Keys必须仅在兄弟元素中唯一

在数组中使用的Keys应在其兄弟元素中唯一。

function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

10. 表单和受控组件

处理表单数据

在React中处理表单数据涉及管理表单字段的状态。

import React, { useState } from 'react';

const MyForm = () => {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    alert('A name was submitted: ' + value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={value} onChange={handleChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
};

export default MyForm;

受控组件与非受控组件

受控组件是由React状态控制的组件。非受控组件是那些维护自己内部状态的组件。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: '' };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({ value: event.target.value });
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

使用Refs处理非受控组件

Refs提供了一种访问DOM节点或在渲染方法中创建的React元素的方式。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.input = React.createRef();
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

表单验证

表单验证确保用户输入是有效的。

const MyForm = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    if (!name || !email) {
      setError('Name and Email are required');
    } else {
      setError('');
      // 提交表单
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {error && <p>{error}</p>}
      <label>
        Name:
        <input type="text" value={name} onChange={(e) => setName(e.target.value)} />
      </label>
      <label>
        Email:
        <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
};

export default MyForm;

11. React Router

React Router是React应用程序中的路由库。它允许你处理导航和基于URL渲染不同的组件。

设置React Router

  1. 安装React Router

    npm install react-router-dom
    
  2. 设置路由

    import React from 'react';
    import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
    
    const Home = () => <h2>Home</h2>;
    const About = () => <h2>About</h2>;
    
    const App = () => {
      return (
        <Router>
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
          </Switch>
        </Router>
      );
    };
    
    export default App;
    

路由参数

你可以使用路由参数从URL中捕获值。

import React from 'react';
import { BrowserRouter as Router, Route, Switch, useParams } from 'react-router-dom';

const User = () => {
  const { id } = useParams();
  return <h2>User ID: {id}</h2>;
};

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/user/:id" component={User} />
      </Switch>
    </Router>
  );
};

export default App;

嵌套路由

嵌套路由允许你在父组件内渲染子组件。

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, useRouteMatch } from 'react-router-dom';

const Topic = ({ match }) => <h3>Requested Topic ID: {match.params.topicId}</h3>;

const Topics = ({ match }) => {
  let { path, url } = useRouteMatch();
  return (
    <div>
      <h2>Topics</h2>
      <ul>
        <li>
          <Link to={`${url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${url}/props-v-state`}>Props v. State</Link>
        </li>
      </ul>
      <Switch>
        <Route exact path={path}>
          <h3>Please select a topic.</h3>
        </Route>
        <Route path={`${path}/:topicId`} component={Topic} />
      </Switch>
    </div>
  );
};

const App = () => {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/topics" component={Topics} />
        </Switch>
      </div>
    </Router>
  );
};

export default App;

重定向和导航

你可以使用Redirect组件以编程方式重定向到不同的路由。

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';

const Home = () => <h2>Home</h2>;
const About = () => <h2>About</h2>;

const App = () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Redirect from="/old-path" to="/new-path" />
      </Switch>
    </Router>
  );
};

export default App;

12. Context API

Context API提供了一种通过组件树传递数据的方法,而不必在每个级别手动向下传递props。

创建Context

要创建一个上下文,使用React.createContext

const MyContext = React.createContext();

消费Context

要在函数组件中使用上下文值,使用useContext钩子,在类组件中使用Context.Consumer

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

函数组件中的Context

const MyComponent = () => {
  return (
    <MyContext.Provider value="Hello">
      <AnotherComponent />
    </MyContext.Provider>
  );
};

const AnotherComponent = () => {
  const value = useContext(MyContext);
  return <div>{value}</div>;
};

更新Context

要更新上下文,创建一个带有状态的提供者组件。

const MyProvider = ({ children }) => {
  const [value, setValue] = useState('Hello');
  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};

const MyComponent = () => {
  const { value, setValue } = useContext(MyContext);
  return (
    <div>
      {value}
      <button onClick={() => setValue('Updated Value')}>Update</button>
    </div>
  );
};

Context最佳实践

  • 避免过度使用context:谨慎使用context,仅用于全局数据。
  • 使用多个contexts:通过使用多个contexts来分离关注点。
  • 记忆context值:使用useMemo以避免不必要的重新渲染。

13. Hooks

Hooks是允许你在函数组件中使用状态和其他React特性的函数。

基础Hooks(useState, useEffect)

  • useState:为函数组件添加状态。
  • useEffect:在函数组件中执行副作用。

附加Hooks(useContext, useReducer)

  • useContext:访问上下文值。
  • useReducer:管理复杂的状态逻辑。
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 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>
  );
}

自定义Hooks

自定义Hooks是封装逻辑的函数,可以在组件间复用。

const useFetch = (url) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => setData(data));
  }, [url]);

  return data;
};

const MyComponent = () => {
  const data = useFetch('https://api.example.com/data'); 
  return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
};

Hooks规则

  • 在顶层调用Hooks:不要在循环、条件或嵌套函数中调用Hooks。
  • 仅从React函数中调用Hooks:从函数组件或自定义Hooks中调用Hooks。

14. 高阶组件(HOC)

高阶组件(HOC)是接受组件并返回新组件的函数。

理解HOC

HOC用于向组件添加额外的功能。

const withLogging = (WrappedComponent) => {
  return (props) => {
    console.log('Rendering', WrappedComponent.name);
    return <WrappedComponent {...props} />;
  };
};

创建HOC

const EnhancedComponent = withLogging(MyComponent);

使用HOC

const MyComponent = (props) => {
  return <div>My Component</div>;
};

const EnhancedComponent = withLogging(MyComponent);

HOC最佳实践

  • 不要改变原始组件:返回一个新的组件。
  • 使用显示名称进行调试:为HOC设置displayName以便更好地调试。

15. 错误边界

错误边界是React组件,它们捕获其子组件树中的JavaScript错误,记录这些错误,并显示一个备用UI。

实现错误边界

错误边界在渲染过程中、生命周期方法中以及它们下面的整个树的构造函数中捕获错误。

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

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 你也可以将错误记录到错误报告服务
    console.log(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

捕获错误

错误边界捕获渲染方法和生命周期方法中的错误。

const MyComponent = () => {
  throw new Error('An error occurred');
  return <div>My Component</div>;
};

const App = () => {
  return (
    <ErrorBoundary>
      <MyComponent />
    </ErrorBoundary>
  );
};

错误边界最佳实践

  • 使用错误边界捕获组件中的错误:使用错误边界捕获并显示UI组件中的错误。
  • 记录错误以进行调试:将错误记录到外部服务以进行调试。

16. React性能优化

记忆化

记忆化帮助避免不必要的组件重新渲染。

import React, { memo } from 'react';

const MyComponent = memo(({ value }) => {
  return <div>{value}</div>;
});

代码分割

代码分割帮助只加载必要的代码,从而提高性能。

import React, { Suspense, lazy } from 'react';

const OtherComponent = lazy(() => import('./OtherComponent'));

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

懒加载

懒加载帮助仅在需要时加载组件。

import React, { Suspense, lazy } from 'react';

const OtherComponent = lazy(() => import('./OtherComponent'));

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

useMemo和useCallback

  • useMemo:记忆化昂贵的计算。
  • useCallback:记忆化函数。
const MyComponent = ({ value }) => {
  const memoizedValue = useMemo(() => {
    return computeExpensiveValue(value);
  }, [value]);

  const memoizedCallback = useCallback(() => {
    doSomething(value);
  }, [value]);

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

React开发者工具

使用React开发者工具识别性能瓶颈。

17. React中的测试

Jest和React Testing Library

Jest和React Testing Library是测试React组件的流行工具。

编写测试

  • 快照测试:捕获渲染的组件并与保存的快照进行比较。
  • 单元测试:测试单个组件和函数。
  • 集成测试:测试组件与服务之间的集成。
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('renders MyComponent', () => {
  render(<MyComponent />);
  const element = screen.getByText(/My Component/i);
  expect(element).toBeInTheDocument();
});

18. React最佳实践

组件结构

  • 按功能组织组件:将相关组件分组在一起。
  • 使用描述性名称:为组件和props使用清晰且描述性的名称。
  • 保持组件小巧:将大型组件拆分为更小的可重用组件。

状态管理

  • 提升状态:将状态提升到最近的共同祖先。
  • 使用Context进行全局状态管理:使用Context API进行全局状态管理。

样式

  • 使用CSS模块:使用CSS模块实现作用域和模块化样式。
  • 使用styled-components:使用styled-components实现动态样式。

性能

  • 避免不必要的重新渲染:使用记忆化和React内置的性能优化工具。
  • 使用代码分割:拆分代码以仅加载必要的组件。

测试

  • 编写全面的测试:为应用程序的所有关键部分编写测试。
  • 使用快照测试:使用快照测试捕获意外更改。

结论

在探索React.js的旅程中,我们不仅揭开了构建动态Web界面的神秘面纱,还深入理解了如何通过组件化、状态管理、以及性能优化等关键概念来提升我们的开发技艺。随着对Context API、Hooks、高阶组件和错误边界等高级特性的掌握,我们得以在代码的海洋中乘风破浪。通过本文的引导,我们学会了如何将这些工具和技巧融会贯通,打造出既强大又优雅的Web应用程序。最终,我们不仅收获了知识,更获得了构建未来Web体验的能力。这份速查表是我们在React世界中不断前行的指南针,提醒我们始终追求卓越,创造无限可能。