React教程(React入门教程)(React组件、JSX、React Props、React State、React事件处理、Hooks、高阶组件HOC)

文章目录

  • 一、什么是React
  • 二、React的特点
    • [1. **组件化**:React采用组件化模式,将UI拆分为独立、可复用的组件,提高代码复用率。](#1. 组件化:React采用组件化模式,将UI拆分为独立、可复用的组件,提高代码复用率。)
    • [2. **声明式设计**:React采用声明范式,可以轻松描述应用状态与UI之间的关系。](#2. 声明式设计:React采用声明范式,可以轻松描述应用状态与UI之间的关系。)
    • [3. **虚拟DOM**:React不直接操作DOM,而是通过虚拟DOM和diff算法,以最小的步骤作用到真实的DOM上,提升性能。](#3. 虚拟DOM:React不直接操作DOM,而是通过虚拟DOM和diff算法,以最小的步骤作用到真实的DOM上,提升性能。)
    • [4. **单向数据流**:React实现了单向响应的数据流,从数据到视图的渲染,减少了重复代码,比传统数据绑定更简单。](#4. 单向数据流:React实现了单向响应的数据流,从数据到视图的渲染,减少了重复代码,比传统数据绑定更简单。)
    • [5. **高效**:通过对DOM的模拟,最大限度地减少与DOM的交互。](#5. 高效:通过对DOM的模拟,最大限度地减少与DOM的交互。)
    • [6. **灵活**:React可以与已知的库或框架很好地配合。](#6. 灵活:React可以与已知的库或框架很好地配合。)
  • 三、React与其他框架的比较
    • [- React只专注于MVC框架中的V(视图层),而AngularJS是一个完整的MVC框架。](#- React只专注于MVC框架中的V(视图层),而AngularJS是一个完整的MVC框架。)
    • [- React是单向数据流,非双向数据绑定。](#- React是单向数据流,非双向数据绑定。)
    • [- React不直接操作DOM,而是通过虚拟DOM进行编程。](#- React不直接操作DOM,而是通过虚拟DOM进行编程。)
  • 四、React应用现状
  • 五、环境搭建
  • 六、React核心概念
  • 七、高阶组件
  • 八、实战:创建一个简单的组件
  • 九、学习资源推荐
    • [1. **官方文档**:React官网提供了最权威的文档和教程。](#1. 官方文档:React官网提供了最权威的文档和教程。)
    • [2. **React视频教程**:《React极速入门指南》、《Vue、Angular、React 项目开发与深度对比》。](#2. React视频教程:《React极速入门指南》、《Vue、Angular、React 项目开发与深度对比》。)
    • [3. **实践项目**:通过构建实际项目(如电商网站、社交应用)来巩固知识。](#3. 实践项目:通过构建实际项目(如电商网站、社交应用)来巩固知识。)
    • [4. **社区支持**:React社区活跃,Stack Overflow、GitHub等平台有大量问题解答。](#4. 社区支持:React社区活跃,Stack Overflow、GitHub等平台有大量问题解答。)
  • 十、结语

一、什么是React

React是由Facebook的软件工程师Jordan Walke创建的JavaScript库,于2011年部署于Facebook的newsfeed,2012年部署于Instagram,2013年5月正式开源。React专注于构建用户界面(UI),是MVC框架中的"V"(视图层),而非完整的MVC框架。

React的核心理念是"从UI出发,抽象出不同的组件,继而将它们拼装起来",这顺应了Web开发组件化的趋势。

二、React的特点

1. 组件化:React采用组件化模式,将UI拆分为独立、可复用的组件,提高代码复用率。

2. 声明式设计:React采用声明范式,可以轻松描述应用状态与UI之间的关系。

3. 虚拟DOM:React不直接操作DOM,而是通过虚拟DOM和diff算法,以最小的步骤作用到真实的DOM上,提升性能。

4. 单向数据流:React实现了单向响应的数据流,从数据到视图的渲染,减少了重复代码,比传统数据绑定更简单。

5. 高效:通过对DOM的模拟,最大限度地减少与DOM的交互。

6. 灵活:React可以与已知的库或框架很好地配合。

三、React与其他框架的比较

React与AngularJS等框架的主要区别在于:

- React只专注于MVC框架中的V(视图层),而AngularJS是一个完整的MVC框架。

- React是单向数据流,非双向数据绑定。

参考文章:React单向数据流(unidirectional data flow)与非双向数据绑定的理解

- React不直接操作DOM,而是通过虚拟DOM进行编程。

四、React应用现状

React在国外应用广泛,Facebook、Yahoo、Reddit等知名公司都在使用。在国内,知乎、豆瓣、优酷等大厂也逐渐采用React技术栈。

值得注意的是,截至2022年第一季度,国内前端框架使用上大多偏向于Vue,这导致React前端工程师相对稀缺。在学习难度上,React比Vue稍高,这也是许多企业对React开发经验要求较高的原因。

五、环境搭建

使用create-react-app脚手架快速搭建React项目:

bash 复制代码
# 创建新项目
npx create-react-app react-project

# 进入项目目录
cd react-project

# 启动项目
yarn start

如果提示没有yarn可看这里:yarn命令介绍(替代npm命令的JavaScript包管理工具)


六、React核心概念

1. 组件

React的核心是组件。组件是UI的构建块,可以是类组件或函数组件。

类组件示例

javascript 复制代码
import React, { Component } from 'react'; // 从React库中导入React对象和Component类

class Welcome extends Component { // 定义一个名为Welcome的类组件,继承自React.Component
  render() { // render方法是React组件的核心,用于返回要渲染的UI
    return <h1>Hello, {this.props.name}</h1>; // 返回一个h1元素,显示"Hello,"和传入的name属性
  }
}

函数组件示例

javascript 复制代码
function Welcome(props) { // 定义一个名为Welcome的函数组件,接收props参数
  return <h1>Hello, {props.name}</h1>; // 返回一个h1元素,显示"Hello,"和传入的name属性
}

2. JSX

JSX是JavaScript语法的扩展,使HTML结构与JavaScript代码混合在一起,提高可读性。

javascript 复制代码
const element = <h1>Hello, world!</h1>; // 创建一个h1元素,显示"Hello, world!"

3. Props

Props是组件的属性,用于从父组件向子组件传递数据。

javascript 复制代码
function Welcome(props) { // 定义一个名为Welcome的函数组件,接收props参数
  return <h1>Hello, {props.name}</h1>; // 返回一个h1元素,显示"Hello,"和传入的name属性
}

const element = <Welcome name="Sara" />; // 创建一个Welcome组件的实例,传入name属性为"Sara"



4. State

State是组件内部的状态,用于管理组件的动态数据。

javascript 复制代码
class Counter extends Component { // 定义一个名为Counter的类组件,继承自React.Component
  constructor(props) { // 构造函数,初始化组件状态
    super(props); // 调用父类Component的构造函数
    this.state = { count: 0 }; // 初始化组件状态,count属性初始值为0
  }

  render() { // render方法,返回要渲染的UI
    return (
      <div>
        <p>Count: {this.state.count}</p> // 显示count状态的值
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Increment // 按钮显示"Increment"
        </button>
      </div>
    );
  }
}

5. 事件处理

React使用事件处理函数处理用户交互。

javascript 复制代码
class Toggle extends Component { // 定义一个名为Toggle的类组件,继承自React.Component
  constructor(props) { // 构造函数,初始化组件状态
    super(props); // 调用父类Component的构造函数
    this.state = { isToggleOn: true }; // 初始化组件状态,isToggleOn属性初始值为true

    // 为确保this指向正确,绑定事件处理函数
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() { // 定义点击事件处理函数
    this.setState(prevState => ({ // 更新状态,使用prevState获取之前的状态
      isToggleOn: !prevState.isToggleOn // 将isToggleOn状态取反
    }));
  }

  render() { // render方法,返回要渲染的UI
    return (
      <button onClick={this.handleClick}> // 按钮的点击事件绑定到handleClick方法
        {this.state.isToggleOn ? 'ON' : 'OFF'} // 根据isToggleOn状态显示"ON"或"OFF"
      </button>
    );
  }
}

疑问:this.handleClick = this.handleClick.bind(this);多余吗?

在 React 类组件中,this.handleClick = this.handleClick.bind(this); 不是多余的 ,而是必要的。以下是详细解释:


为什么需要绑定?

在 JavaScript 中,类方法默认不会自动绑定 this 。当我们将方法作为事件处理函数(如 onClick={this.handleClick})传递时:

  • 事件触发时,handleClick 会被调用,但 this 的指向会丢失 (指向 undefinedwindow,取决于上下文)。
  • 例如:this.setState 会因 thisundefined 而报错。

为什么这行代码是必要的?
javascript 复制代码
this.handleClick = this.handleClick.bind(this); // 绑定 this 指向组件实例
  • 作用 :确保 handleClick 方法中的 this 指向组件实例(Toggle 的实例)。
  • 如果不绑定this.setState 会失败(thisundefined)。

对比其他写法(为什么这行代码是正确方式)
❌ 错误写法(不绑定):
javascript 复制代码
render() {
  return <button onClick={this.handleClick}>...</button>; // this.handleClick 中的 this 为 undefined
}
✅ 正确写法(构造函数中绑定):
javascript 复制代码
constructor(props) {
  super(props);
  this.state = { isToggleOn: true };
  this.handleClick = this.handleClick.bind(this); // 必须在这里绑定
}
✅ 替代方案(类属性 + 箭头函数,ES7 语法):
javascript 复制代码
class Toggle extends Component {
  handleClick = () => { // 自动绑定 this
    this.setState(...);
  };
  // ...其他代码
}

注意 :这是 ES7 语法(类属性),不是类方法。如果使用普通方法(非箭头函数),必须手动绑定


为什么说"不多余"?
  • 性能 :在构造函数中绑定一次(this.handleClick = ...)比在 render 中每次渲染都绑定(onClick={this.handleClick.bind(this)}更高效
  • 正确性 :确保 this 在组件生命周期中始终指向组件实例。

为什么有人觉得"多余"?
  1. 误解 ES6 类方法 :误以为类方法会自动绑定 this实际不会)。
  2. 混淆箭头函数 :如果写成 handleClick = () => { ... }(类属性),则无需绑定,但原始代码是普通方法handleClick() { ... }),所以必须绑定。

结论

this.handleClick = this.handleClick.bind(this); 是必要的,不是多余的。

它确保了事件处理函数中的 this 指向组件实例,是 React 类组件的标准实践。

推荐写法(构造函数绑定):

javascript 复制代码
constructor(props) {
  super(props);
  this.state = { isToggleOn: true };
  this.handleClick = this.handleClick.bind(this); // 必须写
}

替代写法(ES7 类属性):

javascript 复制代码
handleClick = () => { // 自动绑定,无需 bind
  this.setState(...);
}

原始代码中的写法是正确且标准 的,不是多余的

疑问:为什么 this 会丢失?

在 JavaScript 中,this 的指向取决于函数的调用方式,而不是定义方式。这是理解这个问题的关键。

详细解释
1. 为什么 this 会丢失?(看不懂,尴尬😭测也测不出来。。。)

当我们在 React 中这样写:

javascript 复制代码
<button onClick={this.handleClick}>Toggle</button>

React 实际上是这样调用的:

javascript 复制代码
// 伪代码
button.addEventListener('click', this.handleClick);

当按钮被点击时,React 会调用 this.handleClick,但调用方式是作为普通函数调用,而不是作为对象的方法调用。

在 JavaScript 中:

  • 如果函数是作为对象的方法调用(obj.method()),this 指向 obj
  • 如果函数是作为普通函数调用(method()),this 指向全局对象(浏览器中是 window)或 undefined(严格模式下)

所以当 handleClick 通过 onClick 被调用时:

  • 它不是通过 this.handleClick() 调用的
  • 而是通过 handleClick() 调用的
  • 因此 this 会丢失,指向 undefinedwindow
2. 为什么在构造函数中绑定是正确的方式?
javascript 复制代码
constructor(props) {
  super(props);
  this.state = { isToggleOn: true };
  this.handleClick = this.handleClick.bind(this); // 绑定 this
}

这行代码的作用是:创建一个新的函数,将 this 指向组件实例 。这样,无论 handleClick 如何被调用,this 都会指向组件实例。

3. 一个简单示例说明
javascript 复制代码
class Example {
  constructor() {
    this.name = "Example";
    // 不绑定 this
    this.greet = this.greet.bind(this);
  }

  greet() {
    console.log("Hello, " + this.name);
  }

  run() {
    // 普通函数调用
    const fn = this.greet;
    fn(); // 输出: Hello, undefined (this 丢失)
  }
}

const ex = new Example();
ex.run(); // 会输出 "Hello, undefined"

如果我们在构造函数中绑定了 this

javascript 复制代码
constructor() {
  this.name = "Example";
  this.greet = this.greet.bind(this); // 绑定 this
}

那么 fn() 会正确输出 "Hello, Example"。

为什么不是多余的?

在 React 中,事件处理函数的 this 丢失是标准行为,不是 React 特有的问题。这是 JavaScript 语言本身的特性。

  • 如果不绑定,this.setState 会失败(因为 thisundefined
  • 在构造函数中绑定一次,比每次渲染都绑定(如 onClick={this.handleClick.bind(this)}更高效

所以,this.handleClick = this.handleClick.bind(this); 不是多余的 ,而是 React 类组件中必须 的步骤,确保事件处理函数中的 this 指向组件实例。

6. Hooks

React Hooks是React 16.8引入的新特性,允许在函数组件中使用状态和其他React特性。

useState

javascript 复制代码
import React, { useState } from 'react'; // 从React库中导入useState Hook

function Example() { // 定义一个函数组件Example
  // 声明一个名为"count"的state变量,初始值为0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p> // 显示点击次数
      <button onClick={() => setCount(count + 1)}> // 点击按钮时,将count加1
        Click me
      </button>
    </div>
  );
}

useEffect

javascript 复制代码
import React, { useState, useEffect } from 'react'; // 从React库中导入useState和useEffect Hook

function Example() { // 定义一个函数组件Example
  const [count, setCount] = useState(0); // 声明count状态变量

  // 类似于 componentDidMount 和 componentDidUpdate:
  useEffect(() => { // useEffect Hook,用于在组件渲染后执行副作用
    // 更新文档标题
    document.title = `You clicked ${count} times`; // 设置文档标题为"你点击了count次"
  }, [count]); // 仅在count变化时执行

  return (
    <div>
      <p>You clicked {count} times</p> // 显示点击次数
      <button onClick={() => setCount(count + 1)}> // 点击按钮时,将count加1
        Click me
      </button>
    </div>
  );
}



七、高阶组件

高阶组件(HOC)是React中一个重要的概念,它本质上是一个函数,接收一个组件并返回一个新的增强版组件。

代理方式高阶组件

javascript 复制代码
function withLogger(WrappedComponent) { // 定义一个高阶组件withLogger,接收一个组件作为参数
  return class extends React.Component { // 返回一个新的组件
    componentDidMount() { // 组件挂载完成后执行
      console.log(`Component ${WrappedComponent.name} mounted`); // 在控制台打印组件名称
    }

    render() { // 返回渲染的组件
      return <WrappedComponent {...this.props} />; // 将传入的props传递给被包装的组件
    }
  };
}

使用方式

javascript 复制代码
const EnhancedComponent = withLogger(Welcome); // 使用withLogger高阶组件包装Welcome组件

八、实战:创建一个简单的组件

javascript 复制代码
import React from 'react'; // 从React库中导入React对象
import ReactDOM from 'react-dom'; // 从react-dom库中导入ReactDOM对象

class App extends React.Component { // 定义一个名为App的类组件,继承自React.Component
  constructor(props) { // 构造函数,初始化组件状态
    super(props); // 调用父类Component的构造函数
    this.state = { // 初始化组件状态
      message: 'Hello, React!' // message状态初始值为"Hello, React!"
    };
  }

  handleClick = () => { // 定义点击事件处理函数
    this.setState({ // 更新状态
      message: 'Button clicked!' // 将message状态更新为"Button clicked!"
    });
  }

  render() { // render方法,返回要渲染的UI
    return (
      <div> // 返回一个div容器
        <h1>{this.state.message}</h1> // 显示message状态的值
        <button onClick={this.handleClick}>Click Me</button> // 按钮的点击事件绑定到handleClick方法
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root')); // 将App组件渲染到id为"root"的DOM元素中

九、学习资源推荐

1. 官方文档:React官网提供了最权威的文档和教程。

2. React视频教程:《React极速入门指南》、《Vue、Angular、React 项目开发与深度对比》。

3. 实践项目:通过构建实际项目(如电商网站、社交应用)来巩固知识。

4. 社区支持:React社区活跃,Stack Overflow、GitHub等平台有大量问题解答。

十、结语

React作为当今最流行的前端框架之一,以其组件化、声明式和高性能的特点,成为构建现代Web应用的首选。虽然学习曲线比Vue稍高,但掌握React将为你的前端开发技能带来巨大提升。

随着React生态系统的不断发展(如Redux、React Router、Hooks等),React的应用场景越来越广泛。无论你是前端新手还是有经验的开发者,学习React都将为你的职业发展带来重要价值。

开始你的React之旅,用组件化思维构建更高效、更可维护的Web应用吧!

相关推荐
PineappleCoder4 小时前
前端水印收官篇:ECharts 图表水印实战 + Ant Design Vue/Canvas 方案选型指南
前端·echarts
速易达网络5 小时前
Nodejs+html+mysql实现轻量web应用
前端·mysql·html
05Nuyoah5 小时前
Day 02 HTML的基础
前端·javascript·css·html·firefox·jquery·html5
不宕机的小马达5 小时前
【Web前端|第一篇】HTML、CSS与JavaScript
前端·css·html
路光.5 小时前
统一配置管理根据不同域名展现不同信息或相近信息 Vue3类单例模式封装
前端·单例模式·typescript·vue3
一点一木5 小时前
⚡ GitHub 热榜速报 | 2025 年 09 月 第 3 周
前端·人工智能·github
GISer_Jing6 小时前
携程HR面(准备)
前端·javascript·面试
Larry_Yanan6 小时前
QML学习笔记(五)QML新手入门其三:使用Row和Colunm进行简单布局
前端·笔记·qt·学习·ui