React基础学习-Day01
1.React介绍
1.1 react是什么?
React 是一个用于构建用户界面的开源 JavaScript 库,由 Facebook 开发和维护。自 2013 年开源以来,React 已成为前端开发的热门选择,广泛应用于单页应用(SPA)和复杂的多页应用的开发。React 的主要目标是简化 UI 开发,使其更加高效、灵活和可维护。
1.2 react的优势?
1.2.1 react对比DOM的优势体现
- 虚拟 DOM 和高效更新:
- 虚拟 DOM:React 使用虚拟 DOM 作为内存中的副本来表示 UI 结构。每次数据更新时,React 首先在虚拟 DOM 中进行计算和比较,然后再将变化的部分批量更新到实际 DOM 中。这种优化能够显著减少 DOM 操作,提升性能。
- 减少重绘和回流:通过虚拟 DOM,React 可以避免频繁地直接操作实际 DOM,减少页面重绘(repaint)和回流(reflow),从而提高页面的响应速度和性能。
- 组件化和可复用性:
- 组件化:React 鼓励将 UI 拆分为独立的组件,每个组件负责自己的 UI 和逻辑。这种模块化的设计使得开发者可以更容易地复用、测试和维护代码。
- 自定义组件:开发者可以创建自定义的组件,并通过组合这些组件来构建复杂的 UI。这种灵活性和可复用性是传统 DOM 操作所不具备的。
- 声明式编程风格:
- 声明式:React 的编程风格是声明式的,开发者只需描述 UI 应该是什么样子,而不需要关心如何操作 DOM 进行更新。这种方式更加直观和易于理解,提高了代码的可维护性。
- 数据驱动:React 强调通过状态(state)和属性(props)来驱动 UI 的变化,数据变化时,React 负责更新 UI,开发者无需手动操作 DOM。
- 跨平台能力:
- React Native:除了 Web 应用外,React 还支持使用相同的组件模型构建原生移动应用。React Native 提供了一种在 iOS 和 Android 上构建高性能、原生用户界面的方式,这使得开发者可以共享大部分代码和技能,减少开发成本和学习曲线。
- 社区和生态系统支持:
- 庞大的社区:React 拥有一个活跃和庞大的社区,提供了丰富的第三方库、工具和解决方案,帮助开发者更高效地开发和部署应用。
- 组件库:有许多优秀的 UI 组件库(如 Material-UI、Ant Design 等)和状态管理工具(如 Redux、MobX 等),可以与 React 配合使用,进一步提升开发效率和应用性能。
总结来说,React 相对于传统的 DOM 操作,通过虚拟 DOM、组件化、声明式编程等特性,提供了更高效、可维护和可扩展的开发方式。这些优势使得 React 成为前端开发中的主流框架之一,被广泛应用于各种规模和类型的项目中。
1.2.2 react与其他框架的对比
- 灵活性和可定制性:
- React:React 本身是一个库而非完整的框架,它更注重于提供构建用户界面的核心工具和机制,开发者可以自由选择配合使用其他库和工具(如 Redux、React Router 等)。这种灵活性使得开发者可以根据项目需求进行更精确的定制和优化。
- Vue.js:Vue.js 提供了更全面的解决方案,包括路由、状态管理和构建工具等,对于小型到中型应用,Vue.js 提供了更多开箱即用的功能,减少了集成和配置的复杂性。
- 虚拟 DOM 的实现:
- React:React 使用了一套高效的虚拟 DOM 和 diff 算法,这使得 React 在处理大型数据集合和频繁数据更新时性能表现优越。
- Vue.js:Vue.js 也使用了类似的虚拟 DOM 技术,但在一些测试中,React 的 diff 算法可能会更快一些,尤其是在处理复杂的 UI 更新时。
- 生态系统和社区支持:
- React:React 拥有一个庞大和活跃的社区,支持丰富的第三方库和工具,如 Redux、Material-UI 等,这些库能够帮助开发者构建复杂的单页应用和大型项目。
- Vue.js:Vue.js 社区同样非常活跃,Vue.js 生态系统提供了许多插件和工具,如 Vuex(状态管理)、Vue Router(路由管理)等,为开发者提供了更多的选择和集成方案。
- 学习曲线和易用性:
- React:React 的学习曲线相对较陡,特别是对于初学者来说,需要掌握 JSX、组件生命周期、状态管理等概念。但一旦掌握了 React 的核心概念,开发者可以更高效地构建和维护复杂的应用。
- Vue.js:Vue.js 的学习曲线相对平缓,它更接近传统的 HTML、CSS 和 JavaScript,对于有经验的开发者和新手来说都较为友好。Vue.js 的文档和教程也很全面,帮助开发者快速入门和上手。
- 社区文化和响应性:
- React:React 社区倾向于更加注重性能和灵活性,也更倾向于使用 TypeScript 等类型安全的技术栈。Facebook 作为 React 的主要维护者,对于安全性和稳定性有较高的保障。
- Vue.js:Vue.js 社区更加开放和友好,注重开发者体验和易用性。Vue.js 也更容易与其他技术栈集成,比如可以很方便地与现有的项目或库整合。
总体来说,React 和 Vue.js 都是优秀的前端开发框架,选择适合的框架取决于项目需求、团队技术栈和开发者的偏好。React 在大型和复杂的应用中可能更适合,特别是需要高度灵活性和可定制性的场景;而 Vue.js 则更适合于快速开发和中小型应用,提供了更多集成和便捷的解决方案。
1.2.3 react对比DOM如何减少重绘和重排
重绘 (Repaint) 和回流 (Reflow)
- 重绘 (Repaint):当元素的外观样式(如颜色、背景、可见性)发生变化,但不影响布局时,会触发重绘。
- 回流 (Reflow):当元素的尺寸、位置或结构发生变化,影响布局时,会触发回流。回流比重绘更耗费性能,因为需要重新计算布局和渲染树。
React 如何减少重绘和回流
- 虚拟 DOM (Virtual DOM):
- 虚拟 DOM:React 创建了一个虚拟 DOM,它是 UI 的轻量级副本,保存在内存中。每次组件的状态或属性发生变化时,React 会在虚拟 DOM 中进行计算和比较,生成新的虚拟 DOM 树。
- Diff 算法:React 使用高效的 diff 算法比较新旧虚拟 DOM 树,找出变化的部分,并生成一个更新补丁。
- 最小化实际 DOM 操作:
- 批量更新:React 将多个状态更新合并为一次批量更新,减少 DOM 操作次数。批量更新通过一次性更新多个变化,避免频繁操作 DOM 导致的性能损耗。
- 异步更新:React 通过异步批处理(例如 React 18 中引入的 Concurrent Mode),将多次状态更新合并处理,避免频繁的同步 DOM 操作。
- 避免不必要的更新:
shouldComponentUpdate
:类组件中可以使用shouldComponentUpdate
方法来控制组件是否需要重新渲染,从而避免不必要的重绘和回流。- React.memo:函数组件可以使用
React.memo
进行性能优化,它会对比前后属性,只有属性变化时才会重新渲染组件。 useMemo
和useCallback
:Hooks 提供的useMemo
和useCallback
可以缓存计算结果和函数引用,避免子组件不必要的更新。
- 合并更新:
- 批量状态更新:React 会自动合并多次状态更新,在一次事件循环内只触发一次渲染,从而减少 DOM 操作。
示例代码
以下示例演示了 React 如何通过 React.memo
和 useMemo
来减少不必要的渲染:
import React, { useState, useMemo } from 'react';
// 一个简单的子组件,仅在 props 变化时重新渲染
const ChildComponent = React.memo(({ value }) => {
console.log('ChildComponent render');
return <div>{value}</div>;
});
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// 使用 useMemo 缓存计算结果,避免不必要的计算
const memoizedValue = useMemo(() => {
console.log('Calculating memoized value');
return count * 2;
}, [count]);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<ChildComponent value={memoizedValue} />
</div>
);
}
export default App;
在这个示例中:
ChildComponent
只有在value
变化时才会重新渲染,因为它被React.memo
包裹。memoizedValue
使用useMemo
进行缓存,只有在count
变化时才会重新计算,避免了不必要的计算和渲染。
总结
通过虚拟 DOM、批量更新、避免不必要的更新和合并更新,React 有效地减少了重绘和回流的次数,从而提升了应用的性能。与传统的直接操作 DOM 的方式相比,React 提供了一种更加高效和优化的 UI 更新机制。
2.jsx语法介绍
.jsx
是 JavaScript XML 的缩写,它是一种 JavaScript 的语法扩展,允许开发者在 JavaScript 中直接编写类似 HTML 的标记语言,用于定义 React 组件的 UI 结构。以下是一些 .jsx
语法的核心特点和使用方法:
主要特点:
- 类 HTML 结构:
.jsx
允许在 JavaScript 中嵌入类似 HTML 的标记语言,用于声明 UI 的结构和组件的外观。- 例如,在 React 中,可以直接使用
<div>
、<span>
、<h1>
等 HTML 标签,以及自定义的 React 组件。
- JSX 表达式:
- 在
.jsx
中,可以使用{}
花括号来包裹 JavaScript 表达式,用于在标记中动态地插入变量、计算结果或函数调用。 - 这使得
.jsx
具有了 JavaScript 的全部表达能力,可以进行条件判断、循环、计算等操作。
- 在
- 属性传递:
- 类似 HTML,
.jsx
允许在标签上使用属性,并支持使用表达式作为属性值。 - 例如:
<Component name={variable} />
,其中name
是一个属性,variable
是一个 JavaScript 变量或表达式。
- 类似 HTML,
- 自闭合标签:
.jsx
中的标签可以是自闭合的,比如<img>
、<input>
,也可以使用/
符号来表示自闭合,例如<br />
。
- 注释:
- 和 JavaScript 一样,可以使用
//
单行注释和/* */
多行注释在.jsx
文件中添加注释(注释要写在{}内)。
- 和 JavaScript 一样,可以使用
示例:
以下是一个简单的 React 组件示例,展示了 .jsx
的基本语法和特点:
import React from 'react';
// 定义一个 React 函数组件
const MyComponent = () => {
const name = 'World';
return (
<div>
<h1>Hello, {name}!</h1>
<p>Welcome to JSX!</p>
<ul>
{['apple', 'banana', 'cherry'].map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
</div>
);
};
export default MyComponent;
在这个例子中:
<h1>
、<p>
、<ul>
和<li>
等标签直接在 JavaScript 函数中使用,用于构建组件的 UI 结构。- 使用
{name}
在<h1>
中动态地插入变量name
的值。 - 使用
{}
花括号包裹的 JavaScript 表达式,例如['apple', 'banana', 'cherry'].map()
来生成列表项<li>
。 key={index}
用于在 React 中标识列表项的唯一性,帮助 React 更高效地管理组件的更新。
注意事项:
- JSX 本质上是 JavaScript 语法的扩展,需要通过 Babel 等工具进行编译转换成标准的 JavaScript 代码,以便浏览器能够正确解析和执行。
- JSX 让开发者可以更直观地编写和理解 React 组件的结构和逻辑,提高了代码的可读性和开发效率。
总结来说,.jsx
是 React 中用于定义组件 UI 结构的一种语法扩展,它结合了 JavaScript 的表达能力和 HTML 标记的直观性,是开发现代 Web 应用的重要工具之一。
3.jsx实现列表渲染
import React from 'react';
const MyComponent = () => {
// 假设我们有一个水果列表
const fruits = ['apple', 'banana', 'cherry'];
return (
<div>
<h2>Fruit List:</h2>
<ul>
{/* 使用 map 方法遍历数组,并为每个元素生成一个 <li> 元素 */}
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
</div>
);
};
export default MyComponent;
4.jsx实现条件渲染
4.1 三元运算符 (Ternary Operator)
这是最常用的方式,通过三元运算符来实现简单的条件渲染。
import React from 'react';
const MyComponent = () => {
const isLoggedIn = true;
return (
<div>
{isLoggedIn ? <p>Welcome back!</p> : <p>Please sign in.</p>}
</div>
);
};
export default MyComponent;
4.2. 逻辑与运算符 (Logical AND Operator)
适用于条件为真时才渲染内容的场景。
import React from 'react';
const MyComponent = () => {
const isLoggedIn = true;
return (
<div>
{isLoggedIn && <p>Welcome back!</p>}
</div>
);
};
export default MyComponent;
4.3. 立即执行函数表达式 (IIFE)
适用于需要执行更多逻辑或多条件判断的情况。
import React from 'react';
const MyComponent = () => {
const status = 'loggedOut'; // 可以是 'loggedIn' 或 'loggedOut'
return (
<div>
{(() => {
if (status === 'loggedIn') {
return <p>Welcome back!</p>;
} else if (status === 'loggedOut') {
return <p>Please sign in.</p>;
} else {
return <p>Status unknown.</p>;
}
})()}
</div>
);
};
export default MyComponent;
4.4 条件渲染组件
将条件渲染逻辑封装到单独的组件中,可以提高代码的可读性和复用性。
import React from 'react';
const Greeting = ({ isLoggedIn }) => {
if (isLoggedIn) {
return <p>Welcome back!</p>;
} else {
return <p>Please sign in.</p>;
}
};
const MyComponent = () => {
const isLoggedIn = true;
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
</div>
);
};
export default MyComponent;
5.React基础事件绑定
5.1 基本事件绑定
在 React 中,可以直接在 JSX 中通过类似 HTML 的方式来绑定事件处理函数。例如,给按钮绑定点击事件:
import React from 'react';
const MyComponent = () => {
const handleClick = () => {
console.log('Button clicked!');
};
return (
<div>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default MyComponent;
在这个例子中,onClick
是 React 的事件属性,它接收一个函数作为值。当按钮被点击时,handleClick
函数会被调用。
5.2 事件处理函数
React 的事件处理函数与普通的 JavaScript 事件处理函数类似,它们接收一个合成事件对象 (SyntheticEvent),而不是原生 DOM 事件对象。这使得跨浏览器的事件处理更加一致和可靠。
import React from 'react';
const MyComponent = () => {
const handleClick = (event) => {
console.log('Button clicked!', event.target);
};
return (
<div>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default MyComponent;
在上面的示例中,handleClick
函数的参数 event
是一个合成事件对象,可以通过 event.target
访问触发事件的 DOM 元素。
5.3 事件传参
有时候需要给事件处理函数传递额外的参数。可以使用箭头函数或者 bind
方法来实现这一点。
使用箭头函数:
import React from 'react';
const MyComponent = () => {
const handleClick = (name) => {
console.log(`Hello, ${name} clicked!`);
};
return (
<div>
<button onClick={() => handleClick('World')}>Click me</button>
</div>
);
};
export default MyComponent;
使用 bind
方法:
import React from 'react';
const MyComponent = () => {
const handleClick = (name) => {
console.log(`Hello, ${name} clicked!`);
};
return (
<div>
<button onClick={handleClick.bind(null, 'World')}>Click me</button>
</div>
);
};
export default MyComponent;
这两种方式都可以将 'World'
作为参数传递给 handleClick
函数。
5.4 阻止事件默认行为和冒泡
在 React 中,阻止事件的默认行为和冒泡可以通过合成事件对象的方法来实现。
阻止默认行为:
import React from 'react';
const MyComponent = () => {
const handleClick = (event) => {
event.preventDefault();
console.log('Button clicked!');
};
return (
<div>
<a href="#" onClick={handleClick}>Click me</a>
</div>
);
};
export default MyComponent;
阻止冒泡:
import React from 'react';
const MyComponent = () => {
const handleClick = (event) => {
event.stopPropagation();
console.log('Button clicked!');
};
return (
<div onClick={() => console.log('Div clicked!')}>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default MyComponent;
总结
React 的事件绑定和处理与传统的 JavaScript 事件处理有些许差异,主要体现在事件对象的处理上,使用合成事件对象来实现跨浏览器一致性。通过上述方法,可以有效地在 React 中处理各种事件,并进行事件传参、阻止默认行为和冒泡等操作。
6.组件是什么?
在 React 中,组件是构建用户界面的基本单位,可以将 UI 分割成独立的、可复用的代码片段。组件可以是函数组件或类组件,它们可以接收输入的数据(称为 props)并返回描述页面展示内容的 React 元素。组件可以包含其他组件、HTML 标签和逻辑,使得复杂的 UI 可以被有效地管理和开发。
主要特点和作用:
- 封装和复用:
- 组件可以将 UI 划分成独立的部分,每个部分负责自己的功能,从而提高代码的可维护性和复用性。
- 组件层级:
- 组件可以嵌套组合,形成层次化的结构。父组件可以向子组件传递数据和函数,实现数据的流动和交互的功能。
- 状态管理:
- 类组件可以拥有状态(state),用于存储和管理组件内部的数据。状态的改变会触发组件的重新渲染,从而更新用户界面。
- 生命周期:
- 类组件具有生命周期方法,例如
componentDidMount
、componentDidUpdate
和componentWillUnmount
等,用于在组件生命周期不同阶段执行特定的逻辑。
- 类组件具有生命周期方法,例如
- 纯UI组件和容器组件:
- 纯UI组件(Presentational Components)负责如何渲染数据,不涉及业务逻辑。容器组件(Container Components)则负责管理数据和状态,传递给纯UI组件。
示例:
以下是一个简单的 React 函数组件的示例,展示了一个名为 HelloWorld
的组件,它接收一个 name
属性作为输入,并渲染一个简单的问候语句:
import React from 'react';
// 函数组件:HelloWorld
const HelloWorld = ({ name }) => {
return (
<div>
<h1>Hello, {name}!</h1>
<p>Welcome to React components.</p>
</div>
);
};
export default HelloWorld;
在这个例子中,HelloWorld
组件接收一个 name
属性,用于动态显示问候语。这种组件可以被多次使用,每次使用时传入不同的 name
属性,使得界面呈现的问候语可以个性化定制。
总之,React 组件是构建现代 Web 应用的核心概念之一,它们通过组合和抽象,使得开发者可以更加高效和灵活地构建用户界面。
7.useState使用介绍
useState
是 React 中的一个 Hook,用于在函数组件中添加状态管理功能。它允许你在函数组件中声明状态变量,并提供一个方法来更新这些状态。
使用方法
useState
是从 React 中导入的,并且通常在函数组件的顶部进行调用。它返回一个包含两个元素的数组:当前状态值和一个更新状态的函数。
基本示例
import React, { useState } from 'react';
const Counter = () => {
// 声明一个名为 "count" 的状态变量,初始值为 0
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};
export default Counter;
在这个示例中:
const [count, setCount] = useState(0);
声明了一个名为count
的状态变量,初始值为0
。useState
返回一个数组,第一个元素是当前状态值(count
),第二个元素是更新状态的函数(setCount
)。- 每次点击按钮时,调用
setCount(count + 1)
更新count
的值,从而触发组件的重新渲染,并更新显示的点击次数。
使用多个状态变量
你可以在一个组件中使用多个 useState
调用来管理不同的状态变量:
import React, { useState } from 'react';
const MultiStateComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type something..."
/>
<p>You typed: {text}</p>
</div>
);
};
export default MultiStateComponent;
在这个示例中,我们使用了两个 useState
调用来分别管理 count
和 text
两个状态变量。
初始化状态
你可以将状态的初始值设置为函数返回的值,这在初始化需要执行复杂计算时特别有用:
import React, { useState } from 'react';
const ExpensiveComponent = () => {
const initialCount = () => {
// 模拟一个复杂的计算
return 42;
};
const [count, setCount] = useState(initialCount);
return (
<div>
<p>Initial count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
};
export default ExpensiveComponent;
在这个示例中,initialCount
函数只会在组件初次渲染时执行一次,用于计算初始状态值。
总结
useState
是一个 React Hook,用于在函数组件中添加状态管理功能。useState
返回一个包含当前状态值和一个更新状态的函数的数组。- 可以使用多个
useState
调用来管理多个状态变量。 - 状态的初始值可以是一个复杂计算的结果,通过传递一个函数来实现。
8.修改状态的规则
在 React 中,使用 useState
来管理和修改状态时,有一些规则和最佳实践需要遵循,以确保状态的管理和更新过程是高效和可靠的。以下是修改状态的主要规则和一些示例:
1. 状态更新是异步的
React 的状态更新是异步的,这意味着调用状态更新函数后,状态的更新不会立即反映出来,而是在下一次重新渲染时才会生效。
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // 这里的 count 可能不会立即更新
};
return (
<div>
<p>{count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
export default Counter;
在上述例子中,console.log(count)
可能不会立即显示更新后的值,因为状态更新是异步的。
2. 状态更新函数接受当前状态作为参数
为了确保状态更新时使用的值是最新的,可以传递一个函数给状态更新函数。这个函数会接收当前的状态值作为参数。
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>{count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
export default Counter;
在这个示例中,setCount
接收一个函数 prevCount => prevCount + 1
,确保使用的是最新的状态值进行更新。
3. 不要直接修改状态
状态应该是不可变的,不要直接修改状态对象,而是通过创建新对象来更新状态。
import React, { useState } from 'react';
const Example = () => {
const [user, setUser] = useState({ name: 'John', age: 30 });
const updateAge = () => {
setUser(prevUser => ({
...prevUser,
age: prevUser.age + 1
}));
};
return (
<div>
<p>{user.name}</p>
<p>{user.age}</p>
<button onClick={updateAge}>Increment Age</button>
</div>
);
};
export default Example;
在这个示例中,使用扩展运算符 ...prevUser
创建了一个新的用户对象,并更新了年龄属性。
4. 合并状态
对于对象类型的状态,需要手动合并更新的部分,因为 useState
不会自动合并更新对象。
import React, { useState } from 'react';
const Example = () => {
const [user, setUser] = useState({ name: 'John', age: 30 });
const updateName = () => {
setUser(prevUser => ({
...prevUser,
name: 'Doe'
}));
};
return (
<div>
<p>{user.name}</p>
<p>{user.age}</p>
<button onClick={updateName}>Update Name</button>
</div>
);
};
export default Example;
在这个示例中,setUser
的更新函数创建了一个新的用户对象,同时保持其他属性不变。
5. 避免不必要的状态更新
尽量避免在每次渲染中都调用状态更新函数,只有在需要时才更新状态,以提高性能。
import React, { useState } from 'react';
const Example = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
if (count < 10) {
setCount(count + 1);
}
};
return (
<div>
<p>{count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
export default Example;
在这个示例中,只有在 count
小于 10 时才会更新状态,避免了不必要的状态更新。
总结
- 状态更新是异步的:不能依赖立即更新后的值。
- 使用函数式更新:在更新状态时,传递一个函数来确保使用最新的状态值。
- 不要直接修改状态:状态应该是不可变的,需要通过创建新对象来更新状态。
- 手动合并状态:对于对象类型的状态,需要手动合并更新部分。
- 避免不必要的状态更新:只有在需要时才更新状态,以提高性能。