组件化概念
在我们学习react之前,我们先来思考一下,现在你要实现一个这样的界面及对应的功能:

你肯定会先写一个大的div标签,然后里面分割,上边栏,下边栏...,如果在现代化框架我们还这样写那就太落后了,后期维护复杂不说,代码复用效率还低。那我们应该怎么做才更有效率,答案是---组件化
- 何为组件:在react中,组件主要分为函数组件和类组件。现代React开发主要使用函数组件,它是一个返回JSX(JavaScript XML)的JavaScript函数,可以让你在JavaScript中编写类似HTML的结构。JSX看起来像HTML,但实际上是JavaScript语法的扩展。
jsx
import './App.css'
function App() {
return (//返回了一段JSX
<>
<h1>你的第一个组件!!!</h1>
</>
)
}
export default App

定义组件教程:
-
创建一个函数
- 组件名称首字母必须大写,否则当你调用这个组件时,react会把这个组件当作html标签,产生报错
jsx
function App(){
}
-
返回一段JSX
-
如果返回多条JSX元素记得用()包裹,如果返回一条,可以不用()包裹,但是一定得和return在同一行。
-
在写JSX时,只能有一个最外层标签,因为JSX最终会被编译为
React.createElement()函数调用,而一个函数只能返回一个值。如果含多个最外层标签,那么会报错:Adjacent JSX elements must be wrapped in an enclosing tag,翻译过来就是:相邻的 JSX 元素必须被包裹在一个封闭的标签中。 -
可以使用React片段(Fragment)作为最外层标签,它不会在DOM中创建额外的节点:
jsx// 简写形式 <> <h1>标题</h1> <p>内容</p> </> // 完整形式(可以添加key属性) <React.Fragment> <h1>标题</h1> <p>内容</p> </React.Fragment>
-

jsx
function App(){
return(
<>
...
</>
)
}
一:组件导出的两种方式
在 React 中,组件的导出有两种主要方式,它们在语法和使用上有所不同:
1. 默认导出 (Default Export)
特点:一个文件只能有一个默认导出
jsx
import './App.css'
function App() {
return (
<>
<h1>你的第一个组件!!!</h1>
</>
)
}
// 默认导出 - 方式一:分开写(推荐)
export default App
// 默认导出 - 方式二:直接写在函数声明前
// export default function App() { ... }
导入方式:
jsx
import App from './App' // 导入时可以随意命名
import MyComponent from './App' // 这样也是可以的
2. 具名导出 (Named Export)
特点:一个文件可以有多个具名导出
jsx
import './App.css'
function App() {
return (
<>
<h1>你的第一个组件!!!</h1>
</>
)
}
// 具名导出 - 方式一:单独导出
export { App }
// 具名导出 - 方式二:直接在函数前导出
// export function App() { ... }
// 还可以同时导出其他内容
// export const config = { title: "我的应用" }
// export function helper() { ... }
导入方式:
jsx
import { App } from './App' // 必须使用相同的名称
import { App as MyApp } from './App' // 可以使用别名



