像搭乐高一样学 React:从零构建你的第一个前端应用

68 阅读11分钟

从零开始,用 React 构建你的第一个前端应用:像搭乐高一样写网页

如果你刚刚接触前端开发,看到“React”、“组件”、“路由”这些词可能会感到有些晕头转向。别担心!这篇文章将带你一步一步地理解一个真实 React 项目的结构和运行逻辑。我们会像搭乐高积木一样,把网页拆成一块块小零件,再组合起来,最终做出一个能跑、能看、还能交互的小网站。


🧩 一、React 是什么?为什么它像“乐高”?

React 是一个由 Facebook(现 Meta)开发和维护的开源 JavaScript 库,主要用于构建用户界面,特别是单页应用(SPA)中的组件化 UI。它于 2013 年首次发布,如今已成为前端开发中最流行和广泛使用的工具之一。

React 的核心特点:

“组件化” —— 把网页也拆成一个个独立的小模块(组件),每个模块负责自己那一小块功能和界面。

比如:

  • 顶部导航栏是一个组件;
  • 主体内容区是一个组件;
  • 底部版权信息又是一个组件。

这些组件可以互相嵌套、复用,甚至在不同页面之间共享。这不仅让代码更清晰,也更容易维护和扩展。

而我们今天要分析的这个项目,就是一个典型的 React 小应用:它有两个页面——首页(Home)和关于页(About),通过点击导航链接切换,首页还会自动从 GitHub 拉取用户的仓库列表并展示出来。

听起来是不是很酷?那我们就从最外层开始,一层层“剥开”这个项目。


📦 二、 创建项目及依赖介绍

在你的电脑终端(Terminal)中,输入:

npm create vite@latest react-demo -- --template react

或者简写为:

npm init vite react-demo

然后按照提示选择 React + JavaScript(或其他你喜欢的语言),Vite 就会自动为你生成一个完整的 React 项目骨架,名字就叫 react-demo

稍等几秒,你会看到类似这样的目录结构:

react-demo/
├── node_modules/          # 项目依赖包(安装后自动生成)
├── public/                # 静态资源目录(如 favicon.ico)
├── src/                   # 源码目录(你主要写代码的地方)
│   ├── assets/           # 图片、字体等资源文件
│   ├── pages/            # 页面组件(我们自己组织的)
│   │   ├── About.jsx
│   │   └── Home.jsx
│   ├── router/           # 路由配置
│   ├── App.css
│   ├── App.jsx
│   ├── index.css / index.styl
│   └── main.jsx
├── index.html
├── package.json          # ← 重点!项目的“户口本”
└── README.md

这个结构看起来有点多,但别慌——我们真正需要关注的核心文件其实就那么几个。而其中最关键的“说明书”,就是 package.json

打开 package.json,你会看到类似这样的内容(已简化):

{
  "name": "react-demo",
  "version": "0.1.0",
  "dependencies": {
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "react-router-dom": "^7.10.1"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "stylus": "^0.63.0"
  }
}

这里列出了两类依赖:

  • dependencies:项目运行时必须用到的库(上线后也需要);
  • devDependencies:只在开发阶段使用的工具(比如构建、编译、格式检查等)。

我们先聚焦最核心的两个生产依赖:reactreact-dom

🔹 react:负责“思考”的大脑

react 是整个框架的核心。它提供了:

  • 定义组件的能力(比如 function App() { ... });
  • 状态管理(useState);
  • 副作用处理(useEffect);
  • JSX 语法支持(让你能用 <div>Hello</div> 这种写法)。

你可以把它想象成设计师——他画出房子的蓝图(UI 结构),规划每个房间的功能(交互逻辑),但他不会亲自搬砖砌墙。

🔹 react-dom:负责“建造”的施工队

react-dom 则是 React 在浏览器环境中的具体实现。它的唯一使命就是:把 React 组件变成真实的 HTML 元素,并插入到页面中

main.jsx 中,我们这样使用它:

import { createRoot } from 'react-dom/client'
createRoot(document.getElementById('root')).render(<App />)

这行代码的意思是:“请 react-dom<App /> 这个 React 组件,渲染到 HTML 中 id="root" 的位置。”

💡 关键区别

  • react 只管“描述 UI 应该长什么样”;
  • react-dom 负责“把这个描述变成浏览器能显示的真实 DOM”。

它们就像一对搭档:一个出方案,一个干实事。缺一不可

✅ 小知识:如果你将来做服务端渲染(SSR)或 React Native(移动端),就会用到 react-server-domreact-native,而不是 react-dom。但只要是在普通网页里,就必须用 react-dom


🏗️ 三、项目的骨架:index.htmlmain.jsx

1. index.html:网页的“空盒子”

打开 index.html,你会发现它非常简单:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>react-demo</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

实际上,在 Vite(一个现代前端构建工具)项目中,index.html 通常只包含一个空的 <div id="root"></div> 容器。你可以把它想象成一个空的展示柜——里面什么都没有,但等着 React 把内容“放进去”。

