React 18 从入门到实践:构建你的第一个单页应用

71 阅读7分钟

前言

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);
    });
}, []);

关键点:

  1. 执行时机:组件挂载后执行,不会与组件渲染争抢资源
  2. 依赖数组:空数组 [] 表示只在组件挂载时执行一次
  3. 副作用操作:适合执行 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)作为模块规范:

  • 使用 importexport 进行模块导入导出
  • 这是现代 JavaScript 的标准模块系统
  • 相比 CommonJS,ESM 支持静态分析,可以进行更好的优化

5.2 组件文件组织

项目的文件结构清晰地展示了模块化的组织方式:

src/
  ├── main.jsx          # 入口文件
  ├── App.jsx           # 根组件
  ├── router/
  │   └── index.jsx     # 路由配置
  └── pages/
      ├── Home.jsx      # 首页组件
      └── About.jsx     # 关于页组件

这种组织方式:

  • 职责清晰,每个文件都有明确的作用
  • 易于维护和扩展
  • 符合单一职责原则

六、完整的数据流

让我们梳理一下完整的数据流:

  1. 入口main.jsx 使用 createRoot 创建根节点并渲染 App 组件
  2. 路由App.jsx 使用 Router 包裹应用,配置导航和路由
  3. 页面组件Home.jsx 使用 Hooks 管理状态,在 useEffect 中获取数据
  4. 渲染:数据更新后,组件重新渲染,用户看到最新内容

这个过程展示了 React 的声明式编程思想:我们只需要描述"UI 应该是什么样的",React 会负责处理如何更新 DOM。

七、最佳实践总结

7.1 开发建议

  1. 使用函数组件 + Hooks:这是现代 React 的标准写法
  2. 合理使用 useEffect:只在需要时执行副作用,注意依赖数组
  3. 性能优化:为列表项添加 key,避免不必要的重新渲染
  4. 代码组织:保持组件职责单一,合理拆分文件

7.2 注意事项

  1. 组件名必须大写:JSX 区分组件和 HTML 标签的规则
  2. render 优先:先渲染界面,再处理副作用
  3. 依赖数组很重要:正确使用 useEffect 的依赖数组,避免无限循环
  4. key 属性必须:列表渲染时提供唯一且稳定的 key

八、总结

通过这个示例项目,我们学习了:

  • ✅ React 18 的 createRoot API
  • ✅ React Hooks(useStateuseEffect)的使用
  • ✅ React Router 的路由配置和导航
  • ✅ 函数组件的开发模式
  • ✅ JSX 语法和条件渲染
  • ✅ 模块化开发的组织方式

React 的核心理念是声明式编程组件化开发。通过 Hooks,函数组件已经可以完全替代类组件,成为主流开发方式。掌握这些基础知识,你就能够构建现代化的 React 应用了。