为了账号安全,请及时绑定邮箱和手机立即绑定

React 考点解析与实战教程

概述

本文详细介绍了React中的基础概念和核心特性,包括JSX语法、组件使用、状态和属性管理以及路由与状态管理等。文章还涵盖了React组件的优化技巧、项目构建与部署的方法以及常见问题的解决策略,全面解析了React考点。

React 基础概念概述
JSX 语法介绍

JSX 是 React 中使用的一种语法扩展,它允许你在 JavaScript 代码中直接编写类似 HTML 的标记。JSX 通常编译成 React.createElement() 函数调用,或者使用 JSX 的语法糖来创建 React 元素。

JSX 的基本语法

JSX 中的元素可以包含属性,子元素等。以下是一个简单的 JSX 示例:

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

在 JSX 语法中,大括号 {} 用于在 JSX 中插入 JavaScript 表达式。例如,你可以插入一个变量或一个函数调用来在页面上显示动态的内容:

const name = "John";
const element = <h1 className="greeting">Hello, {name}!</h1>;

JSX 的基本属性绑定

JSX 元素通常通过属性传递信息。例如,可以使用 props 将数据传递给组件:

const element = <Greeting name="John" />;

在组件内部,可以通过 props 访问传递的属性:

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

JSX 的注意事项

  1. JSX 必须有唯一的根元素:每个 JSX 语句必须有一个唯一的根元素,所有子元素必须嵌套在这个根元素内。

    // 错误的写法
    <h1>Hello</h1>
    <h2>World</h2>
    
    // 正确的写法
    <div>
     <h1>Hello</h1>
     <h2>World</h2>
    </div>
  2. JSX 中的属性:属性可以是字符串,数字,布尔值等基本数据类型。

    <button className="btn" onClick={handleClick}>Click Me</button>
  3. JSX 中的嵌套:JSX 允许嵌套元素,例如在一个 div 中嵌套多个 span 元素。

    <div>
     <span>Span 1</span>
     <span>Span 2</span>
    </div>
React 组件的基本使用

React 组件是 React 的核心概念,它们可以被看作是页面上的独立单元,可以组合起来构成复杂的用户界面。React 组件有两种类型:函数组件和类组件。

函数组件

函数组件是最简单的一种组件,它只是一个返回 JSX 的 JavaScript 函数。函数组件通常用来展示数据,不包含状态和生命周期方法。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

类组件

类组件是更复杂的一种组件,它可以通过继承 React.Component 类来自定义组件的行为。类组件可以包含状态(state)和生命周期方法。

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

组件的组合

React 组件的组合允许你在一个组件中嵌套使用其他组件。

function Comment(props) {
  return (
    <div className="comment">
      <h2 className="commentAuthor">
        {props.author}
      </h2>
      <button onClick={props.onClick}>Remove</button>
    </div>
  );
}

function CommentList(props) {
  return (
    <div className="commentList">
      <Comment author="Henry" onClick={() => console.log('Remove Comment')} />
    </div>
  );
}
状态和属性的管理

状态(State)

状态是组件内部的可变数据,通常用于组件内部的局部数据管理。状态通常通过构造函数 this.state 来初始化,并使用 this.setState() 方法来更新状态。

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = { date: new Date() };
  }

  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

属性(Props)

属性是父组件传递给子组件的数据和行为,它们是只读的,不能直接修改。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

状态更新的注意事项

  1. 状态更新是异步的this.setState() 是异步执行的,这意味着如果你连续调用两次 setState(),React 可能不会立即更新状态,而是将两次更新合并为一次。

    // 正确的写法
    this.setState((prevState, props) => {
     return { count: prevState.count + 1 };
    });
    
    // 错误的写法
    this.setState({ count: this.state.count + 1 });
    this.setState({ count: this.state.count + 1 });
  2. 不要直接修改状态:直接修改 this.state 会导致不可预测的结果,因为 React 不会检测到这种变化。

    // 错误的代码
    this.state.count = this.state.count + 1;

使用 useState 钩子

在函数组件中,可以使用 useState 钩子来管理状态。

