You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 18 + .NET 6组件加载优化:两种方案实现与示例

解决方案:组件级渐进式渲染方案

方案一:基于SignalR的组件数据主动推送(优先选择)

核心逻辑

利用SignalR建立后端与前端的实时连接,Controller并行异步获取各组件数据,每完成一个组件的数据抓取,就主动推送给前端对应React组件,组件收到数据后立即渲染,完全无需等待全量数据加载。

实现细节

1. 定义SignalR Hub

public class WidgetDataHub : Hub
{
    // 客户端注册组件监听分组
    public async Task RegisterWidget(string widgetGroupId)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, widgetGroupId);
    }
}

2. 拆分数据模型并并行获取

// 组件专属数据契约
public class NameWidgetDataContract
{
    public string FullName { get; set; }
    public string AvatarUrl { get; set; }
}

public class TransactionWidgetDataContract
{
    public List<TransactionRecord> RecentTransactions { get; set; }
    public decimal TotalBalance { get; set; }
}

// Controller实现
public class AccountController : Controller
{
    private readonly IWidgetDataService _dataService;
    private readonly IHubContext<WidgetDataHub> _hubContext;

    public AccountController(IWidgetDataService dataService, IHubContext<WidgetDataHub> hubContext)
    {
        _dataService = dataService;
        _hubContext = hubContext;
    }

    public async Task<IActionResult> Index()
    {
        // 启动并行任务获取各组件数据
        var nameTask = _dataService.GetNameWidgetDataAsync();
        var transactionTask = _dataService.GetTransactionWidgetDataAsync();

        // 逐个处理完成的任务并推送数据
        while (!nameTask.IsCompleted || !transactionTask.IsCompleted)
        {
            var completedTask = await Task.WhenAny(nameTask, transactionTask);
            if (completedTask == nameTask)
            {
                var data = await nameTask;
                await _hubContext.Clients.Group("name-widget").SendAsync("ReceiveWidgetData", data);
            }
            else if (completedTask == transactionTask)
            {
                var data = await transactionTask;
                await _hubContext.Clients.Group("transaction-widget").SendAsync("ReceiveWidgetData", data);
            }
        }

        // 返回仅包含组件容器的主页面
        return View();
    }
}

3. 前端React组件实现

// AccountDashboard.jsx
import { useEffect, useState } from 'react';
import { HubConnectionBuilder } from '@microsoft/signalr';
import NameWidget from './Name';
import TransactionWidget from './Transaction';
import EmptyStateWidget from './EmptyState';

export default function AccountDashboard() {
    const [nameData, setNameData] = useState(null);
    const [transactionData, setTransactionData] = useState(null);

    useEffect(() => {
        const connection = new HubConnectionBuilder()
            .withUrl('/widgetDataHub')
            .build();

        connection.start().then(() => {
            // 注册组件监听分组
            connection.invoke('RegisterWidget', 'name-widget');
            connection.invoke('RegisterWidget', 'transaction-widget');
        });

        // 接收后端推送的组件数据
        connection.on('ReceiveWidgetData', (data) => {
            if (data.fullName) {
                setNameData(data);
            } else if (data.recentTransactions) {
                setTransactionData(data);
            }
        });

        return () => connection.stop();
    }, []);

    return (
        <div className="dashboard-container">
            {/* 收到数据立即渲染,否则显示加载态 */}
            {nameData ? <NameWidget data={nameData} /> : <div className="widget-loader">加载中...</div>}
            {transactionData ? <TransactionWidget data={transactionData} /> : <div className="widget-loader">加载中...</div>}
            {/* 无需数据的组件直接渲染 */}
            <EmptyStateWidget />
        </div>
    );
}

4. 视图页面(Index.cshtml)

@{
    ViewData["Title"] = "Account Dashboard";
}

<div id="react-dashboard"></div>

<script src="~/js/signalr/dist/browser/signalr.js"></script>
<script src="~/js/react-bundle.js"></script>

方案优势

  • 组件独立渲染:单个组件数据就绪后立即渲染,首屏可见时间大幅缩短
  • 彻底规避并发冲突:所有数据由后端主动推送,前端无需发起任何Fetch请求
  • 适配React 18特性:结合useState、useEffect天然支持React 18并发渲染模式
  • 扩展性强:新增组件仅需添加对应数据契约和SignalR监听,无需改动整体架构

方案二:全量数据加载前的渐进式占位渲染

核心逻辑

保留原有全量ViewModel传递模式,在React端为每个组件添加加载占位容器,直到全量数据加载完成后统一渲染组件。

实现细节

1. 保留原有ViewModel结构

public class AccountViewModelContract
{
    public NameWidgetDataContract NameWidgetData { get; set; }
    public TransactionWidgetDataContract TransactionWidgetData { get; set; }
}

2. Controller保持全量数据获取逻辑

public async Task<IActionResult> Index()
{
    var viewModel = new AccountViewModelContract
    {
        NameWidgetData = await _dataService.GetNameWidgetDataAsync(),
        TransactionWidgetData = await _dataService.GetTransactionWidgetDataAsync()
    };
    return View(viewModel);
}

3. 视图页面序列化全量数据

@{
    ViewData["Title"] = "Account Dashboard";
}

<input type="hidden" id="dashboard-data" value="@Json.Serialize(Model)" />
<div id="react-dashboard"></div>

<script src="~/js/react-bundle.js"></script>

4. React端实现加载态与组件渲染

// AccountDashboard.jsx
import { useState, useEffect } from 'react';
import NameWidget from './Name';
import TransactionWidget from './Transaction';
import EmptyStateWidget from './EmptyState';

export default function AccountDashboard() {
    const [dashboardData, setDashboardData] = useState(null);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        const dataElement = document.getElementById('dashboard-data');
        if (dataElement) {
            const data = JSON.parse(dataElement.value);
            setDashboardData(data);
            setIsLoading(false);
        }
    }, []);

    return (
        <div className="dashboard-container">
            {isLoading ? (
                {/* 全量加载时显示组件占位骨架 */}
                <div className="dashboard-skeleton">
                    <div className="widget-skeleton"></div>
                    <div className="widget-skeleton"></div>
                    <div className="widget-skeleton"></div>
                </div>
            ) : (
                <>
                    <NameWidget data={dashboardData.NameWidgetData} />
                    <TransactionWidget data={dashboardData.TransactionWidgetData} />
                    <EmptyStateWidget />
                </>
            )}
        </div>
    );
}

方案优势

  • 改动成本极低:无需修改后端核心逻辑,仅需前端添加加载态实现,适合快速迭代
  • 数据一致性高:全量数据加载完成后统一渲染,避免组件间数据不一致问题
  • 兼容现有架构:完美适配当前未使用SSR Hydration的技术栈,无需引入额外依赖

内容的提问来源于stack exchange,提问作者CompSciFly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:05:23