-
Notifications
You must be signed in to change notification settings - Fork 25
Description
在 React 应用中,“启动流程(Flow)” 指的是从应用初始化到首次渲染完成的整个过程,涉及环境准备、组件初始化、状态管理、路由加载等核心环节。以下是 React 应用(尤其是单页应用,SPA)的典型启动流程拆解,结合主流工具链(如 Create React App、Vite)和常见库(如 React Router、Redux)展开:
一、启动前的准备:环境与资源加载
-
入口文件执行
React 应用的启动入口通常是
index.js(或main.jsx,取决于工具链),这是打包工具(Webpack、Vite 等)配置的入口文件。示例(Create React App 入口):
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
// ========================
// 应用初始化函数所在
// ========================
const domNode = document.getElementById('root');
const root = ReactDOM.createRoot(domNode);
root.render(<App />);
大部分的 React 应用其初始化函数往往是相较简单的,它们往往选择尽快地渲染出骨架部分。然后再借助 react hook 将应用进行初始化渲染。而在大仓架构的模式下,我们会将那些有关初始化的 hook 往上层抽离,扩展到【应用初始化函数】这部分中,然后将 UI 渲染的职责转交给 UiConfigurationService(从而保障骨架渲染的体验)。
Flow 部分的设计更多是为了实现【多宿主应用】、【客制化应用】等等场景而存在的,其本质上我们可以理解为是一个 Adapter 层,期望在这里能将差异化的部分都由 Flow 负责处理掉;—— 适配器模式
- 依赖与资源加载
-
打包工具会先加载入口文件依赖的模块(React 核心库、业务组件、样式文件、第三方库等)。
-
若使用代码分割(
React.lazy+Suspense),非首屏依赖会被延迟加载,减少启动时间。 -
全局资源(如字体、图标、全局样式)在此阶段加载,可能阻塞首次渲染(需通过预加载、异步加载优化)。
二、核心初始化:React 实例与根组件挂载
-
创建 React 根节点(Root)
通过
ReactDOM.createRoot()或旧版ReactDOM.render()创建根节点,绑定到 HTML 中的真实 DOM 容器(通常是<div id="root"></div>)。
- 根节点是 React 管理虚拟 DOM 的起点,所有组件都挂载在其下。
-
渲染根组件(App 组件)
根组件(通常命名为
App)是应用的 “顶层容器”,启动流程从此处进入业务逻辑:
// App.jsx
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store';
import Layout from './Layout';
function App() {
// 初始化 DI 环境简易版
const [containerService] = useState(() => {
const registry = new ServiceRegistry();
registerServices(registry, { userState });
// 初始化
const container = new ContainerService(registry.makeCollection());
return container;
});
return (
<InstantiationContext instantiationService={containerService}>
<Provider store={store}> {/\* 注入 Redux 状态 \*/}
<Router> {/\* 初始化路由 \*/}
<Layout /> {/\* 布局组件(含导航、页脚等) \*/}
</Router>
</Provider>
</InstantiationContext>
);
}
此时,App 组件会递归渲染其内部的子组件,形成组件树。
三、关键子流程:路由、状态、权限初始化
1. 路由系统初始化(如 React Router)
-
若应用使用路由(多页面场景),
Router组件会解析当前 URL,匹配对应的路由规则(如Routes+Route配置)。 -
首次匹配的页面组件(如首页
Home)会被渲染,若涉及路由守卫(如登录验证),会在此阶段拦截并跳转(如未登录→登录页)。示例:
// Layout.jsx(路由配置)
import { Routes, Route, Navigate } from 'react-router-dom';
import Home from './pages/Home';
import Login from './pages/Login';
import PrivateRoute from './components/PrivateRoute';
function Layout() {
  return (
  \<div>
  \<Routes>
  \<Route path="/login" element={\<Login />} />
  {/\* 私有路由:需登录才能访问 \*/}
  \<Route path="/home" element={\<PrivateRoute>\<Home />\</PrivateRoute>} />
  \<Route path="\*" element={\<Navigate to="/home" />} /> {/\* 404 跳转 \*/}
  \</Routes>
  \</div>
  );
}
2. 状态管理初始化(如 Redux、Context)
-
若使用 Redux,
Provider组件会将store注入组件树,此时会执行store的初始化逻辑(如加载默认状态、执行redux-thunk/redux-saga异步初始化任务,如拉取用户信息、配置数据)。 -
若使用 Context API,顶层
Context.Provider会初始化全局状态,供子组件消费。
3. 全局配置与权限校验
-
初始化全局工具(如 API 客户端
axios配置拦截器、日志工具、错误监控)。 -
权限校验:检查用户登录状态(如读取
localStorage中的 token)、加载用户权限列表,动态生成路由或控制组件渲染(如隐藏无权限的按钮)。
四、首次渲染(First Paint)与交互就绪
- 虚拟 DOM 构建与 DOM 渲染
-
React 递归处理组件树,将 JSX 转换为虚拟 DOM(JavaScript 对象)。
-
通过协调算法(Reconciliation)计算虚拟 DOM 与真实 DOM 的差异,最终将差异更新到真实 DOM 中,完成首屏渲染。
- 组件生命周期触发(类组件)/ 副作用执行(函数组件)
function Home() {
  const \[list, setList] = useState(\[]);
  useEffect(() => {
  // 首屏数据请求
  fetch('/api/home/list').then(res => res.json()).then(data => {
  setList(data);
  });
  }, \[]); // 空依赖:仅在组件挂载后执行一次
  return \<div>{list.map(item => \<div key={item.id}>{item.name}\</div>)}\</div>;
}
-
类组件:
constructor→componentWillMount(已废弃)→render→componentDidMount(常用于初始化异步操作,如请求首屏数据)。 -
函数组件:
useState初始化状态 → 执行组件函数(渲染)→useEffect(依赖为空时,相当于componentDidMount,用于加载数据、绑定事件)。示例(函数组件首屏数据加载):
-
交互就绪
当首屏 DOM 渲染完成且事件监听(如点击、输入)绑定后,应用进入 “可交互状态”,用户可操作界面(如点击按钮、输入文本)。
五、优化点:缩短启动时间
-
减少首屏资源体积:通过代码分割(
React.lazy)、Tree-Shaking 移除无用代码,优先加载首屏必要资源。 -
异步初始化:非首屏依赖(如第三方 SDK)、耗时操作(如大数据计算)延迟到首屏渲染后执行。
-
预加载关键资源:对路由、API 数据使用
Suspense+lazy或React Query预加载,减少用户等待。 -
骨架屏(Skeleton):首屏渲染前展示骨架屏,提升用户感知体验。
总结
React 应用启动流程可概括为:
入口文件执行 → 依赖加载 → 根组件挂载 → 路由/状态初始化 → 首屏渲染 → 交互就绪。
核心是通过组件树递归渲染、路由匹配、状态管理串联起整个应用的初始化逻辑,而优化的关键在于减少首屏加载资源、异步处理非必要任务,提升用户首次体验。