React开发全解析:从基础到实战
1. JSX基础
JSX 是一种 JavaScript 的语法扩展,用于在 JavaScript 代码中编写类似 XML 的结构,使代码更简洁易读。
以下是 JSX 的一些基础用法示例:
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
function formatName(name) { return `Mr/Mrs ${name}`; }
在这个例子中,如果
user
被传入函数,将返回不同的 React 元素。
1.1 属性定义
在 JSX 代码中定义属性有两种方式:
- 使用引号:
const element = <div id="someDiv"></div>;
- 使用花括号动态定义值:
function getActiveClass() {
return 'active';
}
const anotherElement = <div class={getActiveClass()} />;
1.2 元素树创建
JSX 代码还可以创建元素树:
function FullName(props) {
return (
<span>{props.givenName} {props.lastName}</span>
)
}
const element = (
<div id="root">
<FullName givenName="Sebastien" lastName="Dubois" />
<FullName givenName="Alexis" lastName="Georges" />
</div>
)
上述 React 元素有多个子元素。为了保持代码可读性,我们将 JSX 表达式拆分成多行,并使用括号包围,以避免自动分号插入(ASI)问题。
JSX 的优点还体现在安全性上,嵌入在 JSX 代码中的值会自动进行转义,能有效防止注入攻击。
2. React 元素
JSX 代码会被转译为
React.createElement
函数调用,
React.createElement
函数用于创建 React 元素。
以下是前面 JSX 示例转译后的 JavaScript 代码:
function FullName(props) {
render(React.createElement('span', null, props.givenName, ' ', props.lastName));
}
const element = React.createElement('div', {
id: 'root'
}, React.createElement(FullName, {
givenName: 'Sebastien',
lastName: 'Dubois'
}), React.createElement(FullName, {
givenName: 'Alexis',
lastName: 'Georges'
}));
createElement
函数接受三个参数:
- 元素类型
- 作为对象传递的一组 props(稍后详细讨论)
- 子元素(可选)
执行
createElement
函数时,会创建一个具有以下基本结构的对象字面量:
const element = {
type: 'div',
props: {
id: 'root',
children: [
{
type: 'FullName',
props: {
givenName: 'Sebastien',
lastName: 'Dubois',
},
},
{ ... }
]
}
};
React 元素是一种描述符,用于创建具有特定状态的组件层次结构,每个 React 元素描述了用户界面的一部分。它不规定如何渲染,渲染工作由渲染器负责。
要渲染元素,可以使用渲染器的
render
函数,例如使用
ReactDOM
将元素渲染到 DOM:
ReactDOM.render(element, document.getElementById('root'));
React 元素是不可变的,更新用户界面意味着创建新元素替换旧元素。由于 React 使用虚拟 DOM,它只会更改最少数量的 DOM 元素,从而提高性能。
以下是一个官方文档的示例:
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
在这个示例中,每秒调用
tick
函数时,都需要创建一个新的 React 元素并传递给
ReactDOM
的
render
函数。
3. 组件和 props
React 组件用于将用户界面拆分成小的、可复用的部分。与 Angular 不同,React 组件不需要注册,只需在使用时处于作用域内(即需要在使用前导入或声明)。
概念上,React 组件接受一个不可变的
props
对象作为输入,返回 JSX 或
React.createElement
调用,即 React 元素。
React 组件有两种定义方式:
-
函数组件
:推荐使用,使用纯函数定义。
-
类组件
:继承
React.Component
类并实现
render
方法。
3.1 类组件
类组件在 React 应用中应作为例外情况使用。以下是一个类组件的示例:
const createElement = React.createElement;
class SayHiButton extends React.Component {
constructor(props) {
super(props);
this.state = {
buttonClicked: false,
};
}
render() {
console.log('Rendering');
if (this.state.buttonClicked) {
console.log('The buttonClicked property of the state is true; showing the message');
return 'Hello from React!';
}
return createElement(
'button',
{
onClick: () => {
console.log("The button's click handler was clicked. Changing the state of the component");
this.setState({
buttonClicked: true,
}, null)
}
},
'Say hi!' // button's text
);
}
}
const domContainer = document.querySelector('#hello-react-container');
ReactDOM.render(createElement(SayHiButton), domContainer);
在这个示例中,
SayHiButton
组件根据其内部状态显示不同的消息。类组件和函数组件一样,通过一个
props
对象接收输入,这些属性会传递给构造函数。
以下是另一个使用 JSX 的类组件示例:
export class Foo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>Bar</div>
);
}
}
从这两个示例可以看出,使用类组件定义方式比较冗长。随着 React 的发展,函数组件变得更强大,应优先使用。
3.2 函数组件
函数组件使用纯函数定义,具有以下优点:
- 更简单易写、易读。
- 代码量更少。
- 稳定、确定性强、可预测且无副作用。
- 易于测试。
以下是一个函数组件的示例:
function DisplayMessage(props) {
return <div>Welcome to React {props.name}!</div>;
}
在 React 16.8 版本之后,引入了 React hooks,函数组件可以使用组件状态、生命周期方法和 refs 等特性,与类组件基本相当,因此更推荐使用。
4. 对象解构
对象解构是 JavaScript 的一种语法,用于轻松提取对象的部分属性。在编写 React 组件时,对象解构非常有用。
以下是一个简单的对象解构示例:
const myObject = {
firstName: 'Sebastien',
age: 36,
country: 'Belgium',
};
const {firstName, age} = myObject;
console.log(`Firstname: ${firstName}`);
在 React 组件中,如果只有一个输入,可以使用对象解构简化代码:
function DisplayMessage({name}) {
return <div>Welcome to React {name}!</div>;
}
通过对象解构,我们可以直接使用属性名,而不需要使用
props.
。
5. 组件状态
创建无状态组件有利于简化和测试,但在某些情况下,组件需要维护自己的内部状态。
在类组件中管理状态的步骤如下:
1.
初始化内部状态对象
:可以通过构造函数或在创建时初始化属性来完成,这是唯一可以直接修改状态的地方。
2.
使用
this.setState
方法修改状态
:在对象构造完成后,需要使用
this.setState
方法来修改状态。
以下是一个类组件管理状态的示例:
import React from 'react';
export class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {
currentResult: this.props.initialValue,
};
}
render() {
return (
<div>
<span>Current result: {this.state.currentResult}</span>
<br/>
<br/>
<button onClick={() => this.add(1)}>+1</button>
</div>
);
}
add(value) {
this.setState((state, props) => {
return {currentResult: state.currentResult + value}
});
}
}
在这个示例中,
add
函数展示了如何更新组件状态。
setState
方法的更新是浅合并的,并且
props
和状态更新可能是异步的,因此推荐使用接受函数作为参数的
setState
方法。
组件可以将自己的状态传递给子组件,但仍然负责管理这些值。默认情况下,只要状态或
props
发生变化,React 就会重新渲染组件及其子组件。可以使用
shouldComponentUpdate
方法避免不必要的重新渲染。
6. 提升渲染性能
在前面的示例中,使用内联函数绑定事件处理程序会导致每次组件重新渲染时都创建一个新的函数,这在实际应用中可能会导致性能问题。
更好的方法是定义一次函数并进行绑定:
function onClickHandler() { ... }
...
<button onClick={this.onClickHandler}>+1</button>
总结
本文从 JSX 基础开始,介绍了 React 元素、组件和
props
、对象解构、组件状态以及渲染性能优化等内容。通过学习这些知识,你可以更好地理解和使用 React 进行开发。函数组件由于其简洁性和灵活性,应优先使用。同时,合理管理组件状态和优化渲染性能可以提高应用的性能和可维护性。
流程图
graph TD;
A[开始] --> B[定义JSX];
B --> C[转译为React.createElement];
C --> D[创建React元素];
D --> E[渲染元素到DOM];
E --> F[组件和props];
F --> G[函数组件];
F --> H[类组件];
G --> I[使用对象解构];
H --> J[管理组件状态];
J --> K[提升渲染性能];
K --> L[结束];
表格
| 概念 | 描述 |
|---|---|
| JSX | JavaScript 语法扩展,用于编写类似 XML 的结构 |
| React 元素 |
由
React.createElement
创建,是组件层次结构的描述符
|
| 组件 | 将用户界面拆分成小的、可复用的部分 |
| props | 组件的输入,不可变 |
| 状态 | 组件的内部数据,可变 |
| 函数组件 | 使用纯函数定义,推荐使用 |
| 类组件 |
继承
React.Component
类,代码冗长
|
| 对象解构 | 用于提取对象的部分属性 |
| 渲染性能优化 | 避免内联函数绑定,提高性能 |
React开发全解析:从基础到实战
7. 生命周期钩子
在 React 组件的整个生命周期中,会经历挂载、更新和卸载等阶段,生命周期钩子允许我们在这些特定阶段执行代码。
7.1 挂载阶段
挂载阶段是组件首次被创建并插入到 DOM 中的过程。常用的挂载阶段钩子有:
-
componentWillMount
(已弃用):在组件挂载之前调用。
-
render
:返回 JSX 或
React.createElement
调用,用于渲染组件。
-
componentDidMount
:在组件挂载完成后调用,通常用于进行网络请求、订阅事件等操作。
以下是一个简单的示例:
import React from 'react';
class MyComponent extends React.Component {
componentWillMount() {
console.log('Component will mount');
}
render() {
return <div>Hello, World!</div>;
}
componentDidMount() {
console.log('Component did mount');
}
}
export default MyComponent;
7.2 更新阶段
当组件的
props
或
state
发生变化时,会触发更新阶段。常用的更新阶段钩子有:
-
componentWillReceiveProps
(已弃用):在组件接收到新的
props
时调用。
-
shouldComponentUpdate
:用于决定组件是否需要更新,可以通过返回
false
来阻止不必要的更新。
-
componentWillUpdate
(已弃用):在组件更新之前调用。
-
render
:再次渲染组件。
-
componentDidUpdate
:在组件更新完成后调用。
以下是一个使用
shouldComponentUpdate
的示例:
import React from 'react';
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
if (this.props.someProp === nextProps.someProp) {
return false;
}
return true;
}
render() {
return <div>{this.props.someProp}</div>;
}
}
export default MyComponent;
7.3 卸载阶段
卸载阶段是组件从 DOM 中移除的过程。常用的卸载阶段钩子有:
-
componentWillUnmount
:在组件卸载之前调用,通常用于清理定时器、取消订阅等操作。
以下是一个示例:
import React from 'react';
class MyComponent extends React.Component {
componentDidMount() {
this.timer = setInterval(() => {
console.log('Timer running');
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
console.log('Component will unmount');
}
render() {
return <div>Hello, World!</div>;
}
}
export default MyComponent;
8. React Hooks
React Hooks 是 React 16.8 引入的新特性,它允许我们在不编写
class
的情况下使用
state
以及其他 React 特性。
8.1
useState
useState
用于在函数组件中添加
state
。它返回一个数组,第一个元素是当前的
state
值,第二个元素是更新
state
的函数。
以下是一个使用
useState
的示例:
import React, { useState } from 'react';
function Counter() {
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;
8.2
useEffect
useEffect
用于在函数组件中执行副作用操作,例如数据获取、订阅事件等。它接受一个函数作为参数,该函数会在组件渲染后执行。
以下是一个使用
useEffect
的示例:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default Example;
8.3 自定义 Hooks
我们还可以创建自定义 Hooks 来复用逻辑。以下是一个自定义 Hooks 的示例:
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
}, [friendID]);
return isOnline;
}
9. 高阶组件
高阶组件(Higher-Order Component,简称 HOC)是一个函数,它接受一个组件作为参数,并返回一个新的组件。高阶组件用于代码复用、状态管理等。
以下是一个简单的高阶组件示例:
import React from 'react';
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log('Component mounted');
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}
class MyComponent extends React.Component {
render() {
return <div>Hello, World!</div>;
}
}
const LoggedComponent = withLogging(MyComponent);
10. 性能优化策略总结
为了提高 React 应用的性能,我们可以采取以下策略:
1.
使用 React.memo
:对于函数组件,使用
React.memo
可以对组件进行浅比较,避免不必要的渲染。
import React from 'react';
const MyComponent = React.memo(function MyComponent(props) {
return <div>{props.someProp}</div>;
});
-
使用 shouldComponentUpdate
:对于类组件,使用
shouldComponentUpdate方法来控制组件是否需要更新。 - 避免内联函数 :内联函数会导致每次渲染时都创建新的函数,使用绑定函数可以避免这个问题。
-
使用 React.lazy 和 Suspense
:对于大型应用,可以使用
React.lazy和Suspense实现代码分割,提高加载速度。
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
流程图
graph TD;
A[组件挂载] --> B[componentWillMount];
B --> C[render];
C --> D[componentDidMount];
D --> E[组件更新];
E --> F[componentWillReceiveProps];
F --> G[shouldComponentUpdate];
G -->|true| H[componentWillUpdate];
H --> I[render];
I --> J[componentDidUpdate];
J --> K[组件卸载];
K --> L[componentWillUnmount];
G -->|false| M[不更新];
表格
| 生命周期钩子 | 阶段 | 描述 |
|---|---|---|
| componentWillMount | 挂载 | 在组件挂载之前调用(已弃用) |
| render | 挂载、更新 |
返回 JSX 或
React.createElement
调用
|
| componentDidMount | 挂载 | 在组件挂载完成后调用 |
| componentWillReceiveProps | 更新 |
在组件接收到新的
props
时调用(已弃用)
|
| shouldComponentUpdate | 更新 | 决定组件是否需要更新 |
| componentWillUpdate | 更新 | 在组件更新之前调用(已弃用) |
| componentDidUpdate | 更新 | 在组件更新完成后调用 |
| componentWillUnmount | 卸载 | 在组件卸载之前调用 |
总结
本文进一步深入介绍了 React 的生命周期钩子、React Hooks、高阶组件以及性能优化策略。生命周期钩子让我们可以在组件的不同阶段执行特定代码,React Hooks 使函数组件能够使用更多特性,高阶组件则用于代码复用。同时,合理运用性能优化策略可以显著提高 React 应用的性能和响应速度。通过掌握这些知识,你可以更加熟练地使用 React 进行复杂应用的开发。
超级会员免费看
27

被折叠的 条评论
为什么被折叠?



