前言
React 作为目前最流行的前端框架之一,已经成为现代 Web 开发的必备技能。本文将带你深入了解 React 18 的核心特性,通过一个完整的路由应用示例,学习如何构建现代化的单页应用(SPA)。
一、项目初始化与入口文件
1.1 React 18 的新特性:createRoot
在 React 18 中,创建应用根节点的方式发生了重要变化。让我们看看入口文件 main.jsx:
import { createRoot } from 'react-dom/client'
createRoot 是 React 18 引入的新 API,它替代了旧版本的 ReactDOM.render。这种方式有以下优势:
- 并发渲染支持:为 React 18 的并发特性提供了基础
- 更好的性能:允许更细粒度的渲染控制
- 更清晰的 API 设计:将根节点的创建和渲染分离
createRoot(document.getElementById('root')).render(
<App />, // jsx
)
这里需要注意,函数组件的名字必须大写,这是 JSX 的约定,用来区分自定义组件和 HTML 标签。
1.2 严格模式(StrictMode)
//import { StrictMode } from 'react'
//严格模式会执行两次,一次是执行 一次是测试
严格模式是 React 提供的一个开发工具,它会在开发环境下:
- 检查代码中的问题:比如重复声明、未使用的变量、未使用的组件等
- 执行两次渲染:一次用于执行,一次用于测试,帮助发现副作用问题
虽然在这个示例中被注释掉了,但在生产环境开发时建议启用,有助于提高代码质量。
1.3 样式预处理:Stylus
import './index.styl' // 全局样式 stylus
//vite 帮我们编译 stylus 文件 生成 css 文件
项目使用了 Stylus 作为 CSS 预处理器。Vite 作为构建工具,会自动帮我们编译 Stylus 文件生成 CSS 文件。这种开发体验让样式编写更加高效和优雅。
二、React Hooks:现代 React 的状态管理
2.1 useState:响应式状态管理
在函数组件中,我们使用 Hooks 来管理状态。useState 是其中最基础也是最重要的 Hook:
const [repos, setRepos] = useState([]);
useState 的特点:
- 返回一个数组,第一个元素是状态值,第二个是更新函数
- 使用数组解构赋值获取状态和更新函数
- 初始化时可以传入初始值
2.2 useEffect:副作用管理
useEffect 是处理副作用的 Hook,类似于 Vue 的 onMounted:
useEffect(() => {
console.log('组件挂载后');
//发送api 请求,不会和组件渲染去争抢
fetch('https://gitee.com/dashboard/projects')
.then(res => res.json())
.then(data => {
setRepos(data);
});
}, []);
关键点:
- 执行时机:组件挂载后执行,不会与组件渲染争抢资源
- 依赖数组:空数组
[]表示只在组件挂载时执行一次 - 副作用操作:适合执行 API 请求、订阅、手动修改 DOM 等操作
为什么叫"副作用"?
副作用是指影响组件外部世界的操作,这些操作不应该在渲染过程中同步执行,而应该在渲染完成后异步执行。这样可以:
- 不阻塞页面渲染
- 避免不必要的重复执行
- 更好地控制资源消耗
2.3 渲染优先级:render 是第一位的
//render 是第一位的
console.log('组件初始化');
React 的核心理念是:渲染是第一位的。这意味着:
- 组件会先完成渲染
- 然后再执行副作用(如 useEffect 中的代码)
- 这样用户可以更快地看到界面,提升用户体验
在 Home 组件中,console.log('组件初始化') 会在每次渲染时执行,而 useEffect 中的代码只在挂载后执行一次。
三、React Router:单页应用的路由管理
3.1 BrowserRouter vs HashRouter
BrowserRouter as Router,// html5 和后端路由是一样的 纯 现代化 低端的浏览器不支持
// HashRouter as Router,// 路由形式之一 as 别名 有点丑 # 早期使用
React Router 提供了两种路由模式:
BrowserRouter(推荐):
- 使用 HTML5 History API
- URL 更加美观,没有
#号 - 需要服务器配置支持(所有路由指向 index.html)
- 现代浏览器的首选
HashRouter(兼容方案):
- 使用 URL 的 hash 部分(
#) - 兼容性更好,低端浏览器也支持
- URL 看起来不够优雅,但无需服务器配置
3.2 Link 组件:路由导航
Link,// 路由链接 a的替代品别名用 Link 代替 内部消化
Link 组件是 <a> 标签的替代品:
- 在内部消化了路由跳转逻辑
- 避免了页面的完全刷新
- 提供了更好的用户体验
<Link to="/">Home</Link>
<Link to="/about">About</Link>
3.3 Routes 和 Route:路由配置
Routes, // 前端路由总管
Route // 具体路由实例
路由配置的核心:
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
Routes是路由的容器,负责管理所有路由规则Route定义具体的路由映射关系path指定 URL 路径element指定要渲染的组件
3.4 路由接管一切
// 路由接管一切
<Router>
<nav>...</nav>
<AppRouters/>
</Router>
在 SPA 应用中,Router 组件会接管所有的路由逻辑,使得:
- 页面切换不需要刷新
- 用户体验更加流畅
- 状态可以在路由间保持
四、组件开发实践
4.1 函数组件:现代 React 的标准
const Home = () => {
// 组件逻辑
return (
<div>
{/* JSX 内容 */}
</div>
)
}
函数组件是现代 React 的标准写法:
- 代码更简洁
- 易于理解和测试
- 配合 Hooks 可以完成所有功能
- 性能更好(没有类组件的实例化开销)
4.2 JSX:JavaScript 的语法扩展
<App /> // jsx
JSX 允许我们在 JavaScript 中写类似 HTML 的语法:
- 更直观的 UI 描述
- 编译时会转换为
React.createElement调用 - 必须返回单个根元素(React 18 的 Fragment 可以解决)
4.3 条件渲染与列表渲染
在 Home 组件中,我们看到了条件渲染和列表渲染的实践:
{
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>
)
}
关键点:
- 使用三元运算符进行条件渲染
map方法遍历数组生成列表- 必须提供
key属性:React 使用 key 来优化列表渲染性能 target="_blank"时记得添加rel="noreferrer"提高安全性
五、模块化开发
5.1 ES Modules(ESM)
// esm cjs commonjs 模块规范
export default Home;
项目使用 ES Modules(ESM)作为模块规范:
- 使用
import和export进行模块导入导出 - 这是现代 JavaScript 的标准模块系统
- 相比 CommonJS,ESM 支持静态分析,可以进行更好的优化
5.2 组件文件组织
项目的文件结构清晰地展示了模块化的组织方式:
src/
├── main.jsx # 入口文件
├── App.jsx # 根组件
├── router/
│ └── index.jsx # 路由配置
└── pages/
├── Home.jsx # 首页组件
└── About.jsx # 关于页组件
这种组织方式:
- 职责清晰,每个文件都有明确的作用
- 易于维护和扩展
- 符合单一职责原则
六、完整的数据流
让我们梳理一下完整的数据流:
- 入口:
main.jsx使用createRoot创建根节点并渲染App组件 - 路由:
App.jsx使用Router包裹应用,配置导航和路由 - 页面组件:
Home.jsx使用 Hooks 管理状态,在useEffect中获取数据 - 渲染:数据更新后,组件重新渲染,用户看到最新内容
这个过程展示了 React 的声明式编程思想:我们只需要描述"UI 应该是什么样的",React 会负责处理如何更新 DOM。
七、最佳实践总结
7.1 开发建议
- 使用函数组件 + Hooks:这是现代 React 的标准写法
- 合理使用 useEffect:只在需要时执行副作用,注意依赖数组
- 性能优化:为列表项添加 key,避免不必要的重新渲染
- 代码组织:保持组件职责单一,合理拆分文件
7.2 注意事项
- 组件名必须大写:JSX 区分组件和 HTML 标签的规则
- render 优先:先渲染界面,再处理副作用
- 依赖数组很重要:正确使用 useEffect 的依赖数组,避免无限循环
- key 属性必须:列表渲染时提供唯一且稳定的 key
八、总结
通过这个示例项目,我们学习了:
- ✅ React 18 的
createRootAPI - ✅ React Hooks(
useState、useEffect)的使用 - ✅ React Router 的路由配置和导航
- ✅ 函数组件的开发模式
- ✅ JSX 语法和条件渲染
- ✅ 模块化开发的组织方式
React 的核心理念是声明式编程和组件化开发。通过 Hooks,函数组件已经可以完全替代类组件,成为主流开发方式。掌握这些基础知识,你就能够构建现代化的 React 应用了。