新手必读:React组件从入门到精通,一篇文章搞定所有核心概念

组件化概念

在我们学习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组件真正显示在浏览器页面上,我们需要完成以下几个步骤:

  1. 创建根容器:在HTML中指定一个DOM元素作为React应用的根容器
  2. 渲染组件:使用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的抽象表示:

  1. 首次渲染时

    • React调用组件的render()方法(函数组件就是直接调用函数)
    • 创建虚拟DOM树(React元素树)
    • ReactDOM将虚拟DOM转换为真实DOM节点
    • 将真实DOM插入到页面容器中
  2. 更新时

    • 当状态或属性改变时,组件重新渲染
    • 创建新的虚拟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更新 → 完成渲染

注意事项:

  1. React未导入问题 : 在React 17+之前,使用JSX必须导入React,因为JSX会被编译为React.createElement()

    jsx 复制代码
    // React 17+在某些构建工具中可以省略React导入
    // 但为了兼容性,建议始终导入
    import React from 'react';
  2. 性能优化

    • 使用React.memo()包裹组件,避免不必要的重新渲染
    • 使用useMemo()useCallback()缓存计算和函数
    • 避免在组件内部创建对象/函数,除非使用useMemo/useCallback
  3. 开发环境与生产环境

    • 开发环境包含额外的错误检查和警告
    • 生产环境进行了代码优化和压缩,体积更小

常见问题:

  1. 为什么需要虚拟DOM?

    • 直接操作DOM非常昂贵(重排、重绘)
    • 虚拟DOM在内存中操作,速度快
    • 通过Diff算法最小化DOM操作,提高性能
  2. React如何知道何时重新渲染?

    • 当组件状态(useState)或属性(props)发生变化时
    • 父组件重新渲染会导致子组件重新渲染(除非使用React.memo优化)
  3. 渲染是同步还是异步的?

    • React 17及之前:渲染通常是同步的
    • React 18:默认使用并发渲染,可以是异步的

通过理解React的渲染原理,你可以编写更高效的组件,避免不必要的重新渲染,并更好地调试React应用。组件化开发结合虚拟DOM和Diff算法,使得React能够高效地管理复杂的用户界面更新。

这是本系列的第一篇,后期会持续更新,下期内容---props组件通信。

望学习愉快!!!

相关推荐
葛葵葵2 小时前
使用 AI Workflow 规范化团队 Commit 信息:从混乱到有序
前端
用户12039112947262 小时前
LangChain 实战:让 LLM 拥有记忆与结构化输出能力
javascript·langchain·llm
Maxkim2 小时前
「✍️JS原子笔记 」一文搞懂 call、apply、bind 特征及手写实现
前端·javascript·面试
iccb10132 小时前
客服系统前端主题配色动态切换的一种实现思路(含代码)
前端
karshey2 小时前
【前端】svelte支持scss,包管理器是webpack
前端·webpack·scss
A24207349302 小时前
深入理解JS DOM:从基础操作到性能优化的全面指南
开发语言·javascript·ecmascript
Zyx20072 小时前
手写 `new`:揭开 JavaScript 实例化背后的秘密
javascript
Можно2 小时前
深入理解 HTML 中的 iframe:特性、用法与现代实践
前端·html
布局呆星2 小时前
Vue 3 事件处理与列表渲染---02
前端·javascript·vue.js