import React, { useState } from 'react';

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

  function increment() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
React 核心特性详解
虚拟 DOM 的工作原理

虚拟 DOM 是 React 中的一个关键概念,它是一种轻量级的 DOM 模拟实现。React 使用虚拟 DOM 来提高页面的渲染性能,避免每次用户交互都直接操作实际的 DOM。

虚拟 DOM 的作用

虚拟 DOM 通过在内存中构建一个与实际 DOM 相对应的轻量级对象模型,然后在每次组件状态发生变化时,React 会重新构建虚拟 DOM,然后通过一个称为 Diff 算法的过程来比较新的虚拟 DOM 与旧的虚拟 DOM,找出最小的差异部分,然后直接更新实际的 DOM。

虚拟 DOM 的实现

React 会自动为每个组件创建一个虚拟 DOM 节点。当组件的状态发生变化时,React 会重新构建虚拟 DOM 节点,并调用 render 方法将新的虚拟 DOM 节点渲染到页面上。

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

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState(state => ({ count: state.count + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <h1>{count}</h1>
      </div>
    );
  }
}

Diff 算法

Diff 算法是用于比较旧的虚拟 DOM 与新的虚拟 DOM 的过程。它通过将新的虚拟 DOM 与旧的虚拟 DOM 进行比较来确定需要更新的实际 DOM 节点。

const oldVNode = {
  type: 'div',
  props: {
    children: [
      { type: 'span', props: { children: 'Hello, ' } },
      { type: 'span', props: { children: 'world!' } }
    ]
  }
};

const newVNode = {
  type: 'div',
  props: {
    children: [
      { type: 'span', props: { children: 'Hello, ' } },
      { type: 'strong', props: { children: 'world!' } }
    ]
  }
};

// Diff 算法会比较 oldVNode 和 newVNode,发现 span 节点未变,但是 span 节点的子元素类型发生了变化,那么就会更新实际的 DOM 节点来替换掉 span 节点的子元素。
受控组件与非受控组件

受控组件

受控组件是将表单元素(如 input、textarea、select)的状态绑定到组件的状态中。这意味着表单元素的值是由组件的状态控制的,而不是由 HTML 元素的 value 属性控制的。

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(`提交的数据:${this.state.value}`);
    event.preventDefault();
  }

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

非受控组件

非受控组件是一种表单元素的值由 HTML 元素的 value 属性控制,而不是由组件的状态控制的方式。通常使用 ref 属性来获取表单元素的值。

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

  handleSubmit(event) {
    alert(`提交的数据:${this.myInput.current.value}`);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" ref={this.myInput} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}
React 路由与状态管理
React Router 简介

React Router 是 React 项目中最常用的路由库之一,它允许你在 React 应用中实现单页面应用(SPA)的导航和路由管理。

基本用法

React Router 主要提供了几个核心组件:<Router><Route><Link><Switch><Redirect>

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

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

        <hr />

        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/users" component={Users} />
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

动态路由与参数

React Router 支持动态路由和参数传递。

<Route path="/users/:userId" component={User} />

function User({ match }) {
  return <h2>{match.params.userId}</h2>;
}
使用 Redux 进行状态管理

Redux 是一个用于管理应用状态的库,它提供了一种集中式的、可预测的状态管理模式。

Redux 核心概念

  1. Store:所有应用的状态都保存在 Store 中,它是唯一的数据源。
  2. Reducers:reducers 是纯函数,它们接受当前 state 和 action,返回一个新的 state。
  3. Actions:actions 是将数据从应用传到 store 的方式,它是一个普通的 JavaScript 对象。

Redux 的基本使用

// Action Creator
function increment() {
  return {
    type: 'INCREMENT'
  };
}

function decrement() {
  return {
    type: 'DECREMENT'
  };
}

// Reducer
const initialState = 0;

function counter(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

// Store
import { createStore } from 'redux';

const store = createStore(counter);

// Component
function Counter() {
  const dispatch = useDispatch();

  return (
    <div>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
      <h1>{store.getState()}</h1>
    </div>
  );
}

// 使用中间件
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(counter, applyMiddleware(thunk));
React 组件优化技巧
性能优化方法

保持组件的纯函数

纯函数组件是只依赖于 props 和 state 的组件,这种组件每次渲染时都会得到相同的结果,这有助于优化性能。

function Greeting({ name }) {
  return <h1>Hello, {name}</h1>;
}

使用 shouldComponentUpdatePureComponent

shouldComponentUpdate 是一个生命周期方法,可以在组件更新前决定是否需要重新渲染。PureComponent 是一个自带 shouldComponentUpdate 方法的类,它会浅比较 props 和 state 来决定是否需要更新。

class Greeting extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.name !== this.props.name;
  }

  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

class Greeting extends React.PureComponent {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

使用 React.memouseMemo

React.memo 是一个高阶组件,它可以用于避免不必要的重新渲染。

import React, { memo } from 'react';

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

useMemo 钩子可以用来优化函数组件中的计算密集型函数。

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

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

  const memoizedIncrement = useMemo(() => {
    return (val) => {
      setCount(val);
    };
  }, [count]);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => memoizedIncrement(count + 1)}>Increment</button>
    </div>
  );
}
组件的生命周期