这个 <div id="root"> 就是 React 应用的“入口点”。所有 React 组件最终都会被渲染到这个盒子里。

同时<script type="module" src="/src/main.jsx"></script> 借助 Vite 在开发时将 React 入口文件按 ES 模块加载,并自动编译 JSX 为浏览器可执行的 JavaScript。

2. main.jsx:启动引擎

接下来是 main.jsx,它是整个应用的“启动按钮”:

import { createRoot } from 'react-dom/client'
import './index.styl'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(<App />)

这段代码做了三件事:

  1. 引入 createRoot:这是 React 18+ 的新 API,用来创建一个“根容器”,负责管理整个应用的渲染。
  2. 引入全局样式 index.styl:使用了 Stylus 预处理器写的 CSS,让页面看起来更美观。
  3. 挂载 App 组件:把 <App /> 这个“主组件”塞进 id="root" 的盒子里。

🏠 三、主组件 App.jsx:房子的“户型图”

现在我们进入 src/App.jsx,这是整个应用的“户型图”——它决定了房间里有哪些区域,以及怎么切换。

import { BrowserRouter as Router } from 'react-router-dom'
import './App.css';
import AppRoutes from './router/index'

function App() {
  return (
    <Router>
      <nav>
        <ul>
          <li><Link to="/">首页</Link></li>
          <li><Link to="/about">关于</Link></li>
        </ul>
      </nav>
      <AppRoutes />
    </Router>
  )
}

export default App

关键角色解析:

  • <Router> :来自 react-router-dom,它是“导航系统”。没有它,点击链接只会刷新整个页面;有了它,页面可以在不刷新的情况下切换内容(就像手机 App 切换页面一样流畅)。
  • <Link> :代替传统的 <a> 标签。它不会跳转新页面,而是告诉 Router:“我想去 //about”。
  • <AppRoutes /> :这是我们自定义的路由配置组件,决定“哪个 URL 对应哪个页面”。

💡
App.jsx 就像你家的客厅 + 走廊。走廊上有两扇门,一扇标着“卧室”(首页),一扇标着“书房”(关于页)。<Router> 是智能门禁系统,你一推门,它就知道该打开哪间房,还不用重新装修整栋房子。


🗺️ 四、路由配置 router/index.jsx:房间分配表

路由的作用是“根据 URL 显示对应的页面”。来看 router/index.jsx

import { Routes, Route } from 'react-router-dom';
import Home from '../pages/Home'
import About from '../pages/About'

export default function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  )
}
  • <Routes>:路由的“总管”,负责监听当前 URL。
  • <Route>:每一条规则,比如当路径是 / 时,显示 <Home /> 组件。

✅ 注意:element={<Home />} 里的花括号表示“这里要放一个 React 元素”,而不是字符串。

整个 AppRoutes 函数组件的核心作用,是将 URL 路径与页面组件建立映射关系,并以可复用的方式封装起来。通过 export default 导出后,主应用(如 App.jsx)只需引入 <AppRoutes /> 并直接使用,就能实现完整的页面切换功能。这不仅让路由逻辑集中、清晰,也符合 React “组件化”的思想——把复杂功能拆成独立、可维护的小单元

📄 五、页面组件:Home.jsxAbout.jsx

1. About.jsx:最简单的页面

const About = () => {
  return (
    <div>
      <h1>About</h1>
    </div>
  )
}
export default About

这就是一个“静态页面”——只显示一行字。没有任何复杂逻辑。

2. Home.jsx:会“动”的智能页面

这才是重点!Home.jsx 不仅显示内容,还会自动从网上拉数据

//   导入依赖
import { useState, useEffect } from 'react'
// 组件定义
const Home = () => {
  const [repos, setRepos] = useState([]);

  console.log('组件初始化');//  每次组件渲染时都会执行(包括初始渲染和后续更新)

  useEffect(() => { // 副作用处理 (数据获取)
    console.log('组件挂载后');
    fetch('https://api.github.com/users/xxx/repos')
      .then(res => res.json())
      .then(data => {
        setRepos(data);
      });
  }, []);

  return (
    <div>
      <h1>Home</h1>
      { repos.length ? (
        <ul>
          { repos.map(repo => (
            <li key={repo.id}> 
              <a href={repo.html_url} target="_blank" rel="noreferrer">
                {repo.name}
              </a>
            </li>
          )) }
        </ul>
      ) : (
        <p>暂无仓库</p>
      ) }
    </div>
  )
}

export default Home;
🔍 分步解读:
(1)useState:给组件“装内存”(用于在函数组件中管理状态)
const [repos, setRepos] = useState([]);
  • repos:当前存储的仓库列表(初始为空数组)。
  • setRepos:一个函数,用来更新 repos
