当前位置: 首页 > article >正文

【超全】React学习笔记 上:基础使用与脚手架

React学习笔记

React系列笔记学习
中篇笔记地址:【超全】React学习笔记 中:进阶语法与原理机制
下篇笔记地址:【超全】React学习笔记 下:路由与Redux状态管理

React 简介

React 是一个由 Facebook 开发并维护的用于构建用户界面的 JavaScript 库。它被广泛用于构建单页应用(SPA),尤其是复杂的交互式用户界面。React 允许开发人员构建 Web 应用程序,这些程序可以在数据更改时自动更新和渲染,而无需重新加载页面。

核心特点:

  1. 组件化

    • React 通过组件化的方式来构建界面,每个组件都是一个独立的、可复用的代码块,负责自己的渲染逻辑。
    • 组件可以嵌套、组合,形成组件树,以构建复杂的用户界面。
  2. 声明式编程

    • React 采用声明式编程方式,允许开发者通过描述结果来编写UI,而不是描述过程。
    • 当数据变更时,React 会自动计算最小的变更集并应用到DOM上,使得代码更易读、易维护。
  3. 虚拟DOM(Virtual DOM)

    • React引入了虚拟DOM的概念,它是一个轻量级的DOM树的副本,使得React能够最小化直接与实际DOM的交互,从而提高性能。
  4. 单向数据流

    • 数据在React中从上到下单向流动,使得组件之间的数据流清晰、直观。

编程思想:

  1. 组件生命周期

    • React组件有自己的生命周期,包括创建、更新和销毁等阶段。在这些不同的阶段,开发者可以通过特定的生命周期方法来控制组件的行为。
  2. 状态管理(State & Props)

    • React组件的状态管理是React编程的核心部分。组件的状态(state)和属性(props)是驱动组件渲染和行为的数据源。
  3. 事件处理

    • React中的事件处理机制允许在组件中捕获和处理用户和系统事件。
  4. 条件渲染和列表渲染

    • React提供了强大的条件渲染和列表渲染功能,使得开发者可以轻松地实现动态界面。

示例:

import React from 'react';
import ReactDOM from 'react-dom';

// 创建一个组件
class HelloWorld extends React.Component {
  render() {
    return (
      <div>Hello, World!</div>
    );
  }
}

// 渲染组件到页面
ReactDOM.render(<HelloWorld />, document.getElementById('root'));

在这个示例中,我们创建了一个简单的 HelloWorld 组件,并使用 ReactDOM.render 方法将其渲染到页面上。React通过组件化的方式使得开发者能够以模块化和可维护的方式构建应用程序,同时通过声明式编程、虚拟DOM和单向数据流等特点,使得开发高效、简洁和高性能的应用成为可能。

React的基本使用

创建和渲染React元素是React应用的基础。下面是这两个方面的介绍和示例代码:

1. 创建React元素:(了解学习)

React元素是构建React应用的最小单位。你可以使用React.createElement方法来创建一个React元素。此方法接受三个参数:

  • type(类型):元素的类型,例如'div''span'等,或者一个React组件。
  • props(属性):一个对象,包含传递给该元素的属性和值。
  • children(子元素):该元素的子元素,可以是文本、React元素或数组。
  • childrens(更多子元素):这个可以放多个createElement,会被一桶方进入去
const element = React.createElement(
  'h1',
  { className: 'greeting' },
  'Hello, world!'
);

2. JSX(可选):

除了React.createElement,你还可以使用JSX来创建React元素,它是一种JavaScript的语法扩展,看起来更像是XML或HTML。使用JSX通常会使你的代码更易读和编写。

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

3. 渲染React元素:

将React元素渲染到DOM中,你可以使用ReactDOM.render方法。此方法接受两个参数:

  • element(元素):你想要渲染的React元素。
  • container(容器):你想要将React元素渲染到的DOM容器。
const container = document.getElementById('root');
ReactDOM.render(element, container);

示例代码:

将以下代码放入你的HTML文件中,你会看到一个显示"Hello, world!"的标题元素。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Example</title>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/javascript">
  const element = <h1 className="greeting">Hello, world!</h1>;
  const container = document.getElementById('root');
  ReactDOM.render(element, container);
</script>

</body>
</html>

在这个示例中,我们首先加载了React和ReactDOM库,然后创建了一个名为root<div>元素,作为我们React应用的根元素。接下来,我们创建了一个React元素,并使用ReactDOM.render方法将其渲染到root元素中。

JSX概念与理解使用

JSX的基本使用:

JSX是JavaScript XML的缩写,它允许你在JavaScript中写HTML。表示在JavaScript代码中写XML ( HTML)格式的代码。优势︰声明式语法更加直观、与HTML结构相同,降低了学习成本、提升开发效率。这种语法糖可以使你的代码更容易编写和阅读,并且React元素通常是通过JSX创建的。

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

为什么学习JSX

createElement()的问题

  1. 繁琐不简洁。

  2. 不直观,无法一眼看出所描述的结构。

  3. 不优雅,用户体验不爽。

React的React.createElement函数虽然功能强大,但在创建复杂节点时可能会显得非常繁琐和不直观。

以下是一个例子,说明通过React.createElement创建一个包含多个元素和文本的复杂节点的过程,以及同样的节点如何通过JSX轻松创建。

使用React.createElement创建复杂节点:

const complexElement = React.createElement(
  'div',
  { className: 'container' },
  React.createElement(
    'h1',
    null,
    'Hello, world!'
  ),
  React.createElement(
    'p',
    null,
    'This is a complex element.'
  ),
  React.createElement(
    'ul',
    null,
    [1, 2, 3, 4, 5].map((number, index) =>
      React.createElement(
        'li',
        { key: index },
        number
      )
    )
  )
);

ReactDOM.render(
  complexElement,
  document.getElementById('root')
);

在上面的代码中,我们尝试创建一个包含标题、段落和列表的容器。你可以看到,代码非常繁琐和嵌套,很难一眼看出元素的结构。

使用JSX创建相同的复杂节点:

const complexElementJSX = (
  <div className="container">
    <h1>Hello, world!</h1>
    <p>This is a complex element.</p>
    <ul>
      {[1, 2, 3, 4, 5].map((number, index) => (
        <li key={index}>{number}</li>
      ))}
    </ul>
  </div>
);

ReactDOM.render(
  complexElementJSX,
  document.getElementById('root')
);

在上面的JSX版本中,代码更简洁、更直观、更易读。你可以很容易地看出元素的结构,而不需要通过多层嵌套的React.createElement调用来理解。这显示了为什么学习和使用JSX是有用的,它可以大大简化React元素的创建和渲染过程,提高开发效率和代码的可读性。

