前端技术的迭代从未停歇。当我们谈论现代前端开发时,React 19 和 Vite 已经成为了不可忽视的标准配置。React 19 带来了更高效的并发渲染机制,而 Vite 则凭借基于 ESM 的极致冷启动速度,彻底改变了开发体验。
本文将通过一个名为 react-demo 的实战项目,带你从零开始理解如何搭建、配置并开发一个标准的现代 React 应用。我们将涵盖工程化配置、路由管理、Hooks 状态逻辑以及样式预处理等核心知识点。
一、 极速启动:Vite 与 ESM 的革命
在过去,Webpack 是构建工具的王者,但它在启动大型项目时往往需要漫长的打包等待。现代开发推荐使用 Vite(法语意为"快")作为脚手架。
1. 为什么是 Vite?
Vite 的核心优势在于它利用了浏览器原生的 ES Modules (ESM) 机制。在开发阶段 (npm run dev),Vite 不需要对代码进行全量打包,而是按需提供模块,这实现了极致的"冷启动"体验。
当我们运行 npm init vite 拉取项目模板后,项目结构非常清晰。观察项目的 package.json 脚本配置:
JSON
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
这对应了完整的开发生命周期:dev(开发) -> build(构建生产包) -> preview(本地预览生产包)。
2. 依赖管理的艺术:Dev vs Prod
在安装依赖时,区分"开发依赖"和"生产依赖"至关重要。
- dependencies (生产依赖) :如
react和react-dom。React 19.2.0 是核心库,负责组件定义和 diff 算法;而react-dom负责将组件渲染到浏览器 DOM 中。这类似于 Vue 的生态,React Core 对应 Vue Core,React DOM 对应 Vue 的渲染器。对应配置为package.json中的dependencies - devDependencies (开发依赖) :如
stylus。我们使用npm i -D stylus安装它,因为 Stylus 只是在开发阶段帮助我们将.styl文件编译为 CSS,上线后的代码并不需要 Stylus 引擎。对应配置为package.json中的devDependencies
json
// 生产依赖
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.10.1"
},
// 开发依赖
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"stylus": "^0.64.0",
"vite": "^7.2.4"
}
二、 入口与渲染:React 19 的严谨模式
项目的入口文件 main.jsx 展示了 React 19 最标准的挂载方式。
JavaScript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.styl'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
严格模式 (StrictMode)
你可能会发现,在开发环境下组件的生命周期函数(如 useEffect)会执行两次。这并非 Bug,而是 <StrictMode> 的有意为之。它通过双重调用来帮助开发者检测不安全的副作用(Side Effects)和过时的 API 使用,确保代码在生产环境中更加健壮。
样式预处理
我们引入了全局样式 index.styl。Stylus 的魅力在于其极简的语法------省略花括号、冒号和分号,通过缩进来组织代码:
Stylus
*
margin: 0
padding: 0
body
background-color pink
Vite 内置了对 CSS 预处理器的支持,无需繁琐的 Webpack Loader 配置,安装即用,安装指令为npm i -D stylus。其中的-D就代表了开发依赖,如果不书写-D则会默认安装至生产依赖。
三、 路由架构:单页应用的骨架
单页应用(SPA)的核心在于:页面不刷新,URL 改变,内容切换 ,在一个页面 (index.html) 中实现 "多页面" 的切换效果。我们使用 react-router-dom v7 来实现这一功能。首先需要通过npm i react-router-dom指令安装路由。
1. 路由模式选择
在 App.jsx 中,我们采用了 BrowserRouter(别名为 Router)。相比于 URL 中带有 # 号的 HashRouter,BrowserRouter 利用 HTML5 History API,提供了更现代化、更美观的 URL 结构,是目前的行业标准。
2. 声明式导航:Link vs A
在 React Router 中,我们严禁使用传统的 <a href> 标签进行内部跳转。因为 <a> 标签会导致浏览器强制刷新页面,从而重置 React 的所有状态。
相反,我们使用 <Link> 组件:
JavaScript
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
<Link> 组件在内部"消化"了点击事件,通过 JavaScript 修改 URL 并通过 Context 通知路由系统更新视图,实现了无缝的页面切换。
3. 路由配置分离
为了保持代码的整洁,我们将具体的路由规则抽离到了 router/index.jsx 中:
JavaScript
import { Routes, Route } from 'react-router-dom';
import Home from '../pages/Home.jsx';
import About from '../pages/About.jsx';
export default function AppRoutes() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
)
}
这种集中管理路由表的方式,使得 App.jsx 只需关注整体布局,而将路由细节交给 AppRoutes 组件处理。
四、 核心业务逻辑:Hooks 驱动的数据流
Home.jsx 组件展示了 React 函数式组件的核心逻辑:Hooks。我们的目标是调用 GitHub API 并展示数据。
1. 响应式状态:useState
JavaScript
const [repos, setRepos] = useState([]);
useState 是 React 响应式系统的基石。它返回当前状态 repos 和更新函数 setRepos。每当调用 setRepos 时,React 就会感知数据变化,并触发组件的重新渲染(Re-render),更新视图。
2. 副作用管理:useEffect
网络请求属于"副作用"(Side Effect),不能直接写在组件渲染逻辑中。我们使用 useEffect 来处理组件挂载后的逻辑:
JavaScript
useEffect(() => {
// 组件挂载完成 (onMounted)
fetch('https://api.github.com/users/shunwuyu/repos')
.then(res => res.json())
.then(json => setRepos(json))
}, [])
- 执行时机 :
useEffect确保代码在组件渲染并挂载到 DOM 之后执行,避免阻塞 UI 渲染。 - 依赖数组
[]:第二个参数传入空数组[],意味着这个 Effect 只在组件初始化时执行一次(相当于类组件的componentDidMount)。如果不传此参数,每次渲染都会触发请求,导致无限循环。
3. 条件渲染与列表 Key
在 JSX 中,我们利用 JavaScript 的灵活性来构建 UI。
JavaScript
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>
) : null
}
</div>
);
- Diff 算法的关键 :在遍历列表时,必须为每个元素提供唯一的
key(如repo.id)。这能帮助 React 的 Diff 算法高效地识别元素的增删改,最小化 DOM 操作。 - 条件渲染 :通过三元运算符检查
repos.length,在数据加载前不渲染列表,防止页面报错。
五、 总结
通过这个项目,我们不仅搭建了一个简单的 GitHub 仓库浏览器,更重要的是实践了现代 React 开发的标准范式:
- 工程化:利用 Vite 极速构建,区分开发与生产依赖。
- 组件化:通过 Props 和 Hooks 实现逻辑复用。
- 路由化:使用 React Router 实现 SPA 的无感跳转。
- 响应式 :利用
useState和useEffect驱动数据流向。
从 React 19 的底层优化到 Vite 的工程实践,这套技术栈为开发者提供了极其高效的开发体验,是构建未来 Web 应用的坚实基础。
六、Home.jsx 源代码
js
import { useState, useEffect } from 'react';
const Home = () => {
const [repos, setRepos] = useState([]);
// render 是第一位的
// console.log('Home 组件渲染了');
useEffect(() => {
// home 组件可以看到了
// console.log('Home 组件挂载了');
// 发送api请求,不会和组件渲染去争抢
fetch('https://api.github.com/users/shunwuyu/repos')
.then(res => res.json())
.then(json => setRepos(json))
}, [])
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>
) : null
}
</div>
);
}
export default Home;