(2)useEffect:组件的“生命周期钩子”(用于处理副作用)
useEffect(() => {
  // 发请求
}, []);
  • useEffect 接收两个参数:回调函数和依赖数组
  • 空依赖数组 []  表示这个 effect 只在组件挂载后执行一次(类似 componentDidMount
  • 获取 GitHub 用户 "shunwuyu" 的仓库数据
  • 数据获取成功后,通过 setRepos(data) 更新状态

⚠️ 注意:fetch 是浏览器原生 API,用于发送网络请求。它返回一个 Promise,所以要用 .then() 处理结果。

(3)条件渲染:有数据就列清单,没数据就提示
{ repos.length ? (
  <ul>...</ul>
) : (
  <p>暂无仓库</p>
) }

这是 React 中常见的“三元表达式”写法:如果 repos 有内容,就渲染列表;否则显示提示文字。

(4)map 渲染列表
{ repos.map(repo => (
  <li key={repo.id}>
    <a href={repo.html_url}>{repo.name}</a>
  </li>
)) }
  • map 把数组变成一组 JSX 元素。
  • key={repo.id} 是 React 的要求:每个列表项必须有唯一标识,方便高效更新。
  • rel="noreferrer" 防止安全漏洞

🎨 六、样式与构建:index.styl 和 Vite

项目中使用了 index.styl 作为全局样式文件:

import './index.styl' // in main.jsx

Stylus 是一种 CSS 预处理器,支持变量、嵌套等高级语法,写起来更简洁。虽然你没提供具体内容,但可以想象它定义了导航栏颜色、字体、间距等。

而整个项目是用 Vite 搭建的:

  • npm create vite@latest 创建项目;
  • npm run dev 启动开发服务器;
  • 支持热更新(改代码,浏览器自动刷新);
  • 极速冷启动(秒开,不像老工具要等半分钟)。

💡 Vite vs Webpack
如果 Webpack 是一辆重型卡车(功能强但启动慢),Vite 就是电动滑板车——轻快、敏捷,特别适合现代开发。


🔄 七、数据流动:从 API 到屏幕的全过程

让我们串一遍首页的数据流:

  1. 用户访问 / → Router 匹配到 <Home />
  2. React 开始渲染 Home 组件;
  3. 执行 useState([])repos 初始化为空;
  4. 组件首次渲染完成,触发 useEffect
  5. fetchhttps://api.github.com/users/shunwuyu/repos 发起请求;
  6. 收到 JSON 数据后,调用 setRepos(data)
  7. React 检测到 repos 变化,自动重新渲染组件;
  8. 页面从“暂无仓库”变成一个带链接的仓库列表。

✅ 这就是 React 的“响应式”魅力:数据变了,视图自动更新,你不需要手动操作 DOM。


🧪 八、StrictMode:React 的“代码体检医生”

🔍 它是什么?

StrictMode 不是功能组件,也不渲染任何 UI。它更像一位严谨的代码审查员,只在开发环境中运行,默默帮你发现潜在问题:

  • 警告使用了即将废弃的 API(如旧版生命周期方法);
  • 检测不安全的副作用(比如在函数组件主体中直接操作 DOM);
  • 故意对某些函数进行双重调用(如 useState 初始化函数、useEffect 回调),以暴露依赖缺失或非幂等逻辑,帮助你写出更健壮、可预测的代码。

💡 举个例子:如果你在 useEffect 里忘了加依赖数组,StrictMode 可能会让副作用执行两次,从而暴露出“数据重复请求”或“状态异常”等问题——这正是它“提前暴露 bug”的设计哲学。

⚙️ 如何启用?

只需包裹你的根组件:

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>
)

🧭 九、路由的两种形式:BrowserRouter vs HashRouter

App.jsx 中,我们用了:

import { BrowserRouter as Router } from 'react-router-dom'
  • BrowserRouter:使用 HTML5 History API,URL 看起来干净,如 http://localhost:3000/about
  • HashRouter:URL 带 #,如 http://localhost:3000/#/about,兼容老浏览器。

现代项目基本都用 BrowserRouter,除非你要部署到不支持 History API 的静态服务器。


🌟 十、总结:React 项目的“心法”

通过这个小项目,我们掌握了 React 开发的核心脉络:

概念作用类比
组件(Component)UI 的基本单元乐高积木
状态(State)组件的记忆笔记本
副作用(useEffect)处理外部交互(如 API)开机启动程序
路由(Router)页面切换房间门牌系统
JSX用 JS 写 HTML混合语言说明书
响应式更新数据变 → 视图自动变智能镜子

记住一句话
React 不是直接操作网页,而是描述“在某种状态下,页面应该长什么样”。剩下的,交给 React 自己去算。


💬 结语:编程不是魔法,而是逻辑的艺术

很多人觉得前端开发很神秘,其实它就像搭积木、写菜谱、画流程图——把复杂问题拆解成小步骤,再用代码描述清楚

React 的伟大之处,在于它用“组件 + 状态”的模型,让这种拆解变得直观而优雅。你不需要记住所有 API,只要理解“数据如何流动”、“界面如何响应”,就能写出健壮的应用。

希望这篇文章让你对 React 有了温暖而清晰的认识。下次当你看到一个动态网页时,不妨想一想:它的背后,是不是也藏着一个像 Home.jsx 这样会“思考”的小精灵呢?

动手吧!最好的学习,永远是从写下第一行代码开始。