【超全】React学习笔记 上:基础使用与脚手架
React学习笔记
React系列笔记学习
中篇笔记地址:【超全】React学习笔记 中:进阶语法与原理机制
下篇笔记地址:【超全】React学习笔记 下:路由与Redux状态管理
React 简介
React 是一个由 Facebook 开发并维护的用于构建用户界面的 JavaScript 库。它被广泛用于构建单页应用(SPA),尤其是复杂的交互式用户界面。React 允许开发人员构建 Web 应用程序,这些程序可以在数据更改时自动更新和渲染,而无需重新加载页面。
核心特点:
-
组件化:
- React 通过组件化的方式来构建界面,每个组件都是一个独立的、可复用的代码块,负责自己的渲染逻辑。
- 组件可以嵌套、组合,形成组件树,以构建复杂的用户界面。
-
声明式编程:
- React 采用声明式编程方式,允许开发者通过描述结果来编写UI,而不是描述过程。
- 当数据变更时,React 会自动计算最小的变更集并应用到DOM上,使得代码更易读、易维护。
-
虚拟DOM(Virtual DOM):
- React引入了虚拟DOM的概念,它是一个轻量级的DOM树的副本,使得React能够最小化直接与实际DOM的交互,从而提高性能。
-
单向数据流:
- 数据在React中从上到下单向流动,使得组件之间的数据流清晰、直观。
编程思想:
-
组件生命周期:
- React组件有自己的生命周期,包括创建、更新和销毁等阶段。在这些不同的阶段,开发者可以通过特定的生命周期方法来控制组件的行为。
-
状态管理(State & Props):
- React组件的状态管理是React编程的核心部分。组件的状态(state)和属性(props)是驱动组件渲染和行为的数据源。
-
事件处理:
- React中的事件处理机制允许在组件中捕获和处理用户和系统事件。
-
条件渲染和列表渲染:
- 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()的问题
-
繁琐不简洁。
-
不直观,无法一眼看出所描述的结构。
-
不优雅,用户体验不爽。
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元素的创建和渲染过程,提高开发效率和代码的可读性。
使用细节
-
驼峰命名法:
在JSX中,所有的React元素属性名应使用驼峰命名法 (camelCase),而不是HTML中的短横线命名法 (kebab-case)。例如,HTML中的tabindex
在JSX中应该写为tabIndex
。// 正确 <input type="text" readOnly={true} /> // 错误 <input type="text" read-only={true} />
-
特殊属性名:
当您在JSX中创建React元素时,有一些HTML属性名需要以特殊的JSX属性名来代替。这是因为
class
,for
,和tabindex
在JavaScript中是保留字。下面是一些例子来说明这点:-
class -> className:
在HTML中,我们用class
属性来指定CSS类名,但在JSX中,我们应该用className
来代替。// HTML <div class="app">Hello, world!</div> // JSX <div className="app">Hello, world!</div>
-
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" />
-
tabindex -> tabIndex:
在HTML中,tabindex
属性用于指定元素的tab顺序。但在JSX中,我们应该用tabIndex
来代替。// HTML <input tabindex="1" type="text" /> // JSX <input tabIndex="1" type="text" />
以上的例子明确了在JSX中使用特殊属性名的重要性,以及如何正确地使用它们。
-
-
自闭合标签:
没有子节点的React元素可以用/>
结束,这与XML和HTML中的自闭合标签类似。-
没有子节点的React元素:
当一个React元素没有子节点时,您可以使用自闭合标签的形式来定义它。这种形式类似于XML和HTML中的自闭合标签,它以/>
结束,而不是使用一个单独的结束标签。// 正确 <input type="text" /> <br /> <hr />
-
具有子节点的React元素:
对于具有子节点的React元素,您应该使用一个开始标签和一个结束标签来定义它,而不是使用自闭合标签的形式。// 正确 <div> <p>Hello, world!</p> </div> // 错误 <div />
在上面的例子中,
<input>
,<br>
, 和<hr>
元素没有子节点,所以它们是以自闭合标签的形式定义的。而<div>
元素有一个子节点<p>
,所以它是以一个开始标签和一个结束标签的形式定义的。在第二个错误的示例中,<div />
是一个自闭合标签,但是它应该包含一个子节点<p>
,所以应该使用一个开始标签和一个结束标签来定义它。 -
-
使用小括号包裹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 + 2
、user.firstName
或formatName(user)
都是有效的JavaScript表达式。
语法∶ {JavaScript表达式}
注意∶语法中是单大括号,不是双大括号! 不要与Vue混淆!
在JSX中,可以使用单大括号 {}
来包裹JavaScript表达式。在大括号内,您可以放置任何有效的JavaScript表达式。表达式是计算并产生一个值的代码片段。例如,2 + 2
、user.firstName
或 formatName(user)
都是JavaScript表达式。
- 单大括号中可以使用任意的JavaScript表达式
在JSX中,您可以在大括号中放置任何JavaScript表达式。例如:
const element = <h1>Hello, {10 + 20}</h1>;
在这个例子中,10 + 20
是一个JavaScript表达式,它的结果是 30
,所以输出的HTML将是 <h1>Hello, 30</h1>
。
- JSX自身也是JS表达式
JSX表达式本身也是JavaScript表达式,这意味着您可以在if语句、for循环、赋值、参数、运算符等JavaScript代码结构中使用JSX。
const dv = <div>Hello</div>;
const rt_value = (
<div>嵌入表达式: {dv}</div>
)
- 注意: 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>;
- 注意: 不能在JSX中出现语句(比如: if/for等)
在JSX的大括号内,您不能使用语句,比如if
或for
。但您可以在外面的JavaScript代码中使用它们,或者使用JavaScript的表达式语法来实现相同的效果。
例如,您不能直接在JSX中这样做:
// 错误
const element = <h1>{if (user) {return user.firstName}}</h1>;
但您可以这样做:
// 正确
const element = <h1>{user ? user.firstName : 'Stranger'}</h1>;
在这个例子中,我们使用了条件(三元)运算符来实现条件渲染。
JSX的条件渲染:
条件渲染是根据某些条件动态显示不同的UI结构。在React中,你可以使用JavaScript的条件语句(如 if
、else
)或表达式(如三元运算符 ? :
或逻辑与运算符 &&
)来实现条件渲染。
- 使用
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.isLoading
为 true
,将渲染显示 “Loading…” 的 div
。否则,将渲染显示 “Content loaded” 的 div
。
- 使用三元运算符实现条件渲染:
你也可以使用三元运算符 ? :
在 JSX 内直接进行条件渲染。
function LoadingIndicator(props) {
return (
<div>
{props.isLoading ? 'Loading...' : 'Content loaded'}
</div>
);
}
ReactDOM.render(
<LoadingIndicator isLoading={true} />,
document.getElementById('root')
);
- 使用逻辑与运算符
&&
实现条件渲染:
逻辑与运算符 &&
是一个简洁的方法,用于在满足某个条件时渲染某些元素。
function LoadingIndicator(props) {
return (
<div>
{props.isLoading && 'Loading...'}
</div>
);
}
ReactDOM.render(
<LoadingIndicator isLoading={true} />,
document.getElementById('root')
);
在上述代码中,如果 props.isLoading
为 true
,则会渲染显示 “Loading…”。否则,如果 props.isLoading
为 false
,则不渲染任何内容(返回 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中,可以通过几种不同的方法来处理样式。以下是两种常见的方法。
- 行内样式
在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
元素。
- 类名
可以使用 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元素,使得代码更为简洁和易读。下面是对前面内容的简单总结:
- JSX基础:
- JSX是JavaScript XML的简写,它允许我们在JavaScript代码中写HTML。
- 使用JSX比
React.createElement
更简洁、直观和优雅,特别是在创建复杂节点时。 - 示例:
const element = <h1>Hello, world!</h1>;
- 细节使用:
- 驼峰命名法: JSX中的属性名应使用驼峰命名法。
- 特殊属性名: 如
class
应写为className
,for
应写为htmlFor
,tabindex
应写为tabIndex
。 - 自闭合标签: 没有子节点的React元素可以用
/>
来结束。 - 小括号包裹: 推荐使用小括号包裹多行的JSX,避免JavaScript的自动分号插入机制带来的问题。
- JavaScript表达式在JSX中的使用:
- 在JSX中可以使用单大括号
{}
包裹任意的JavaScript表达式。 - JSX自身也是JavaScript表达式。
- 注意: 不能在JSX中使用语句如
if
和for
等。
- 条件渲染:
- 通过
if/else
, 三元运算符? :
或逻辑与运算符&&
实现条件渲染。
- 列表渲染:
- 使用数组的
map()
方法渲染列表,并为每个元素提供唯一的key
属性值。 - 注意: 尽量避免使用数组索引作为
key
值。
- 样式处理:
- 行内样式: 通过对象的形式传递样式,属性名采用驼峰命名法。
- 类名: 使用
className
属性指定CSS类。
- 为什么能使用JSX:
- JSX不是标准的ECMAScript语法,需要通过Babel编译处理后才能在浏览器环境中运行。
create-react-app
脚手架中已经默认包含了这种配置,使得我们能够直接使用JSX语法。
通过上述的详细介绍和示例,应该能够对JSX有一个全面且深入的理解,知道如何在React项目中利用JSX来创建和渲染UI结构,以及如何处理常见的使用细节和常见问题。这为接下来的React学习和开发打下了坚实的基础。
React基础组件概念与使用
1. React 组件介绍
在React的世界中,组件是核心的构建块,它们是React应用的基础。通过组件,我们可以将UI拆分成独立可重用的片段,每个片段都可以单独思考。这种模块化的设计方式不仅提高了代码的可维护性,同时也为复杂的应用开发提供了清晰的结构。
1.1 组件的基本概念:
-
组件是React的一等公民:
- 组件是React框架中最基本的单位,所有的React应用都是由一个或多个组件组成的。
- 每个组件都是一个独立的模块,它包含了一些特定的功能和显示逻辑。
-
使用React就是在用组件:
- 开发React应用的过程,实际上就是一个不断地创建组件和组装组件的过程。
- React通过组件提供了一种高效的方式来构建和组织代码,使得代码结构清晰、易于维护。
-
组件表示页面中的部分功能:
- 组件通常对应页面上的某个视觉元素或功能模块,例如按钮、表单、导航条等。
- 通过组件,我们可以将复杂的页面拆分成更小、更管理的部分。
-
组合多个组件实现完整的页面功能:
- 组件可以嵌套和组合使用,通过将简单的组件组合成更复杂的组件,我们可以构建出完整的页面。
- 组件的组合提供了极大的灵活性,使我们能够以模块化的方式构建复杂的应用。
-
特点: 可复用、独立、可组合:
- 可复用: 组件是可复用的,同一个组件可以在多个地方使用,降低了代码重复度,提高了开发效率。
- 独立: 每个组件是独立的,它有自己的状态和逻辑,可以独立运行和测试。
- 可组合: 组件可以与其他组件组合使用,创建复杂的UI结构和功能。
通过理解和运用组件的这些特点,我们可以更好地组织和管理React应用的代码,同时也能更高效地开发和维护复杂的应用程序。
2. React组件的两种创建方式
在React中,有两种主要的组件创建方式,分别是通过函数和通过类来创建组件。每种方法都有其特点和用途,下面将分别介绍这两种方法的基本使用和特点。
2.1 使用函数创建组件
使用函数来创建组件是React中最常见也最简单的组件创建方式。通过定义一个JavaScript函数,并返回一些 JSX,我们就能创建一个简单的React组件。这种类型的组件通常被称为“函数组件”。
函数组件是React的基础,它们提供了一种简单、清晰和易于理解的方式来创建组件。随着React的发展,函数组件通过Hooks得到了极大的增强,使得它们不仅仅是表现组件,还能拥有状态和其他复杂功能,但这已经超越了基本的函数组件范畴。
特点:
- 函数组件必须有返回值: 函数组件必须返回React元素,这可以是一个JSX表达式,也可以是
null
或false
来表示不渲染任何内容。 - 组件名称必须以大写字母开头: 这是React的命名规范,以大写字母开头的名称表示它是一个组件,而不是普通的React元素或者JavaScript对象。React会根据这个规则来确定如何处理和渲染这个实体。
- 使用函数名作为组件标签名: 在使用函数组件时,应使用函数的名称作为组件的标签名。例如,上面的
Hello
组件应该用<Hello />
标签来使用。
基本语法
function Hello() {
return (
<div>这是我的第一个函数组件!</div>
);
}
ReactDOM.render(
<Hello />,
document.getElementById('root')
);
2.2 使用类创建组件
类组件是利用ES6的class
语法创建的组件,它提供了更多的功能和灵活性,特别是在处理组件的状态和生命周期时。下面是创建类组件的基本约定和步骤:
- 类名称的约定:
- 类名称也必须以大写字母开头: 类似于函数组件,类组件的名称也必须以大写字母开头,以区分React组件和普通的JavaScript类。
- 继承的约定: 2. 类组件应该继承
React.Component
父类: 通过继承React.Component
,类组件可以获得React为组件提供的方法和属性,这是创建类组件的基础。 render
方法的约定: 3. 类组件必须提供render()
方法:render
方法是类组件的核心,它定义了组件的输出。每当组件的props
或state
发生变化时,render
方法都会被调用。render()
方法必须有返回值:render
方法应返回一个React元素,该元素描述了组件在页面上的表现。这个返回值可以是JSX表达式、一个React元素、null
或false
(表示不渲染任何内容)。
下面是一个简单的类组件创建示例:
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文件中的步骤:
-
创建组件文件:
- 创建一个新的JS文件,例如
Hello.js
,用于存放组件的代码。
- 创建一个新的JS文件,例如
-
导入React:
- 在
Hello.js
文件的顶部,导入React库。这是因为组件需要React的支持才能正常工作。import React from 'react';
- 在
-
创建组件:
- 在
Hello.js
文件中创建您的组件。您可以选择创建函数组件或类组件,具体取决于您的需求。function Hello() { return <div>Hello World!</div>; } // 或 class Hello extends React.Component { render() { return <div>Hello World!</div>; } }
- 在
-
导出组件:
- 在
Hello.js
文件的底部,导出您刚刚创建的组件。这样其他文件就可以导入和使用它了。export default Hello;
- 在
-
导入组件:
- 在
index.js
或您想要使用该组件的文件中,导入Hello
组件。import Hello from './Hello';
- 在
-
渲染组件:
- 使用
ReactDOM.render
方法将Hello
组件渲染到页面上。ReactDOM.render( <Hello />, document.getElementById('root') );
- 使用
通过以上步骤,您已经将Hello
组件从index.js
文件中抽离出来,并放置在了一个单独的Hello.js
文件中。这种组织方式不仅使代码结构更清晰,也更便于团队协作和代码维护。随着项目的发展,推荐将相关的组件组织到特定的目录和文件中,例如将所有与用户相关的组件放在一个名为users
的目录中,每个组件有其单独的JS文件。
3. React 事件处理
3.1 事件绑定
在React中,事件处理程序的命名和DOM中稍有不同,但大体相似,主要体现在以下几点:
-
事件命名:
- React事件的命名采用驼峰命名法,而非全小写。例如,
onClick
代替onclick
,onMouseEnter
代替onmouseenter
。
- React事件的命名采用驼峰命名法,而非全小写。例如,
-
事件绑定语法:
- 在React中,事件绑定的语法是
on
+事件名称
={事件处理程序}
。例如,onClick={handleClick}
。 - 注意,事件处理程序通常是一个函数的引用,而非函数调用(除非您想在事件触发时立即执行该函数)。
- 在React中,事件绑定的语法是
以下是在类组件和函数组件中绑定事件的例子:
类组件中的事件绑定:
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,并触发组件重新渲染,以显示新的计数值。
总的来说,state
和setState
是React组件中管理和更新状态的核心机制。通过理解和掌握它们的使用,可以有效地管理组件的状态,并实现动态交互功能。
5.3 setState()修改状态
在React中,组件的状态是可以变化的,但是我们不能直接修改state
对象的值,而应该通过this.setState()
方法来修改状态。下面是setState
方法的基本语法:
this.setState({
// 要修改的数据
});
注意事项:
- 不要直接修改state中的值: 这是非常重要的,直接修改
state
是错误的做法,可能会导致未预期的结果和难以调试的问题。// 错误的做法! this.state.count += 1;
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')
);
在上述代码中:
- 在
Hello
组件的构造函数中,通过调用this.onIncrement.bind(this)
将onIncrement
方法的this
值绑定到类实例上,并将绑定结果赋值回this.onIncrement
。 - 在
render
方法中,通过onClick={this.onIncrement}
属性将onIncrement
方法绑定到按钮的点击事件上。 - 当用户点击按钮时,
onIncrement
方法会被调用,并且this
值指向的是类实例,从而可以正确地调用this.setState
方法来更新状态。
通过使用bind
方法,可以确保事件处理程序中this
的正确指向,但是这种方式需要在构造函数中编写额外的代码。为了减少样板代码,很多开发者倾向于使用箭头函数来绑定事件处理程序。
4. 事件绑定和this
指向总结
在React中,确保事件处理程序中this
的正确指向是非常重要的,特别是在类组件中。下面是三种常见的处理this
指向的方法,并给出了相应的代码示例。
- 使用类的实例方法
通过使用箭头函数定义类的实例方法,可以确保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')
);
- 使用箭头函数
在事件处理属性中直接使用箭头函数也可以确保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')
);
- 使用
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决定,并通过事件处理程序更新。
步骤:
- 添加状态
在组件的state中添加一个状态项,该状态项将作为表单元素的value值。
state = { txt: '' }
- 绑定事件
给表单元素绑定一个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
的值。
- 完整示例
在受控组件中,表单元素的值由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
中的值,实现了数据与视图的同步更新。
-
文本框、富文本框、下拉框:通过操作
value
属性并绑定onChange
事件处理函数,将表单元素的值设置为state
的值,实现受控操作。 -
复选框:通过操作
checked
属性并绑定onChange
事件处理函数,将复选框的选中状态设置为state
的值,实现受控操作。 -
受控组件多表单元素优化:
在处理表单时,如果表单元素很多,为每个表单元素编写单独的处理函数可能会很繁琐。一个更好的方法是编写一个通用的处理函数,它可以处理所有表单元素的变化。以下是如何实现这个优化的示例:
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;
在上面的代码中,我们实现了以下几点优化:
- 添加
name
属性:为每个表单元素添加了name
属性,并且name
属性的值与state
中的属性名相同。 - 获取对应值:通过检查表单元素的
type
属性,我们可以确定是获取value
属性还是checked
属性的值。 - 通用事件处理函数:创建了一个通用的
handleForm
事件处理函数,它可以处理所有表单元素的onChange
事件。在这个函数中,我们使用了计算属性名称([name]
)来动态更新state
中对应的值。这样,无论我们有多少表单元素,只需要一个handleForm
函数就可以处理所有的表单元素变化。
7.2 非受控组件 (DOM方式)
与受控组件不同,非受控组件不通过 state 来保存表单元素的值,而是直接通过 DOM 来操作。非受控组件更接近于传统的 HTML 表单处理方式。
步骤:
- 创建 ref 对象
使用React.createRef()
方法创建一个 ref 对象。
constructor() {
super();
this.txtRef = React.createRef();
}
- 将 ref 对象添加到表单元素中
在表单元素的ref
属性中传入创建好的 ref 对象。
<input type="text" ref={this.txtRef} />
- 通过 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脚手架
-
xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
-
包含了所有需要的配置(语法检查、jsx编译、devServer…)
-
下载好了所有相关的依赖
-
可以直接运行一个简单效果
-
react提供了一个用于创建react项目的脚手架库: create-react-app
-
项目的整体技术架构为: react + webpack + es6 + eslint
-
使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
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路径,这样,无论应用程序是部署在根路径还是子路径,都能正确地引用到资源文件。
逻辑流程:
- 项目启动: 当运行
npm start
或yarn start
时,项目会启动,执行src/index.js
文件。 - 渲染组件:
index.js
文件负责渲染App
组件到index.html
文件中的#root
元素上。 - 加载样式和资源: 在
App.js
和其他组件中,可以导入 CSS、图片和其他资源,用于构建应用的界面。 - 组件逻辑: 在
App.js
和其他组件文件中,可以编写 React 组件和业务逻辑,构建应用的功能。
通过这个文件结构和逻辑流程,可以看出 Create React App 脚手架提供了一个很好的起点,帮助开发者快速开始构建 React 应用。同时,它的配置也可以通过 eject 或其他方法进行定制,以适应更复杂的项目需求。
6. SPA文件架构
什么是SPA
SPA (Single Page Application, 单页应用程序) 是一种现代的网页应用架构,它通过在客户端动态重写页面内容,而不是从服务器加载新的页面,来提供更接近于原生应用的用户体验。下面是SPA架构的主要特点和组成部分:
主要特点:
- 单页面:SPA 只有一个 HTML 页面,所有的视图更新和交互都在这个页面中完成。
- 前端路由:通过前端路由管理不同的视图和状态,使得 URL 变化能够对应到不同的内容和视图,而不需要重新加载页面。
- 动态内容加载:通过 AJAX 或 Fetch API 动态加载数据,然后用 JavaScript 动态渲染页面内容。
- 快速响应:由于减少了页面加载的时间和服务器请求的次数,SPA 可以提供快速的响应和流畅的用户体验。
组成部分:
-
HTML/CSS/JavaScript:
- HTML: 作为页面的结构基础。
- CSS: 提供样式和布局。
- JavaScript: 实现动态内容加载和交互逻辑。
-
前端框架/库:
- 如 React, Angular, Vue.js 等,它们提供了组件化、路由管理、状态管理等功能,帮助开发者构建复杂的 SPA。
-
前端路由:
- 前端路由是 SPA 的核心,它通过更改 URL 的 hash 或使用 HTML5 History API 来实现页面的无刷新跳转。
-
状态管理:
- 如 Redux 或 Vuex,用于管理应用的状态,使得状态的管理更加清晰和有序。
-
模块打包工具:
- 如 Webpack 或 Parcel,它们可以帮助开发者将多个模块和资源文件打包成一个或多个文件,以便在浏览器中加载。
-
网络请求:
- 使用 AJAX, Fetch API 或者 axios 等库来发送网络请求,获取或提交数据。
-
后端 API:
- SPA 通常会与一个或多个后端 API 交互,获取数据或执行操作。
工作流程:
- 用户访问 SPA 的入口 URL,加载单一的 HTML 页面。
- 页面加载后,JavaScript 代码被执行,初始化应用。
- 用户与应用交互,前端路由根据 URL 的变化显示不同的视图,同时通过网络请求与后端 API 交互,获取或提交数据。
- JavaScript 代码根据获取的数据动态更新页面内容,提供流畅的用户体验。
SPA 架构的优点是用户体验好、响应快速,而缺点则可能包括首屏加载时间较长、SEO 优化困难等。不过,随着服务端渲染 (SSR) 和预渲染 (Prerendering) 等技术的发展,这些问题得到了一定程度的解决。
React下的SPA
React 是构建用户界面的库,非常适合用于构建 SPA。下面是在 React 中实现 SPA 的基本概念和步骤:
-
组件化:
- 在 React 中,一切都是组件。你可以将每个页面视为一个组件,每个组件都有自己的状态(state)和属性(props)。
-
路由:
- 使用 React Router 来管理不同的视图。React Router 是一个基于组件的路由库,可以帮助你根据 URL 的变化渲染不同的组件。
-
状态管理:
- 在大型 SPA 中,可能需要使用 Redux 或 Context API 来管理应用级的状态。
-
按需加载:
- 使用动态导入(dynamic imports)和 React.lazy 来实现组件的按需加载,提高首屏加载速度。
具体步骤如下:
-
创建 React 项目:
npx create-react-app my-spa cd my-spa
-
安装 React Router:
npm install react-router-dom
-
配置路由:
在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;
-
创建页面组件:
在src/components
目录下创建Home.js
和About.js
文件,每个文件代表一个页面。 -
启动应用:
在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 交互、错误处理等其他方面的问题。