React 组件的生命周期可以分为以下几个阶段:初始化、更新、卸载。

生命周期方法

  • componentWillMount:在组件挂载之前调用。
  • componentDidMount:在组件挂载之后调用。
  • componentWillReceiveProps:在组件接收到新的 props 时调用。
  • shouldComponentUpdate:在组件接收到新的 props 或 state 时调用,决定组件是否需要重新渲染。
  • componentWillUpdate:在组件更新前调用。
  • componentDidUpdate:在组件更新后调用。
  • componentWillUnmount:在组件卸载前调用。

使用函数组件和 useEffect

在函数组件中,可以使用 useEffect 钩子来替代生命周期方法。

import React, { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    console.log('组件挂载或更新了');
    return () => {
      console.log('组件卸载了');
    };
  }, []); // 注意:这里传入了一个空数组,表示不依赖任何 props 或 state

  useEffect(() => {
    console.log('组件接收到新的 props');
  }, [props]); // 注意:这里传入了 props,表示依赖 props
}
React 项目构建与部署
使用 Webpack 打包项目

Webpack 是一个模块打包工具,它能将项目中的各种资源(如 JavaScript 文件、CSS 文件、图片等)打包成一个或多个文件,以便在浏览器中加载。

安装和配置 Webpack

  1. 安装 Webpack 和相关插件。

    npm install --save-dev webpack webpack-cli webpack-dev-server
  2. 创建 webpack.config.js 文件,配置 Webpack 的各项参数。

    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
     entry: './src/index.js',
     output: {
       filename: 'bundle.js',
       path: path.resolve(__dirname, 'dist'),
     },
     module: {
       rules: [
         {
           test: /\.js$/,
           exclude: /node_modules/,
           use: {
             loader: 'babel-loader',
           },
         },
         {
           test: /\.css$/,
           use: ['style-loader', 'css-loader'],
         },
         {
           test: /\.(png|svg|jpg|gif)$/,
           use: ['file-loader'],
         },
       ],
     },
     plugins: [
       new HtmlWebpackPlugin({
         template: './public/index.html',
       }),
     ],
     devServer: {
       contentBase: './dist',
     },
    };
  3. package.json 中添加 scripts 配置,以便使用命令行工具进行开发和构建。

    {
     "scripts": {
       "start": "webpack serve --mode development",
       "build": "webpack --mode production"
     }
    }

使用 Babel 转换代码

为了支持 ES6 等现代 JavaScript 语法,通常需要使用 Babel 进行代码转换。

  1. 安装 Babel 和相关的插件。

    npm install --save-dev @babel/core @babel/preset-env babel-loader
  2. 创建 .babelrc 配置文件,指定 Babel 预设。

    {
     "presets": ["@babel/preset-env"]
    }

使用 ESLint 进行代码检查