使用细节
  1. 驼峰命名法:
    在JSX中,所有的React元素属性名应使用驼峰命名法 (camelCase),而不是HTML中的短横线命名法 (kebab-case)。例如,HTML中的tabindex在JSX中应该写为tabIndex

    // 正确
    <input type="text" readOnly={true} />
    
    // 错误
    <input type="text" read-only={true} />
    
  2. 特殊属性名:

    当您在JSX中创建React元素时,有一些HTML属性名需要以特殊的JSX属性名来代替。这是因为classfor,和tabindex在JavaScript中是保留字。下面是一些例子来说明这点:

    1. class -> className:
      在HTML中,我们用class属性来指定CSS类名,但在JSX中,我们应该用className来代替。

      // HTML
      <div class="app">Hello, world!</div>
      
      // JSX
      <div className="app">Hello, world!</div>
      
    2. for -> htmlFor:
      在HTML中,for属性用于将<label>元素关联到<input>元素。但在JSX中,我们应该用htmlFor来代替。

      // HTML
      <label for="username">Username:</label>
      <input id="username" type="text" />
      
      // JSX
      <label htmlFor="username">Username:</label>
      <input id="username" type="text" />
      
    3. tabindex -> tabIndex:
      在HTML中,tabindex属性用于指定元素的tab顺序。但在JSX中,我们应该用tabIndex来代替。

      // HTML
      <input tabindex="1" type="text" />
      
      // JSX
      <input tabIndex="1" type="text" />
      

    以上的例子明确了在JSX中使用特殊属性名的重要性,以及如何正确地使用它们。

  3. 自闭合标签:
    没有子节点的React元素可以用/>结束,这与XML和HTML中的自闭合标签类似。

    1. 没有子节点的React元素:
      当一个React元素没有子节点时,您可以使用自闭合标签的形式来定义它。这种形式类似于XML和HTML中的自闭合标签,它以/>结束,而不是使用一个单独的结束标签。

      // 正确
      <input type="text" />
      <br />
      <hr />
      
    2. 具有子节点的React元素:
      对于具有子节点的React元素,您应该使用一个开始标签和一个结束标签来定义它,而不是使用自闭合标签的形式。

      // 正确
      <div>
        <p>Hello, world!</p>
      </div>
      
      // 错误
      <div />
      

    在上面的例子中,<input>, <br>, 和 <hr> 元素没有子节点,所以它们是以自闭合标签的形式定义的。而<div>元素有一个子节点<p>,所以它是以一个开始标签和一个结束标签的形式定义的。在第二个错误的示例中,<div />是一个自闭合标签,但是它应该包含一个子节点<p>,所以应该使用一个开始标签和一个结束标签来定义它。

  4. 使用小括号包裹JSX:
    当JSX元素跨越多行时,推荐使用小括号将其包裹起来。这样做可以避免JavaScript的自动分号插入(ASI)机制在意料之外的地方插入分号,导致意想不到的结果。

    // 推荐
    const element = (
      <div>
        <p>Hello, world!</p>
      </div>
    );
    
    // 不推荐
    const element = 
      <div>
        <p>Hello, world!</p>
      </div>;
    

以上的细节和规范可以帮助开发者在编写JSX代码时保持清晰和一致,避免常见的错误和陷阱。

小结
  • 推荐使用JSX语法创建React元素
  • 写JSX就跟写HTML一样,更加直观、友好
  • JSX语法更能体现React的声明式特点(描述UI长什么样子)

使用步骤︰

const title = <h1>Hello JSX<h1>
ReactDOM.render(title, root);

为什么脚手架create react-app能使用JSX语法?

  • JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展。
  • 需要使用babel编译处理后,才能在浏览器环境中使用。
  • create-react-app脚手架中已经默认有该配置,无需手动配置。
  • 编译JSX语法的包为:@babel/preset-react。

JSX中使用JavaScript表达式:

你可以在JSX中的大括号内放入任何有效的JavaScript表达式。例如,2 + 2user.firstNameformatName(user)都是有效的JavaScript表达式。

语法∶ {JavaScript表达式}

注意∶语法中是单大括号,不是双大括号! 不要与Vue混淆!

在JSX中,可以使用单大括号 {} 来包裹JavaScript表达式。在大括号内,您可以放置任何有效的JavaScript表达式。表达式是计算并产生一个值的代码片段。例如,2 + 2user.firstNameformatName(user) 都是JavaScript表达式。

  1. 单大括号中可以使用任意的JavaScript表达式

在JSX中,您可以在大括号中放置任何JavaScript表达式。例如:

const element = <h1>Hello, {10 + 20}</h1>;

在这个例子中,10 + 20 是一个JavaScript表达式,它的结果是 30,所以输出的HTML将是 <h1>Hello, 30</h1>

  1. JSX自身也是JS表达式

JSX表达式本身也是JavaScript表达式,这意味着您可以在if语句、for循环、赋值、参数、运算符等JavaScript代码结构中使用JSX。

const dv = <div>Hello</div>;
const rt_value = (
	<div>嵌入表达式: {dv}</div>
)
  1. 注意: JS中的对象是一个例外,一般只会出现在style属性中

虽然您可以在JSX中的大括号内放置任何表达式,但直接放置一个对象字面量是个例外。如果您尝试这样做,JSX会将其解释为多个单独的表达式而不是一个对象表达式。所以,如果您想传递一个对象,您应该包裹它在圆括号里。

例如,以下代码是错误的:

const element = <h1 style={ background: 'blue', color: 'white' }>Hello World!</h1>;

正确的做法应该是:

const element = <h1 style={{ background: 'blue', color: 'white' }}>Hello World!</h1>;
  1. 注意: 不能在JSX中出现语句(比如: if/for等)

在JSX的大括号内,您不能使用语句,比如iffor。但您可以在外面的JavaScript代码中使用它们,或者使用JavaScript的表达式语法来实现相同的效果。

例如,您不能直接在JSX中这样做:

// 错误
const element = <h1>{if (user) {return user.firstName}}</h1>;

但您可以这样做:

// 正确
const element = <h1>{user ? user.firstName : 'Stranger'}</h1>;

在这个例子中,我们使用了条件(三元)运算符来实现条件渲染。

JSX的条件渲染:

条件渲染是根据某些条件动态显示不同的UI结构。在React中,你可以使用JavaScript的条件语句(如 ifelse)或表达式(如三元运算符 ? : 或逻辑与运算符 &&)来实现条件渲染。

  1. 使用 if / else 实现条件渲染:

在React中,你可以在函数的外部或内部使用 if / else 语句来决定渲染哪些组件。

function LoadingIndicator(props) {
  if (props.isLoading) {
    return <div>Loading...</div>;
  } else {
    return <div>Content loaded</div>;
  }
}

ReactDOM.render(
  <LoadingIndicator isLoading={true} />,
  document.getElementById('root')
);

在上述代码中,如果 props.isLoadingtrue,将渲染显示 “Loading…” 的 div。否则,将渲染显示 “Content loaded” 的 div

  1. 使用三元运算符实现条件渲染:

你也可以使用三元运算符 ? : 在 JSX 内直接进行条件渲染。

function LoadingIndicator(props) {
  return (
    <div>
      {props.isLoading ? 'Loading...' : 'Content loaded'}
    </div>
  );
}

ReactDOM.render(
  <LoadingIndicator isLoading={true} />,
  document.getElementById('root')
);
  1. 使用逻辑与运算符 && 实现条件渲染:

逻辑与运算符 && 是一个简洁的方法,用于在满足某个条件时渲染某些元素。

function LoadingIndicator(props) {
  return (
    <div>
      {props.isLoading && 'Loading...'}
    </div>
  );
}

ReactDOM.render(
  <LoadingIndicator isLoading={true} />,
  document.getElementById('root')
);

在上述代码中,如果 props.isLoadingtrue,则会渲染显示 “Loading…”。否则,如果 props.isLoadingfalse,则不渲染任何内容(返回 null)。

JSX的列表渲染:

列表渲染是在React中很常见的操作。通常你会有一个数组的数据,你需要将其转换为一个由React元素组成的数组,然后将它们显示在UI中。

1. 使用 Array.prototype.map() 方法渲染列表:

map() 方法是在JavaScript数组中处理列表数据的常用方式。它会遍历数组的每个元素,并返回一个新的数组,该数组由对原数组的每个元素应用某个函数得到的结果组成。

