Skip to content

React Flow 应用启动流程 #112

@pfan123

Description

@pfan123

在 React 应用中,“启动流程(Flow)” 指的是从应用初始化到首次渲染完成的整个过程,涉及环境准备、组件初始化、状态管理、路由加载等核心环节。以下是 React 应用(尤其是单页应用,SPA)的典型启动流程拆解,结合主流工具链(如 Create React App、Vite)和常见库(如 React Router、Redux)展开:

一、启动前的准备:环境与资源加载

  1. 入口文件执行

    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 负责处理掉;—— 适配器模式

  1. 依赖与资源加载
  • 打包工具会先加载入口文件依赖的模块(React 核心库、业务组件、样式文件、第三方库等)。

  • 若使用代码分割(React.lazy + Suspense),非首屏依赖会被延迟加载,减少启动时间。

  • 全局资源(如字体、图标、全局样式)在此阶段加载,可能阻塞首次渲染(需通过预加载、异步加载优化)。

二、核心初始化:React 实例与根组件挂载

  1. 创建 React 根节点(Root)

    通过 ReactDOM.createRoot() 或旧版 ReactDOM.render() 创建根节点,绑定到 HTML 中的真实 DOM 容器(通常是 <div id="root"></div>)。

  • 根节点是 React 管理虚拟 DOM 的起点,所有组件都挂载在其下。
  1. 渲染根组件(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() {

&#x20; return (

&#x20;   \<div>

&#x20;     \<Routes>

&#x20;       \<Route path="/login" element={\<Login />} />

&#x20;       {/\* 私有路由:需登录才能访问 \*/}

&#x20;       \<Route path="/home" element={\<PrivateRoute>\<Home />\</PrivateRoute>} />

&#x20;       \<Route path="\*" element={\<Navigate to="/home" />} /> {/\* 404 跳转 \*/}

&#x20;     \</Routes>

&#x20;   \</div>

&#x20; );

}

2. 状态管理初始化(如 Redux、Context)

  • 若使用 Redux,Provider 组件会将 store 注入组件树,此时会执行 store 的初始化逻辑(如加载默认状态、执行 redux-thunk/redux-saga 异步初始化任务,如拉取用户信息、配置数据)。

  • 若使用 Context API,顶层 Context.Provider 会初始化全局状态,供子组件消费。

3. 全局配置与权限校验

  • 初始化全局工具(如 API 客户端 axios 配置拦截器、日志工具、错误监控)。

  • 权限校验:检查用户登录状态(如读取 localStorage 中的 token)、加载用户权限列表,动态生成路由或控制组件渲染(如隐藏无权限的按钮)。

四、首次渲染(First Paint)与交互就绪

  1. 虚拟 DOM 构建与 DOM 渲染
  • React 递归处理组件树,将 JSX 转换为虚拟 DOM(JavaScript 对象)。

  • 通过协调算法(Reconciliation)计算虚拟 DOM 与真实 DOM 的差异,最终将差异更新到真实 DOM 中,完成首屏渲染。

  1. 组件生命周期触发(类组件)/ 副作用执行(函数组件)
function Home() {

&#x20; const \[list, setList] = useState(\[]);

&#x20; useEffect(() => {

&#x20;   // 首屏数据请求

&#x20;   fetch('/api/home/list').then(res => res.json()).then(data => {

&#x20;     setList(data);

&#x20;   });

&#x20; }, \[]); // 空依赖:仅在组件挂载后执行一次

&#x20; return \<div>{list.map(item => \<div key={item.id}>{item.name}\</div>)}\</div>;

}
  • 类组件:constructorcomponentWillMount(已废弃)→ rendercomponentDidMount(常用于初始化异步操作,如请求首屏数据)。

  • 函数组件:useState 初始化状态 → 执行组件函数(渲染)→ useEffect(依赖为空时,相当于 componentDidMount,用于加载数据、绑定事件)。

    示例(函数组件首屏数据加载):

  1. 交互就绪

    当首屏 DOM 渲染完成且事件监听(如点击、输入)绑定后,应用进入 “可交互状态”,用户可操作界面(如点击按钮、输入文本)。

五、优化点:缩短启动时间

  1. 减少首屏资源体积:通过代码分割(React.lazy)、Tree-Shaking 移除无用代码,优先加载首屏必要资源。

  2. 异步初始化:非首屏依赖(如第三方 SDK)、耗时操作(如大数据计算)延迟到首屏渲染后执行。

  3. 预加载关键资源:对路由、API 数据使用 Suspense + lazyReact Query 预加载,减少用户等待。

  4. 骨架屏(Skeleton):首屏渲染前展示骨架屏,提升用户感知体验。

总结

React 应用启动流程可概括为:

入口文件执行 → 依赖加载 → 根组件挂载 → 路由/状态初始化 → 首屏渲染 → 交互就绪

核心是通过组件树递归渲染、路由匹配、状态管理串联起整个应用的初始化逻辑,而优化的关键在于减少首屏加载资源、异步处理非必要任务,提升用户首次体验。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions