Administrator
发布于 2026-04-30 / 5 阅读
0
0

首页预加载配置

VisibilityDetector 只是组件曝光触发器,不应该成为首页加载策略本身。

真正应该是:

配置驱动的预加载系统

而不是:

看到组件了才临时加载

正确模型应该是这样

后台配置里应该带加载策略:

{
  "id": "goods_recommend_001",
  "type": "goodsList",
  "loadPolicy": {
    "mode": "preload",
    "priority": 80,
    "preloadDistance": 1200,
    "trigger": "afterFirstScreen",
    "cachePolicy": "staleWhileRevalidate"
  }
}

也就是说,每个组件不是简单的 lazy,而是有一套完整策略:

class ComponentLoadPolicy {
  final ComponentLoadMode mode;
  final int priority;
  final double preloadDistance;
  final ComponentLoadTrigger trigger;
  final ComponentCachePolicy cachePolicy;
  final bool allowConcurrent;
  final Duration? timeout;
}

加载模式

enum ComponentLoadMode {
  immediate,      // 进入页面立刻加载
  firstScreen,    // 首屏优先加载
  preload,        // 根据距离提前加载
  lazy,           // 真正曝光后加载
  manual,         // 手动触发
  cacheOnly,      // 只读缓存
}

触发方式

enum ComponentLoadTrigger {
  onPageEnter,        // 页面进入
  afterLayoutReady,   // 布局配置完成
  afterFirstScreen,   // 首屏加载完成后
  nearViewport,       // 接近可视区域
  onVisible,          // 真正曝光
  onUserIdle,         // 用户空闲
  onPullRefresh,      // 下拉刷新
}

首页真实加载流程应该是

1. 进入首页
2. 读取缓存 layout
3. 请求最新 layout
4. 根据 layout 生成 ComponentLoadPlan
5. 先执行 immediate / firstScreen
6. 首屏完成后,执行 preload 队列
7. 根据组件距离 viewport 提前加载
8. 用户空闲时加载低优先级组件
9. 真正曝光时只做兜底检查

重点是:

曝光不是加载策略
曝光只是最后一道保险

推荐结构

HomeLoadPlanner
    ↓
ComponentLoadScheduler
    ↓
ComponentPreloadController
    ↓
ComponentLoader

分别负责:

HomeLoadPlanner:根据后台配置生成加载计划
ComponentLoadScheduler:按优先级、并发数、触发条件执行
ComponentPreloadController:根据滚动位置和预加载距离判断
ComponentLoader:真正请求组件数据

示例

class HomeLoadPlanner {
  List<ComponentLoadTask> buildPlan(List<HomeComponent> components) {
    final tasks = components.map((component) {
      return ComponentLoadTask(
        componentId: component.id,
        priority: component.loadPolicy.priority,
        trigger: component.loadPolicy.trigger,
        preloadDistance: component.loadPolicy.preloadDistance,
      );
    }).toList();

    tasks.sort((a, b) => b.priority.compareTo(a.priority));
    return tasks;
  }
}

执行时不是组件自己决定加载,而是 Scheduler 决定:

class ComponentLoadScheduler {
  Future<void> runInitialTasks(List<ComponentLoadTask> tasks) async {
    final firstScreenTasks = tasks.where((task) {
      return task.trigger == ComponentLoadTrigger.onPageEnter ||
             task.trigger == ComponentLoadTrigger.afterLayoutReady;
    });

    await Future.wait(
      firstScreenTasks.map((task) => loader.load(task.componentId)),
    );
  }

  void onScroll(double offset, double viewportHeight) {
    final preloadTasks = tasks.where((task) {
      return task.shouldPreload(offset, viewportHeight);
    });

    for (final task in preloadTasks) {
      loader.loadIfNeeded(task.componentId);
    }
  }
}

页面层应该变成这样

NotificationListener<ScrollNotification>(
  onNotification: (notification) {
    viewModel.onHomeScroll(
      pixels: notification.metrics.pixels,
      viewportDimension: notification.metrics.viewportDimension,
    );
    return false;
  },
  child: ListView.builder(
    itemCount: state.components.length,
    itemBuilder: (context, index) {
      final component = state.components[index];

      return HomeComponentRenderer(
        component: component,
      );
    },
  ),
)

组件不负责判断加载。

组件只负责展示:

loading
success
failed
empty

更高级一点

每个组件可以配置:

{
  "id": "flash_sale_001",
  "type": "flashSale",
  "loadPolicy": {
    "mode": "immediate",
    "priority": 100,
    "maxRetry": 2,
    "timeoutMs": 3000,
    "cachePolicy": "networkFirst",
    "preloadDistance": 0
  }
}

推荐商品:

{
  "id": "recommend_001",
  "type": "goodsList",
  "loadPolicy": {
    "mode": "preload",
    "priority": 30,
    "trigger": "afterFirstScreen",
    "preloadDistance": 1600,
    "cachePolicy": "staleWhileRevalidate"
  }
}

底部模块:

{
  "id": "brand_area_001",
  "type": "brandArea",
  "loadPolicy": {
    "mode": "lazy",
    "priority": 10,
    "trigger": "nearViewport",
    "preloadDistance": 600,
    "cachePolicy": "cacheFirst"
  }
}

所以最终应该改成

VisibilityDetector 不是核心方案
ScrollNotification + LayoutPosition + LoadPolicy 才是核心方案

最终首页架构是:

Home Layout Config
   ↓
HomeLoadPlanner
   ↓
ComponentLoadTask Queue
   ↓
ComponentLoadScheduler
   ↓
Scroll/Idle/PageEnter Trigger
   ↓
ComponentLoader
   ↓
ComponentState Update
   ↓
HomeComponentRenderer

这才符合你说的“热插拔 + 任务流 + 配置驱动”。


评论