在React中,你可以使用 map() 方法将一个数据数组转换为一个元素数组,并使用 key 属性来保证每个元素具有稳定的标识。

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

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);
// 或则
const user_info = (
	{id : 1, name : 'caixy'},
    {id : 2, name : 'Caixy!!!'},
    {id : 3, name : 'CAIXYPROMISE'}
)
const list = (
	<ul>
    	{user_info.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
)

注意:

  • key 属性是React用来识别哪些元素改变了的重要标识。每个元素的 key 值应该是唯一的。
  • 应避免使用数组索引作为 key 值,除非列表不会重新排序、不会过滤、不会删除或添加新元素。因为当元素顺序改变时,索引也会改变,这可能会导致不必要的组件重新渲染和状态丢失。
  • key 属性的值应该是稳定、可预测和唯一的,以便React能够匹配元素与其对应的状态。

嵌套组件中的列表渲染:

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

const numbers = [1, 2, 3, 4, 5];

ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

JSX的样式处理:

在React和JSX中,可以通过几种不同的方法来处理样式。以下是两种常见的方法。

  1. 行内样式

在React中,行内样式不是作为字符串,而是作为一个对象来使用的。样式属性的名称采用驼峰命名法(camelCase),而不是短划线命名法。此外,属性值作为字符串提供。下面是一个示例:

const divStyle = {
  color: 'blue',
  backgroundColor: 'lightgray',
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

ReactDOM.render(
  <HelloWorldComponent />,
  document.getElementById('root')
);

在上述代码中,我们首先创建了一个名为 divStyle 的对象,该对象包含我们想要应用到 div 元素上的样式属性。然后,我们通过 style 属性将 divStyle 对象传递给 div 元素。

  1. 类名

可以使用 className 属性为React元素指定CSS类。className 属性接受一个字符串,该字符串包含一个或多个类名。下面是一个示例:

function HelloWorldComponent() {
  return <div className="hello-world">Hello World!</div>;
}

ReactDOM.render(
  <HelloWorldComponent />,
  document.getElementById('root')
);

// CSS:
// .hello-world {
//   color: blue;
//   background-color: lightgray;
// }

在上述代码中,我们为 div 元素指定了一个 className 属性,该属性的值是 hello-world。然后,我们在CSS中定义了一个 .hello-world 类,该类应用了我们想要的样式。

注意

  • 由于 class 是JavaScript中的保留字,因此在JSX中应使用 className 而不是 class
  • 在行内样式中,应使用驼峰命名法而不是短划线命名法来命名样式属性。例如,应使用 backgroundColor 而不是 background-color

JSX小结

在React的世界里,JSX扮演了非常重要的角色,它为我们提供了一种在JavaScript中书写HTML的方式,降低了学习曲线,同时提升了开发效率。通过对JSX的使用,我们能够快速且直观地创建React元素,使得代码更为简洁和易读。下面是对前面内容的简单总结:

  1. JSX基础:
  • JSX是JavaScript XML的简写,它允许我们在JavaScript代码中写HTML。
  • 使用JSX比React.createElement更简洁、直观和优雅,特别是在创建复杂节点时。
  • 示例: const element = <h1>Hello, world!</h1>;
  1. 细节使用:
  • 驼峰命名法: JSX中的属性名应使用驼峰命名法。
  • 特殊属性名: 如class应写为classNamefor应写为htmlFortabindex应写为tabIndex
  • 自闭合标签: 没有子节点的React元素可以用/>来结束。
  • 小括号包裹: 推荐使用小括号包裹多行的JSX,避免JavaScript的自动分号插入机制带来的问题。
  1. JavaScript表达式在JSX中的使用:
  • 在JSX中可以使用单大括号{}包裹任意的JavaScript表达式。
  • JSX自身也是JavaScript表达式。
  • 注意: 不能在JSX中使用语句如iffor等。
  1. 条件渲染:
  • 通过if/else, 三元运算符? : 或逻辑与运算符&&实现条件渲染。
  1. 列表渲染:
  • 使用数组的map()方法渲染列表,并为每个元素提供唯一的key属性值。
  • 注意: 尽量避免使用数组索引作为key值。
  1. 样式处理:
  • 行内样式: 通过对象的形式传递样式,属性名采用驼峰命名法。
  • 类名: 使用className属性指定CSS类。
  1. 为什么能使用JSX:
  • JSX不是标准的ECMAScript语法,需要通过Babel编译处理后才能在浏览器环境中运行。
  • create-react-app脚手架中已经默认包含了这种配置,使得我们能够直接使用JSX语法。

通过上述的详细介绍和示例,应该能够对JSX有一个全面且深入的理解,知道如何在React项目中利用JSX来创建和渲染UI结构,以及如何处理常见的使用细节和常见问题。这为接下来的React学习和开发打下了坚实的基础。

React基础组件概念与使用

1. React 组件介绍

在React的世界中,组件是核心的构建块,它们是React应用的基础。通过组件,我们可以将UI拆分成独立可重用的片段,每个片段都可以单独思考。这种模块化的设计方式不仅提高了代码的可维护性,同时也为复杂的应用开发提供了清晰的结构。

1.1 组件的基本概念:
  1. 组件是React的一等公民:

    • 组件是React框架中最基本的单位,所有的React应用都是由一个或多个组件组成的。
    • 每个组件都是一个独立的模块,它包含了一些特定的功能和显示逻辑。
  2. 使用React就是在用组件:

    • 开发React应用的过程,实际上就是一个不断地创建组件和组装组件的过程。
    • React通过组件提供了一种高效的方式来构建和组织代码,使得代码结构清晰、易于维护。
  3. 组件表示页面中的部分功能:

    • 组件通常对应页面上的某个视觉元素或功能模块,例如按钮、表单、导航条等。
    • 通过组件,我们可以将复杂的页面拆分成更小、更管理的部分。
  4. 组合多个组件实现完整的页面功能:

    • 组件可以嵌套和组合使用,通过将简单的组件组合成更复杂的组件,我们可以构建出完整的页面。
    • 组件的组合提供了极大的灵活性,使我们能够以模块化的方式构建复杂的应用。
  5. 特点: 可复用、独立、可组合:

    • 可复用: 组件是可复用的,同一个组件可以在多个地方使用,降低了代码重复度,提高了开发效率。
    • 独立: 每个组件是独立的,它有自己的状态和逻辑,可以独立运行和测试。
    • 可组合: 组件可以与其他组件组合使用,创建复杂的UI结构和功能。

通过理解和运用组件的这些特点,我们可以更好地组织和管理React应用的代码,同时也能更高效地开发和维护复杂的应用程序。

2. React组件的两种创建方式

在React中,有两种主要的组件创建方式,分别是通过函数和通过类来创建组件。每种方法都有其特点和用途,下面将分别介绍这两种方法的基本使用和特点。

2.1 使用函数创建组件

使用函数来创建组件是React中最常见也最简单的组件创建方式。通过定义一个JavaScript函数,并返回一些 JSX,我们就能创建一个简单的React组件。这种类型的组件通常被称为“函数组件”。

函数组件是React的基础,它们提供了一种简单、清晰和易于理解的方式来创建组件。随着React的发展,函数组件通过Hooks得到了极大的增强,使得它们不仅仅是表现组件,还能拥有状态和其他复杂功能,但这已经超越了基本的函数组件范畴。

特点:

  1. 函数组件必须有返回值: 函数组件必须返回React元素,这可以是一个JSX表达式,也可以是nullfalse来表示不渲染任何内容。
  2. 组件名称必须以大写字母开头: 这是React的命名规范,以大写字母开头的名称表示它是一个组件,而不是普通的React元素或者JavaScript对象。React会根据这个规则来确定如何处理和渲染这个实体。
  3. 使用函数名作为组件标签名: 在使用函数组件时,应使用函数的名称作为组件的标签名。例如,上面的Hello组件应该用<Hello />标签来使用。

基本语法

function Hello() {
  return (
    <div>这是我的第一个函数组件!</div>
  );
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);
2.2 使用类创建组件

类组件是利用ES6的class语法创建的组件,它提供了更多的功能和灵活性,特别是在处理组件的状态和生命周期时。下面是创建类组件的基本约定和步骤:

  • 类名称的约定:
    1. 类名称也必须以大写字母开头: 类似于函数组件,类组件的名称也必须以大写字母开头,以区分React组件和普通的JavaScript类。
  • 继承的约定: 2. 类组件应该继承React.Component父类: 通过继承React.Component,类组件可以获得React为组件提供的方法和属性,这是创建类组件的基础。
  • render方法的约定: 3. 类组件必须提供render()方法: render方法是类组件的核心,它定义了组件的输出。每当组件的propsstate发生变化时,render方法都会被调用。
    1. render()方法必须有返回值: render方法应返回一个React元素,该元素描述了组件在页面上的表现。这个返回值可以是JSX表达式、一个React元素、nullfalse(表示不渲染任何内容)。

下面是一个简单的类组件创建示例:

class Hello extends React.Component {
  render() {
    return <div>Hello class Component!</div>;
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

在上述示例中,我们创建了一个名为Hello的类组件,该组件继承了React.Component。我们为Hello组件定义了一个render方法,该方法返回了一个简单的JSX表达式。最后,我们使用ReactDOM.render方法将Hello组件渲染到页面上。

这个简单的示例展示了创建和使用类组件的基本步骤。通过继承React.Component和实现render方法,我们可以创建具有更多功能和灵活性的组件。随着您对React的深入理解,您会发现类组件可以提供更多高级功能,例如状态管理、生命周期方法和错误边界处理等。

2.3 抽离为独立JS文件

随着项目规模的扩大和组件数量的增加,组织和管理这些组件变得至关重要。一种常见的做法是将每个组件放在一个单独的JS文件中,这样做有助于保持代码的清晰和模块化。下面是如何将组件抽离到独立JS文件中的步骤:

  1. 创建组件文件:

    • 创建一个新的JS文件,例如Hello.js,用于存放组件的代码。
  2. 导入React:

    • Hello.js文件的顶部,导入React库。这是因为组件需要React的支持才能正常工作。
      import React from 'react';
      
  3. 创建组件:

    • Hello.js文件中创建您的组件。您可以选择创建函数组件或类组件,具体取决于您的需求。
      function Hello() {
        return <div>Hello World!</div>;
      }
      // 或
      class Hello extends React.Component {
        render() {
          return <div>Hello World!</div>;
        }
      }
      
  4. 导出组件:

    • Hello.js文件的底部,导出您刚刚创建的组件。这样其他文件就可以导入和使用它了。
      export default Hello;
      
  5. 导入组件:

    • index.js或您想要使用该组件的文件中,导入Hello组件。
      import Hello from './Hello';
      
  6. 渲染组件:

    • 使用ReactDOM.render方法将Hello组件渲染到页面上。
      ReactDOM.render(
        <Hello />,
        document.getElementById('root')
      );
      

通过以上步骤,您已经将Hello组件从index.js文件中抽离出来,并放置在了一个单独的Hello.js文件中。这种组织方式不仅使代码结构更清晰,也更便于团队协作和代码维护。随着项目的发展,推荐将相关的组件组织到特定的目录和文件中,例如将所有与用户相关的组件放在一个名为users的目录中,每个组件有其单独的JS文件。

3. React 事件处理

3.1 事件绑定

在React中,事件处理程序的命名和DOM中稍有不同,但大体相似,主要体现在以下几点:

  1. 事件命名:

    • React事件的命名采用驼峰命名法,而非全小写。例如,onClick 代替 onclickonMouseEnter 代替 onmouseenter
  2. 事件绑定语法:

    • 在React中,事件绑定的语法是 on + 事件名称 = {事件处理程序}。例如,onClick={handleClick}
    • 注意,事件处理程序通常是一个函数的引用,而非函数调用(除非您想在事件触发时立即执行该函数)。

以下是在类组件和函数组件中绑定事件的例子:

类组件中的事件绑定:
class App extends React.Component {
  handleClick() {
    console.log('单击事件触发了');
  }

  render() {
    // 注意:事件名称为onClick,而非onclick
    // 并且事件处理函数为this.handleClick,而非this.handleClick()
    return (
      <button onClick={this.handleClick}></button>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

在上述代码中,我们创建了一个名为 handleClick 的方法来处理按钮的点击事件,并使用 onClick 属性将其绑定到按钮上。

函数组件中的事件绑定:
function App() {
  function handleClick() {
    console.log('单击事件触发了');
  }

  // 注意:事件名称为onClick,而非onclick
  return (
    <button onClick={handleClick}>点我</button>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

在上述代码中,我们在函数组件内部定义了一个名为 handleClick 的函数来处理按钮的点击事件,并使用 onClick 属性将其绑定到按钮上。

通过这些例子,您可以看到在类组件和函数组件中绑定事件的方式是非常相似的。最主要的区别是在类组件中,事件处理函数通常是类的方法,而在函数组件中,事件处理函数通常是组件函数内部的函数。同时,注意React事件的命名和绑定语法,以确保事件能够正确触发。

3.2 事件对象

在React的事件处理程序中,可以通过参数获取到事件对象。React有自己的事件对象系统,称为合成事件(Synthetic Event),**它是对浏览器的原生事件的跨浏览器封装。这意味着React的事件对象具有统一的属性和方法,无论在哪个浏览器中运行,都能保证相同的行为。**这有助于处理跨浏览器兼容性问题,使得在不同浏览器中的事件处理变得更简单、更一致。

以下是一个简单的示例,展示了如何在事件处理函数中访问事件对象,并使用它来阻止默认行为:

function handleClick(e) {
  // 阻止默认行为,例如阻止链接的跳转
  e.preventDefault();

  // 输出事件对象
  console.log('事件对象:', e);

  // 输出事件类型
  console.log('事件类型:', e.type);
}

// 注意: 事件名称为onClick,而非onclick
// 事件处理函数为handleClick,而非handleClick()
return (
  <a href="#some-link" onClick={handleClick}>点我,不会跳转页面</a>
);

// 也可以直接在事件绑定时传递事件对象
return (
  <a href="https://www.baidu.com/" onClick={(e) => {
    e.preventDefault();
    console.log('事件对象:', e);
  }}>点我,不会跳转页面</a>
);

ReactDOM.render(
  <a href="#some-link" onClick={handleClick}>点我,不会跳转页面</a>,
  document.getElementById('root')
);

在上述代码中,我们创建了一个名为 handleClick 的函数,该函数接受一个参数 e,表示事件对象。在函数体内,我们使用 e.preventDefault() 方法阻止了链接的默认跳转行为,并通过 console.log 输出了事件对象和事件类型。最后,我们使用 onClick 属性将 handleClick 函数绑定到了链接元素上。

另外,我们也展示了如何在事件绑定时直接传递事件对象,并在箭头函数中处理事件对象。这种方式可以让我们在事件处理函数中直接访问事件对象,而无需创建一个单独的事件处理函数。这在处理简单事件时可能会很方便。

4. 有状态组件和无状态组件

在React中,组件分为有状态组件和无状态组件两大类。这个分类基于组件是否具有内部状态(state)。状态是React组件中存储数据的地方,它决定了组件的行为和显示。

4.1 无状态组件(函数组件)

无状态组件通常使用函数来创建,它仅依赖于外部传入的props来确定渲染结果,没有自己的状态(state),也没有生命周期方法。由于它们只是纯函数,无状态组件通常更简单、更容易理解和维护。以下是一个简单的无状态组件示例:

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

ReactDOM.render(
  <Greeting name="World" />,
  document.getElementById('root')
);

在上述代码中,Greeting 是一个无状态组件,它接受一个 props 对象作为参数,并返回一个React元素。无状态组件是纯函数,它们的输出完全由输入(即props)决定。函数无状态组件通常用在不需要与用户操作反馈的组件当中使用。

4.2 有状态组件(类组件)

有状态组件通常使用类来创建,并且拥有内部状态(state)和生命周期方法。有状态组件可以在内部管理数据,响应事件,并在数据变化时自动更新UI。所以,有状态的类组件通常用于需要与用户操作进行反馈的组件中去使用。以下是一个简单的计数器,可以作为 有状态组件示例:

class Counter extends React.Component {
  constructor() {
    super();
    // 初始化状态
    this.state = { count: 0 };
  }

  // 事件处理函数
  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById('root')
);

在上述代码中,Counter 是一个有状态组件,它具有内部状态 state,并定义了一个事件处理函数 handleIncrement 来处理按钮的点击事件。当按钮被点击时,handleIncrement 函数被调用,它通过 setState 方法更新组件的状态,从而引发组件的重新渲染,以显示新的计数值。

总结来说,无状态组件和有状态组件是React中两种主要的组件类型,无状态组件用于简单的、纯展示性的任务,而有状态组件用于更复杂的、交互式的任务。在设计React应用时,应根据组件的职责和需求来选择合适的组件类型。

5. 组件中的state和setState

5.1 state的基本使用

在React组件中,state是一个特殊的对象,它存储了组件的状态数据。state是组件内部私有的,不能从组件外部访问或修改。我们可以通过this.state来访问组件的状态,如下例所示:

class Hello extends React.Component { // 注意: 是 React.Component, 不是 React.component
  // 使用构造函数初始化状态
  constructor() {
    super();
    this.state = { count: 0 }; // 注意: 是 0, 不是 o
  }

  render() {
    return (
      <div>有状态组件,{this.state.count}</div>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

在上述代码中,我们首先通过constructor构造函数初始化state对象,并设置初始状态count为0。然后,在render方法中,我们通过this.state.count访问state对象中的count属性,并将其渲染到DOM中。

5.2 setState的基本使用

当我们需要修改组件的状态时,不能直接修改state对象,而应该使用this.setState方法。this.setState是异步的,它接受一个新的状态对象或一个函数作为参数,并安排一个组件更新。以下是一个简单的例子:

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

  // 事件处理函数
  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById('root')
);

在上述代码中,我们定义了一个事件处理函数handleIncrement,它通过this.setState方法更新count状态。每次点击按钮时,handleIncrement函数都会被调用,count状态会增加1,并触发组件重新渲染,以显示新的计数值。

总的来说,statesetState是React组件中管理和更新状态的核心机制。通过理解和掌握它们的使用,可以有效地管理组件的状态,并实现动态交互功能。

5.3 setState()修改状态

在React中,组件的状态是可以变化的,但是我们不能直接修改state对象的值,而应该通过this.setState()方法来修改状态。下面是setState方法的基本语法:

this.setState({
  // 要修改的数据
});
注意事项:
  1. 不要直接修改state中的值: 这是非常重要的,直接修改state是错误的做法,可能会导致未预期的结果和难以调试的问题。
    // 错误的做法!
    this.state.count += 1;
    
  2. setState()的作用: setState()方法主要有两个作用:
    • 修改state中的数据。
    • state数据被修改并且组件重新渲染后,更新UI界面。
数据驱动视图的思想

React框架遵循数据驱动视图的思想,即状态(state)的变化会自动驱动视图的更新。这种方式简化了UI编程,我们只需要关注数据的管理,不需要直接操作DOM元素。当数据发生变化时,React会负责计算最小的DOM操作,来保证UI的更新效率。

下面是一个setState的使用例子,展示了如何通过点击按钮来更新组件的状态,并且显示新的状态值:

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

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Counter />,
  document.getElementById('root')
);

在这个例子中,handleIncrement方法通过this.setState()来增加count的值,每次点击按钮时,count值会增加1,并且组件会重新渲染,显示新的count值。这个例子体现了React的数据驱动视图的思想,通过修改数据来自动更新视图。

6. 事件绑定和this指向

在React的类组件中,事件处理器的this指向可能会导致一些混淆。在JavaScript中,函数的this值是由调用它的上下文决定的,而不是由函数自身决定的。因此,当我们在类组件中定义事件处理器时,需要确保this指向正确。

1. 箭头函数

箭头函数不会创建自己的this值,它会捕获创建时所在的this值。因此,可以利用箭头函数来保证this指向的正确。下面是一个示例,展示了如何使用箭头函数来绑定事件处理器:

class Hello extends React.Component {
  onIncrement = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={this.onIncrement}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

在上述代码中,onIncrement方法是一个箭头函数,它会捕获类实例的this值。因此,当onIncrement方法被作为事件处理器调用时,this指向的是类实例,从而可以正确地调用this.setState方法来更新状态。

2. 在render方法中使用箭头函数

另一种方式是在render方法中使用箭头函数来创建事件处理器。以下是一个示例:

class Hello extends React.Component {
  onIncrement() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={() => this.onIncrement()}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

在这个例子中,我们在render方法中创建了一个新的箭头函数,该箭头函数调用onIncrement方法。由于箭头函数捕获了render方法的this值(也就是类实例的this值),因此onIncrement方法中的this指向是正确的。

这两种方式都可以保证事件处理器中this的正确指向,但是第一种方式(在类属性中使用箭头函数)通常更为推荐,因为它可以避免在每次渲染时创建新的函数对象,从而提高性能。

3. 使用 Function.prototype.bind

在ES5中,bind方法可以创建一个新的函数,该函数的this值被绑定到提供的值,以及可选的参数列表。在React的类组件中,可以在构造函数中使用bind方法,将事件处理程序的this值绑定到类实例上。以下是一个示例:

class Hello extends React.Component {
  constructor() {
    super();
    this.onIncrement = this.onIncrement.bind(this);
  }

  onIncrement() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={this.onIncrement}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

在上述代码中:

  1. Hello组件的构造函数中,通过调用this.onIncrement.bind(this)onIncrement方法的this值绑定到类实例上,并将绑定结果赋值回this.onIncrement
  2. render方法中,通过onClick={this.onIncrement}属性将onIncrement方法绑定到按钮的点击事件上。
  3. 当用户点击按钮时,onIncrement方法会被调用,并且this值指向的是类实例,从而可以正确地调用this.setState方法来更新状态。

通过使用bind方法,可以确保事件处理程序中this的正确指向,但是这种方式需要在构造函数中编写额外的代码。为了减少样板代码,很多开发者倾向于使用箭头函数来绑定事件处理程序。

4. 事件绑定和this指向总结

在React中,确保事件处理程序中this的正确指向是非常重要的,特别是在类组件中。下面是三种常见的处理this指向的方法,并给出了相应的代码示例。

  1. 使用类的实例方法

通过使用箭头函数定义类的实例方法,可以确保this指向类的实例。这是因为箭头函数不会创建自己的this上下文,而是从外围作用域继承this值。这是一种简洁且推荐的方法。

class Hello extends React.Component {
  onIncrement = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={this.onIncrement}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);
  1. 使用箭头函数

在事件处理属性中直接使用箭头函数也可以确保this的正确指向,但可能会导致每次组件渲染时都创建新的函数实例,这可能会影响性能。

class Hello extends React.Component {
  onIncrement() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={() => this.onIncrement()}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);
  1. 使用bind方法

通过在构造函数中使用bind方法,可以将事件处理程序的this值绑定到类实例上。这是一种传统的方法,但可能会增加样板代码的数量。

class Hello extends React.Component {
  constructor() {
    super();
    this.onIncrement = this.onIncrement.bind(this);
  }

  onIncrement() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }

  render() {
    return (
      <button onClick={this.onIncrement}>Increment</button>
    );
  }
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

以上三种方法根据个人和项目的需求可以选择使用,其中使用类的实例方法是比较现代且简洁的方法。

7. React 表单处理

在React中,表单处理可以通过两种主要方式来完成:受控组件和非受控组件。下面我们分别介绍这两种方式:

7.1 受控组件

受控组件是指其表单数据由React组件的state管理的表单元素。在受控组件中,表单元素的值由state决定,并通过事件处理程序更新。

步骤:

  1. 添加状态
    在组件的state中添加一个状态项,该状态项将作为表单元素的value值。
state = { txt: '' }
  1. 绑定事件
    给表单元素绑定一个change事件,当表单元素的值发生变化时,更新state的值。
<input 
  type="text" 
  value={this.state.txt} 
  onChange={e => this.setState({ txt: e.target.value })} 
/>

通过上述步骤,我们就创建了一个受控组件。在这个例子中,<input>元素的值始终由this.state.txt决定,每当用户输入时,onChange事件处理程序会被触发,从而更新this.state.txt的值。

  1. 完整示例

在受控组件中,表单元素的值由React组件中的state控制。以下是一个完整的实例,包括文本框、下拉框和复选框的操作:

import React, { Component } from 'react';

class FormExample extends Component {
  state = {
    textValue: '',
    selectedOption: '',
    isChecked: false
  };

  handleTextChange = (event) => {
    this.setState({ textValue: event.target.value });
  }

  handleSelectChange = (event) => {
    this.setState({ selectedOption: event.target.value });
  }

  handleCheckboxChange = (event) => {
    this.setState({ isChecked: event.target.checked });
  }

  render() {
    return (
      <div>
        {/* 文本框 */}
        <input 
          type="text" 
          value={this.state.textValue} 
          onChange={this.handleTextChange} 
        />

        {/* 下拉框 */}
        <select 
          value={this.state.selectedOption} 
          onChange={this.handleSelectChange}
        >
          <option value="option1">Option 1</option>
          <option value="option2">Option 2</option>
        </select>

        {/* 复选框 */}
        <input 
          type="checkbox" 
          checked={this.state.isChecked} 
          onChange={this.handleCheckboxChange} 
        />
      </div>
    );
  }
}

export default FormExample;

在以上示例中,通过定义状态state和事件处理函数,我们实现了对文本框、下拉框和复选框的受控操作。每当用户对表单元素进行操作时,对应的onChange事件处理函数就会被调用,从而更新state中的值,实现了数据与视图的同步更新。

  1. 文本框、富文本框、下拉框:通过操作value属性并绑定onChange事件处理函数,将表单元素的值设置为state的值,实现受控操作。

  2. 复选框:通过操作checked属性并绑定onChange事件处理函数,将复选框的选中状态设置为state的值,实现受控操作。

  3. 受控组件多表单元素优化:

在处理表单时,如果表单元素很多,为每个表单元素编写单独的处理函数可能会很繁琐。一个更好的方法是编写一个通用的处理函数,它可以处理所有表单元素的变化。以下是如何实现这个优化的示例:

import React, { Component } from 'react';

class FormExample extends Component {
  state = {
    txt: '',
    checkboxValue: false
  };

  handleForm = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <div>
        {/* 文本框 */}
        <input 
          type="text"
          name="txt"
          value={this.state.txt}
          onChange={this.handleForm}
        />

        {/* 复选框 */}
        <input 
          type="checkbox"
          name="checkboxValue"
          checked={this.state.checkboxValue}
          onChange={this.handleForm}
        />
      </div>
    );
  }
}

export default FormExample;

在上面的代码中,我们实现了以下几点优化:

  1. 添加name属性:为每个表单元素添加了name属性,并且name属性的值与state中的属性名相同。
  2. 获取对应值:通过检查表单元素的type属性,我们可以确定是获取value属性还是checked属性的值。
  3. 通用事件处理函数:创建了一个通用的handleForm事件处理函数,它可以处理所有表单元素的onChange事件。在这个函数中,我们使用了计算属性名称([name])来动态更新state中对应的值。这样,无论我们有多少表单元素,只需要一个handleForm函数就可以处理所有的表单元素变化。
7.2 非受控组件 (DOM方式)

与受控组件不同,非受控组件不通过 state 来保存表单元素的值,而是直接通过 DOM 来操作。非受控组件更接近于传统的 HTML 表单处理方式。

步骤:

  1. 创建 ref 对象
    使用 React.createRef() 方法创建一个 ref 对象。
constructor() {
  super();
  this.txtRef = React.createRef();
}
  1. 将 ref 对象添加到表单元素中
    在表单元素的 ref 属性中传入创建好的 ref 对象。
<input type="text" ref={this.txtRef} />
  1. 通过 ref 对象获取表单元素的值
    在事件处理程序中,通过 ref 对象的 current 属性来获取表单元素的值。
console.log(this.txtRef.current.value);

完整示例:

下面是一个非受控组件的完整示例,演示了如何通过 ref 来获取表单元素的值。

import React, { Component } from 'react';

class UncontrolledForm extends Component {
  constructor(props) {
    super(props);
    // 1. 创建一个 ref 对象
    this.txtRef = React.createRef();
  }

  handleSubmit = (event) => {
    event.preventDefault();
    // 3. 通过 ref 对象获取文本框的值
    console.log('Text field value:', this.txtRef.current.value);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        {/* 2. 将创建好的 ref 对象添加到文本框中 */}
        <input type="text" ref={this.txtRef} />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

export default UncontrolledForm;

在这个示例中,我们首先通过 React.createRef() 创建了一个 ref 对象 this.txtRef,然后将它传给了 input 元素的 ref 属性。在 handleSubmit 方法中,我们通过 this.txtRef.current.value 获取到了文本框的值,并在控制台中打印出来。

通过上述步骤,我们实现了一个非受控组件。在实际应用中,通常推荐使用受控组件来处理表单数据,因为它们可以更好地集成在 React 组件的状态管理系统中,但在某些情况下,非受控组件可能会更简单或方便,但是React官方不推荐使用非受控组件,因为它会设计到DOM操作,而DOM操作会消耗大量时间和内存,不利于网站系统。

8. React组件的总结

React 是一个用于构建用户界面的 JavaScript 库,其核心思想是通过组件来构建应用。下面,我们将深入探讨 React 组件的基础知识,包括组件的创建、状态管理、事件处理以及表单处理。

1. 组件的两种创建方式: 函数组件和类组件

在 React 中,组件可以通过两种方式来创建:函数组件和类组件。

1.1 函数组件
函数组件是最简单的组件类型,它是一个接收 props 参数并返回 React 元素的 JavaScript 函数。

function Hello() {
  return <div>Hello, World!</div>;
}

1.2 类组件
类组件则是使用 ES6 类来创建的,它可以包含局部状态(state)和生命周期方法等更多功能。

class Hello extends React.Component {
  render() {
    return <div>Hello, World!</div>;
  }
}
2. 无状态(函数)组件与有状态(类)组件

2.1 无状态组件
无状态组件主要负责展示数据,它通常没有自己的状态,只接收外部传入的 props

function UserInfo(props) {
  return <div>User: {props.name}</div>;
}

2.2 有状态组件
有状态组件可以有自己的局部状态,它们负责数据的处理和交互。

class Counter extends React.Component {
  state = { count: 0 };

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        Count: {this.state.count}
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}
3. 事件处理和 this 指向问题

在 React 组件中,事件处理程序中 this 的指向是一个常见的问题。以下是解决 this 指向问题的常见方法:

class Hello extends React.Component {
  handleClick = () => {
    console.log('Button clicked:', this);
  };

  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

在上述代码中,我们使用箭头函数来确保 handleClick 方法中的 this 指向组件实例。

4. 受控组件与非受控组件

React 中的表单处理可以通过受控组件和非受控组件两种方式来实现。受控组件通过 state 来控制表单元素的值,而非受控组件则通过 ref 来直接操作 DOM 元素。

受控组件示例:

class FormExample extends React.Component {
  state = { textValue: '' };

  handleTextChange = (event) => {
    this.setState({ textValue: event.target.value });
  }

  render() {
    return (
      <input 
        type="text" 
        value={this.state.textValue} 
        onChange={this.handleTextChange} 
      />
    );
  }
}

非受控组件示例:

class UncontrolledForm extends React.Component {
  txtRef = React.createRef();

  handleSubmit = (event) => {
    event.preventDefault();
    console.log('Text field value:', this.txtRef.current.value);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" ref={this.txtRef} />
        <button type="submit">Submit</button>
      </form>
    );
  }
}
5. React 的设计思想

React 强调组件化的设计和开发方式,充分利用 JavaScript 语言的能力来创建和组织组件。这种设计思想使得开发者能够构建复杂、可维护和高效的前端应用。

通过以上的介绍和示例,你应该对 React 组件的基础有了更深刻的理解。从组件的创建方式、状态管理、事件处理到表单处理,这些基础知识是掌握 React 开发的重要基础。

React脚手架的使用

React 脚手架是一个用来帮助开发者快速创建和配置一个新的 React 项目的工具。它通过预配置好的工具和默认的项目结构,使得开发者可以立即开始编写 React 代码,而不需要花费时间去配置 Babel、Webpack 等开发工具。其中,最知名的 React 脚手架工具是 Create React App。

1. 使用create-react-app创建react应用

2. react脚手架

  1. xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目

  2. 包含了所有需要的配置(语法检查、jsx编译、devServer…)

  3. 下载好了所有相关的依赖

  4. 可以直接运行一个简单效果

  5. react提供了一个用于创建react项目的脚手架库: create-react-app

  6. 项目的整体技术架构为: react + webpack + es6 + eslint

  7. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

3.创建第一个React脚手架应用

创建一个新的 React 应用非常简单。React 团队提供了一个叫做 Create React App 的官方脚手架工具,它可以帮助你快速地搭建好基础的项目结构,包括一些基础的配置,比如 Babel 和 Webpack 等。以下是使用 Create React App 创建一个新的 React 应用的步骤:

1. 确保你的系统已经安装了 Node.js 和 npm:

React 应用的开发和构建依赖于 Node.js 和 npm,所以在开始之前,请确保你的系统上已经安装了它们。你可以在命令行中运行以下命令来检查它们是否已经安装:

node -v
npm -v

如果你的系统上还没有安装 Node.js 和 npm,你可以去 Node.js 官网 下载并安装最新版本的 Node.js,npm 会随同 Node.js 一起安装。

2. 创建新的 React 应用:

打开你的终端或命令提示符,然后运行以下命令来创建一个新的 React 应用:

npx create-react-app my-react-app

在这里,my-react-app 是你的应用的名称,你可以用任何你喜欢的名称来替换它。

npx 是一个 npm 包运行器,它会自动下载并运行 create-react-app 脚本,以创建一个新的 React 应用。

3. 切换到项目目录并启动应用:

创建完应用后,切换到项目目录,并运行以下命令来启动应用:

cd my-react-app
npm start

现在,你的新 React 应用应该已经在浏览器中打开了,并且你应该能够看到一个简单的欢迎页面。你可以开始编辑 src 目录下的文件,来开发你的应用了。

这样,你就成功地使用 Create React App 创建了一个新的 React 应用,并且为开始开发做好了准备。

4. 使用React库

进入React库内之后你会看到src文件夹,你可以在此之内随便创建一个js文件,并输入

// 1.导入React
import React from 'react'
import ReactDOM from 'react-dom'

// 2.创建React元素
const title = React.createElement('h1', null, 'Hello World');

// 3. 渲染ReactDOM
ReactDOM.render(title, document.getElementById('root'));

这样,你就可以使用你的ReactApp内的React库了。

4. react脚手架项目结构

public ---- 静态资源文件夹

  • favicon.icon ------ 网站页签图标
  • index.html -------- 主页面
  • logo192.png ------- logo图
  • logo512.png ------- logo图
  • manifest.json ----- 应用加壳的配置文件
  • robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

  • App.css -------- App组件的样式
  • App.js --------- App****组件
  • App.test.js ---- 用于给App做测试
  • index.css ------ 样式
  • index.js ------- 入口文件
  • logo.svg ------- logo图
  • reportWebVitals.js— 页面性能分析文件(需要web-vitals库的支持)
  • setupTests.js---- 组件单元测试的文件(需要jest-dom库的支持)

5.React 文件内容详解

<!DOCTYPE html> <!-- 声明文档类型为 HTML5 -->
<html lang="en"> <!-- 设置页面的语言为英文 -->

  <head> <!-- 文档的头部区域,包含了文档的元数据(metadata) -->
  
    <meta charset="utf-8" /> <!-- 设置文档字符编码为 UTF-8,使得文档可以正确显示多种字符 -->
    <!-- 设置网页图标,%PUBLIC_URL% 是一个占位符,它将被替换为公共URL的路径 -->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> 
    <!-- 设置视口,使网页具有移动端的响应式特性: 仅限安卓 -->
    <meta name="viewport" content="width=device-width, initial-scale=1" /> 
    <meta name="theme-color" content="#000000" /> <!-- 设置浏览器工具栏的颜色 -->
    <meta
      name="description"
      content="Web site created using create-react-app" <!-- 设置网页的描述,有助于搜索引擎优化 -->
    />
    <!-- 设置苹果设备上的应用图标 -->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> 

    <!-- 链接到Web应用程序的清单文件,该文件描述了应用程序的名称、图标等信息 -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> 

    <title>React App</title> <!-- 设置网页的标题 -->
    
  </head>
  
  <body> <!-- 文档的主体部分 -->
  
    <noscript>You need to enable JavaScript to run this app.</noscript> <!-- 如果浏览器不支持或禁用了JavaScript,会显示此提示 -->
    <div id="root"></div> <!-- React应用程序的挂载点,React会将其组件渲染到这个div元素中 -->
    
  </body>
  
</html>

上述配置主要是一个基础的HTML模板,它为React应用程序提供了一个起始点。在React应用程序中,所有的组件将会被渲染到<div id="root"></div>这个元素中。其他的元数据和链接有助于提供一些基本的、常见的网页特性和设置,例如字符编码、视口设置、网页图标等。同时,%PUBLIC_URL%是一个特殊的占位符,它在构建过程中会被替换成实际的公共URL路径,这样,无论应用程序是部署在根路径还是子路径,都能正确地引用到资源文件。

逻辑流程:

  1. 项目启动: 当运行 npm startyarn start 时,项目会启动,执行 src/index.js 文件。
  2. 渲染组件: index.js 文件负责渲染 App 组件到 index.html 文件中的 #root 元素上。
  3. 加载样式和资源: 在 App.js 和其他组件中,可以导入 CSS、图片和其他资源,用于构建应用的界面。
  4. 组件逻辑: 在 App.js 和其他组件文件中,可以编写 React 组件和业务逻辑,构建应用的功能。

通过这个文件结构和逻辑流程,可以看出 Create React App 脚手架提供了一个很好的起点,帮助开发者快速开始构建 React 应用。同时,它的配置也可以通过 eject 或其他方法进行定制,以适应更复杂的项目需求。

6. SPA文件架构

什么是SPA

SPA (Single Page Application, 单页应用程序) 是一种现代的网页应用架构,它通过在客户端动态重写页面内容,而不是从服务器加载新的页面,来提供更接近于原生应用的用户体验。下面是SPA架构的主要特点和组成部分:

主要特点

  1. 单页面:SPA 只有一个 HTML 页面,所有的视图更新和交互都在这个页面中完成。
  2. 前端路由:通过前端路由管理不同的视图和状态,使得 URL 变化能够对应到不同的内容和视图,而不需要重新加载页面。
  3. 动态内容加载:通过 AJAX 或 Fetch API 动态加载数据,然后用 JavaScript 动态渲染页面内容。
  4. 快速响应:由于减少了页面加载的时间和服务器请求的次数,SPA 可以提供快速的响应和流畅的用户体验。

组成部分

  1. HTML/CSS/JavaScript

    • HTML: 作为页面的结构基础。
    • CSS: 提供样式和布局。
    • JavaScript: 实现动态内容加载和交互逻辑。
  2. 前端框架/库

    • 如 React, Angular, Vue.js 等,它们提供了组件化、路由管理、状态管理等功能,帮助开发者构建复杂的 SPA。
  3. 前端路由

    • 前端路由是 SPA 的核心,它通过更改 URL 的 hash 或使用 HTML5 History API 来实现页面的无刷新跳转。
  4. 状态管理

    • 如 Redux 或 Vuex,用于管理应用的状态,使得状态的管理更加清晰和有序。
  5. 模块打包工具

    • 如 Webpack 或 Parcel,它们可以帮助开发者将多个模块和资源文件打包成一个或多个文件,以便在浏览器中加载。
  6. 网络请求

    • 使用 AJAX, Fetch API 或者 axios 等库来发送网络请求,获取或提交数据。
  7. 后端 API

    • SPA 通常会与一个或多个后端 API 交互,获取数据或执行操作。

工作流程

  1. 用户访问 SPA 的入口 URL,加载单一的 HTML 页面。
  2. 页面加载后,JavaScript 代码被执行,初始化应用。
  3. 用户与应用交互,前端路由根据 URL 的变化显示不同的视图,同时通过网络请求与后端 API 交互,获取或提交数据。
  4. JavaScript 代码根据获取的数据动态更新页面内容,提供流畅的用户体验。

SPA 架构的优点是用户体验好、响应快速,而缺点则可能包括首屏加载时间较长、SEO 优化困难等。不过,随着服务端渲染 (SSR) 和预渲染 (Prerendering) 等技术的发展,这些问题得到了一定程度的解决。

React下的SPA

React 是构建用户界面的库,非常适合用于构建 SPA。下面是在 React 中实现 SPA 的基本概念和步骤:

  1. 组件化:

    • 在 React 中,一切都是组件。你可以将每个页面视为一个组件,每个组件都有自己的状态(state)和属性(props)。
  2. 路由:

    • 使用 React Router 来管理不同的视图。React Router 是一个基于组件的路由库,可以帮助你根据 URL 的变化渲染不同的组件。
  3. 状态管理:

    • 在大型 SPA 中,可能需要使用 Redux 或 Context API 来管理应用级的状态。
  4. 按需加载:

    • 使用动态导入(dynamic imports)和 React.lazy 来实现组件的按需加载,提高首屏加载速度。

具体步骤如下:

  1. 创建 React 项目:

    npx create-react-app my-spa
    cd my-spa
    
  2. 安装 React Router:

    npm install react-router-dom
    
  3. 配置路由:
    src 目录下创建一个 routes.js 文件,配置不同的路由路径和组件。

    import React from 'react';
    import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
    import Home from './components/Home';
    import About from './components/About';
    
    const Routes = () => (
      <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </Router>
    );
    
    export default Routes;
    
  4. 创建页面组件:
    src/components 目录下创建 Home.jsAbout.js 文件,每个文件代表一个页面。

  5. 启动应用:
    src/index.js 文件中导入 Routes 组件,并将其渲染到页面上。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Routes from './routes';
    import './index.css';
    
    ReactDOM.render(<Routes />, document.getElementById('root'));
    

通过以上步骤,你就可以创建一个基本的 SPA,并通过 React Router 管理不同的视图。随着项目的增长,你可能还需要考虑状态管理、API 交互、错误处理等其他方面的问题。


http://www.kler.cn/a/159540.html

相关文章:

  • Linux 实现自动登陆远程机器
  • 官方压测工具memtier-benchmark压测redis
  • 122、java的LambdaQueryWapper的条件拼接实现数据sql中and (column1 =1 or column1 is null)
  • 基于STM32设计的矿山环境监测系统(NBIOT)_262
  • 论文 | The Capacity for Moral Self-Correction in LargeLanguage Models
  • 【LeetCode】每日一题 2024_11_14 统计好节点的数目(图/树的 DFS)
  • 接口自动化测试过程中怎么处理接口依赖?
  • Docker 部署 Memos 服务
  • 数据集 mean std计算方法
  • Linux:使用pv实现执行进度监控
  • java实验:数据库应用(idea+mysql+php)设计用户注册和登录
  • JS实现网页页面的框架(demo)
  • 数据湖和中央数据仓库的设计
  • 若依前后端分离版idea启动
  • ELK配置记录
  • 安装mysql数据库
  • 本地ip查询介绍(包含公开免费的API接口)-本地ip查询API接口
  • 使用求2个字符串最短编辑距离动态规划算法实现 git diff 算法 java 实现
  • 2-1、地址加法器CS:IP
  • 【开发实践】使用jstree实现文件结构目录树
  • Go 语言中的反射机制
  • Hadoop学习笔记(HDP)-Part.04 基础环境配置
  • 数字化转型如何落地?_光点科技
  • 在Ubuntu18.04运行qt程序不小心修改内部文件出现The following files have no write permissions的解决方案
  • ARM预取侧信道(Prefetcher Side Channels)攻击与防御
  • 【热点】Docker镜像自动升级?Watchtower帮你搞定!