3. 两种方式可以同时存在
jsx
import './App.css'
function App() {
return (
<>
<h1>你的第一个组件!!!</h1>
</>
)
}
const Version = "1.0.0"
// 同时使用两种导出方式
export default App // 默认导出
export { App, Version } // 具名导出
导入方式:
jsx
import App, { Version } from './App' // 混合导入
4.语法对比表
| 导出方式 | 语法 | 导入语法 | 特点 |
|---|---|---|---|
| 默认导出 | export default App |
import App from './App' |
一个文件只能有一个 |
| 具名导出 | export { App } |
import { App } from './App' |
可以有多个 |
| 具名导出 | export function App() {} |
import { App } from './App' |
声明时直接导出 |
5.重要注意事项
- 一个组件中只能由一个默认导出,但是可以有多个具名导出
- 如何导出的,在你导入对应的组件时,你也应该如何导入。默认导出---默认导入,具名导出---具名导入
- 如果使用两种方式导出,那也可以使用两种方式导入
导入时的对应关系:
jsx
// 如果使用 export default App
import App from './App' // ✅
// 如果使用 export { App }
import { App } from './App' // ✅
// 如果同时使用两种方式
import App, { App as NamedApp } from './App' // ✅
二: 组件渲染到页面
要让React组件真正显示在浏览器页面上,我们需要完成以下几个步骤:
- 创建根容器:在HTML中指定一个DOM元素作为React应用的根容器
- 渲染组件:使用ReactDOM将组件渲染到根容器中
完整示例:
1.index.html (HTML入口文件)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>React App</title>
</head>
<body>
<!-- 这是React应用的根容器 -->
<div id="root"></div>
</body>
</html>
2.index.js (JavaScript入口文件)
jsx
// 1. 导入必要的库和组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
// 2. 获取HTML中的根元素
const rootElement = document.getElementById('root');
// 3. 创建React根(React 18+的写法)
const root = ReactDOM.createRoot(rootElement);
// 4. 渲染App组件到根容器中
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
3.App.jsx (你的组件文件)
jsx
import './App.css'
function App() {
return (
<>
<h1>你的第一个组件!!!</h1>
<p>恭喜你成功渲染了React组件!</p>
</>
)
}
export default App
三、React组件渲染原理详解
要理解React组件是如何渲染到页面的,我们需要了解其底层工作原理:
1. JSX的本质
你在组件中写的JSX代码并不是直接插入到DOM中的。实际上,JSX是一种语法糖,它会被Babel(或TypeScript编译器)编译为普通的JavaScript代码:
jsx
// JSX写法
function App() {
return (
<div className="container">
<h1>Hello, React!</h1>
</div>
);
}
// 编译后的JavaScript(相当于)
function App() {
return React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Hello, React!')
);
}
React.createElement()函数会创建一个React元素对象,这个对象描述了你想在屏幕上看到的内容:
javascript
// React.createElement()返回的对象类似于:
{
type: 'div',
props: {
className: 'container',
children: {
type: 'h1',
props: {
children: 'Hello, React!'
}
}
}
}
2. 虚拟DOM(Virtual DOM)
React使用虚拟DOM来提高渲染性能。虚拟DOM是一个轻量级的JavaScript对象树,它是真实DOM的抽象表示:
-
首次渲染时:
- React调用组件的
render()方法(函数组件就是直接调用函数) - 创建虚拟DOM树(React元素树)
- ReactDOM将虚拟DOM转换为真实DOM节点
- 将真实DOM插入到页面容器中
- React调用组件的
-
更新时:
- 当状态或属性改变时,组件重新渲染
- 创建新的虚拟DOM树
- React对比新旧虚拟DOM树(Diff算法)
- 只更新发生变化的部分到真实DOM
3. 渲染流程图解
scss
JSX代码 → 编译为React.createElement() → 创建React元素对象 → 形成虚拟DOM树
↓
首次渲染:虚拟DOM → ReactDOM.render() → 创建真实DOM → 插入到页面容器
↓
更新时:新虚拟DOM → 与旧虚拟DOM对比(Diff算法) → 计算最小变更 → 更新真实DOM
4. Diff算法(协调算法)
React使用高效的Diff算法来决定如何更新DOM,主要策略包括:
- 同级比较:React只会比较同一层次的节点,不会跨层次比较
- 元素类型不同:如果元素类型不同,React会销毁整个子树并重建
- 元素类型相同:如果元素类型相同,React会保留DOM节点,只更新有变化的属性
- Key属性优化:为列表元素添加key属性,帮助React识别哪些元素发生了变化
jsx
// 列表渲染时使用key属性
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
5. React 18的并发渲染
React 18引入了并发渲染(Concurrent Rendering):
- 可中断渲染:React可以中断、暂停和恢复渲染工作
- 优先级调度:高优先级的更新可以打断低优先级的更新
- 自动批处理:多个状态更新会被自动合并为一次渲染
jsx
// React 18的并发模式
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
6. 组件渲染的生命周期(简化版)
对于函数组件,渲染过程可以简化为:
markdown
组件函数调用 → 执行组件逻辑 → 返回JSX → 编译为React元素 → 形成虚拟DOM
↓
Diff对比 → 计算DOM变更 → 执行DOM更新 → 完成渲染
注意事项:
-
React未导入问题 : 在React 17+之前,使用JSX必须导入React,因为JSX会被编译为
React.createElement():jsx// React 17+在某些构建工具中可以省略React导入 // 但为了兼容性,建议始终导入 import React from 'react'; -
性能优化:
- 使用
React.memo()包裹组件,避免不必要的重新渲染 - 使用
useMemo()和useCallback()缓存计算和函数 - 避免在组件内部创建对象/函数,除非使用
useMemo/useCallback
- 使用
-
开发环境与生产环境:
- 开发环境包含额外的错误检查和警告
- 生产环境进行了代码优化和压缩,体积更小
常见问题:
-
为什么需要虚拟DOM?
- 直接操作DOM非常昂贵(重排、重绘)
- 虚拟DOM在内存中操作,速度快
- 通过Diff算法最小化DOM操作,提高性能
-
React如何知道何时重新渲染?
- 当组件状态(useState)或属性(props)发生变化时
- 父组件重新渲染会导致子组件重新渲染(除非使用React.memo优化)
-
渲染是同步还是异步的?
- React 17及之前:渲染通常是同步的
- React 18:默认使用并发渲染,可以是异步的
通过理解React的渲染原理,你可以编写更高效的组件,避免不必要的重新渲染,并更好地调试React应用。组件化开发结合虚拟DOM和Diff算法,使得React能够高效地管理复杂的用户界面更新。