ESLint 是一个静态代码分析工具,可以帮助你检查代码中的错误和不规范的地方。

  1. 安装 ESLint 和相关的插件。

    npm install --save-dev eslint eslint-plugin-react
  2. 创建 .eslintrc.js 配置文件,指定 ESLint 规则。

    module.exports = {
     parserOptions: {
       ecmaVersion: 2015,
       sourceType: 'module',
     },
     extends: ['eslint:recommended', 'plugin:react/recommended'],
     rules: {},
    };
CI/CD 自动化部署

持续集成(CI)和持续部署(CD)是现代软件开发中的常用实践,它们可以帮助开发者自动化地构建、测试和部署代码。

使用 GitLab CI/CD

  1. 在项目根目录下创建 .gitlab-ci.yml 文件,指定 CI/CD 的配置。

    image: node:14
    
    stages:
     - build
     - deploy
    
    build:
     stage: build
     script:
       - npm install
       - npm run build
    
    deploy:
     stage: deploy
     script:
       - npm run deploy
     only:
       - master
  2. 在项目根目录下创建 package.json 文件,添加构建和部署的脚本。

    {
     "scripts": {
       "build": "webpack --mode production",
       "deploy": "scp -r dist/*.js user@server:/path/to/deploy"
     }
    }

使用 Travis CI

  1. 在项目根目录下创建 .travis.yml 文件,指定 CI/CD 的配置。

    language: node_js
    node_js:
     - 14
    
    script:
     - npm install
     - npm run build
    
    deploy:
     provider: script
     script: npm run deploy
     on:
       branch: master
  2. 在项目根目录下创建 package.json 文件,添加构建和部署的脚本。

    {
     "scripts": {
       "build": "webpack --mode production",
       "deploy": "scp -r dist/*.js user@server:/path/to/deploy"
     }
    }
React 常见问题与解答
常见错误排查与解决

错误一:Cannot read property 'xxx' of undefined

这个错误通常出现在尝试访问一个未定义的对象属性时。你需要检查相关代码,确保对象已经正确初始化或赋值。

function App() {
  const user = { name: 'John' };
  return <h1>Hello, {user.name}</h1>;
}

错误二:Warning: Each child in a list should have a unique "key" prop

这个警告出现在你使用数组来渲染列表时,但是每个列表项没有指定 key 属性。key 属性可以帮助 React 更高效地更新列表。

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

错误三:Warning: React does not recognize the 'xxx' prop on a DOM element

这个警告通常出现在你尝试传递一个无效的 HTML 属性给 DOM 元素时。你需要检查传递的属性名称是否正确。

function Button({ onClick }) {
  return <button onClick={onClick}>Click Me</button>;
}

错误四:TypeError: Cannot read property 'setState' of undefined

这个错误通常出现在你尝试访问已经卸载或不存在的组件的状态时。你需要确保组件仍然存在,并且状态访问是在正确的生命周期方法中进行的。

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

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState(state => ({ count: state.count + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return <h1>{this.state.count}</h1>;
  }
}
React 生态系统工具介绍

React DevTools

React DevTools 是一个浏览器扩展,它可以让你在浏览器中查看和调试 React 组件树。它可以帮助你检查组件的状态和属性,从而更容易地发现和修复问题。

Redux DevTools

Redux DevTools 是一个浏览器扩展,它可以让你在浏览器中查看和调试 Redux store 的状态变化。它可以帮助你理解应用的状态变化,从而更容易地进行调试和优化。

React Router

React Router 是一个用于管理 React 应用中路由的库。它提供了一系列的组件和功能,帮助你实现单页面应用的导航和路由管理。

React Hot Loader

React Hot Loader 是一个工具,它可以在开发过程中自动重新加载和热更新组件,从而加快你的开发速度。它允许你在不刷新浏览器的情况下看到代码更改的结果。

Storybook

Storybook 是一个工具,它可以让你在独立的环境中预览和测试 React 组件。它可以帮助你更好地组织和管理组件库,从而更容易地开发和维护复杂的 React 应用。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
手记
粉丝
31
获赞与收藏
200

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消