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

LazyVGrid未懒加载:20个ViewModel被立即创建问题咨询

问题

我用SwiftUI写了如下代码,创建了包含20个RMCharacterViewModel的列表,界面每次仅显示6个单元格,但所有20个ViewModel却被立即创建。我原本预期仅会创建6个单元格并下载对应图片,请问这是什么原因?

struct ContentView: View {
    @StateObject var vm = RMCharacterListResponseDownloadViewModel()
    
    let columns = Array(repeating: GridItem(.flexible(),spacing: 1, alignment: nil), count: 2)
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(vm.items, id: \.self) { item in
                    RMCharacterView(viewModel: item)
                }
            }
            .padding(1)
        }
    }
}

struct RMCharacterView: View {
    let viewModel: RMCharacterViewModel
    private let imageWidth = 110.0
    private let cellHeight = 330.0

    var body: some View {
        CacheAsyncImage(
            url: viewModel.characterImageURL
        ) { phase in
            switch phase {
            case .success(let image):
                VStack {
                    CharacterCard(characterImage: image)
                    CharacterDescriptionView(viewModel: viewModel)
                }
            case .failure(let error):
                ErrorView(error: error)
            case .empty:
                VStack {
                    ProgressView()
                        .progressViewStyle(CircularProgressViewStyle(tint: .red))
                    Spacer()
                }
            @unknown default:
                // AsyncImagePhase is not marked as @frozen.
                // We need to support new cases in the future.
                Image(systemName: "questionmark")
            }
        }
    }
}
原因及解决方法

核心原因

LazyVGrid的懒加载只作用于View的实例化与渲染,不会控制你提前准备好的ViewModel对象的创建时机。你当前的vm.items是提前把20个RMCharacterViewModel全部初始化完成的数组,所以只要这个数组被创建,所有ViewModel就会立刻存在,和LazyVGrid是否渲染对应的Cell无关。

如果你的CacheAsyncImage是在ViewModel初始化时就触发了图片下载逻辑(比如ViewModel内部直接发起网络请求),那所有图片也会被同时触发下载;如果只是在CacheAsyncImage的body里访问URL才发起请求,那图片下载会和Cell渲染同步,但ViewModel本身还是会全部提前创建。

解决思路

不要提前创建所有ViewModel,而是把原始数据(比如RMCharacter模型对象)存在vm.items里,在RMCharacterView内部按需初始化ViewModel:

  1. 修改RMCharacterListResponseDownloadViewModel的items类型为原始模型数组(比如[RMCharacter])
  2. 修改RMCharacterView的参数为原始模型,在View内部创建ViewModel:
struct RMCharacterView: View {
    private let character: RMCharacter
    private var viewModel: RMCharacterViewModel {
        RMCharacterViewModel(character: character)
    }
    // ... 其余代码不变
}

或者在View初始化时懒加载ViewModel:

struct RMCharacterView: View {
    private let character: RMCharacter
    private lazy var viewModel: RMCharacterViewModel = RMCharacterViewModel(character: character)
    // ... 其余代码不变
}

这样只有当LazyVGrid渲染某个Cell时,对应的RMCharacterView才会被创建,ViewModel也才会随之初始化,图片下载也会同步延迟到Cell可见时触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 10:52:39