/** * 第 9 章配套代码 · 性能优化 demo(骨架屏 + 延后渲染 + 帧率监控) * * 文件位置:shared/src/commonMain/kotlin/com/example/kuikly/pages/PerfDemoPage.kt * * 演示 4 个性能优化技巧: * 1. 骨架屏(first paint 速度) * 2. 拆 body:首屏可见 vs 折叠以下 * 3. 延后渲染(pageDidAppear 后再挂) * 4. 帧率监控 */ package com.example.kuikly.pages import com.tencent.kuikly.core.annotations.Page import com.tencent.kuikly.core.base.Color import com.tencent.kuikly.core.base.ViewBuilder import com.tencent.kuikly.core.directives.vfor import com.tencent.kuikly.core.module.TimerModule import com.tencent.kuikly.core.pager.Pager import com.tencent.kuikly.core.reactive.collection.observableListOf import com.tencent.kuikly.core.reactive.handler.observable import com.tencent.kuikly.core.views.List import com.tencent.kuikly.core.views.Text import com.tencent.kuikly.core.views.View @Page("PerfDemoPage") internal class PerfDemoPage : Pager() { // ─── 状态字段 ───── private var firstLoading by observable(true) // 首屏是否还在加载骨架屏 private var deferredLoaded by observable(false) // 折叠以下内容是否已挂载 private var currentFps by observable(60f) // 当前帧率(演示用) private val items = observableListOf() private val timer by lazy { acquireModule(TimerModule.MODULE_NAME) } private var fpsTaskId: Int = -1 private var deferredTaskId: Int = -1 override fun created() { super.created() // 模拟首屏数据加载(500ms 后骨架屏消失) timer.schedule(500) { (1..50).forEach { items.add("信息流 Item #$it") } firstLoading = false } } override fun pageDidAppear() { super.pageDidAppear() // 折叠以下内容延后 800ms 挂载(启动优化技巧 ④) deferredTaskId = timer.schedule(800) { deferredLoaded = true } // 启动 FPS 监控(每秒模拟一次) fpsTaskId = timer.scheduleInterval(1000) { currentFps = (50..60).random().toFloat() } } override fun pageWillDestroy() { super.pageWillDestroy() // ⚠️ 必须取消定时器,避免内存泄漏 if (deferredTaskId >= 0) timer.cancel(deferredTaskId) if (fpsTaskId >= 0) timer.cancel(fpsTaskId) } override fun body(): ViewBuilder { val ctx = this return { attr { backgroundColor(Color(0xFFF5F5F7L)) } // ─── FPS 状态条 ───── View { attr { height(36f); flexDirectionRow(); alignItemsCenter() paddingHorizontal(16f) backgroundColor( if (ctx.currentFps < 50f) Color(0xFFFFEBEEL) else Color(0xFFE8F5E9L) ) } Text { attr { text("FPS: ${ctx.currentFps.toInt()} · " + if (ctx.currentFps >= 55f) "✅ 流畅" else "⚠️ 掉帧") fontSize(12f) color(if (ctx.currentFps < 50f) Color.RED else Color(0xFF388E3CL)) fontWeightBold() } } } // ─── 主内容区 ───── if (ctx.firstLoading) { renderSkeleton().invoke(this) } else { renderContent(ctx).invoke(this) } } } /** 骨架屏:占位 5 个灰色 block */ private fun renderSkeleton(): ViewBuilder = { View { attr { padding(16f); backgroundColor(Color(0xFFF5F5F7L)); flex(1f) } repeat(5) { View { attr { height(80f); marginBottom(8f) backgroundColor(Color(0xFFE0E0E0L)) borderRadius(8f) } } } Text { attr { text("骨架屏渲染中…") fontSize(12f) color(Color(0xFF9E9E9EL)) textAlignCenter() marginTop(20f) } } } } /** 真实内容(含首屏 + 折叠以下) */ private fun renderContent(ctx: PerfDemoPage): ViewBuilder = { List { attr { flex(1f); paddingHorizontal(16f); paddingTop(8f) } // ─── 首屏可见区(立即渲染)───── View { attr { backgroundColor(Color.WHITE); padding(16f) borderRadius(8f); marginBottom(12f) } Text { attr { text("首屏 Hero 区") fontSize(18f); fontWeightBold() color(Color.BLACK) } } Text { attr { text("此区域立即渲染,是 first paint 的关键。") fontSize(13f) color(Color(0xFF757575L)) marginTop(4f) } } } // ─── 长列表(List + vfor 复用)───── Text { attr { text("信息流(List + vfor)") fontSize(14f); fontWeightBold() marginVertical(8f) } } vfor({ ctx.items }) { item -> View { attr { height(64f); flexDirectionRow(); alignItemsCenter() paddingHorizontal(12f); marginBottom(4f) backgroundColor(Color.WHITE); borderRadius(6f) } Text { attr { text(item); fontSize(14f); flex(1f) color(Color(0xFF424242L)) } } Text { attr { text("›"); fontSize(20f); color(Color(0xFFBDBDBDL)) } } } } // ─── 折叠以下内容:延后挂载 ───── if (ctx.deferredLoaded) { renderDeferredSection().invoke(this) } else { View { attr { padding(16f); allCenter() } Text { attr { text("⏳ 折叠以下内容延后加载中…") fontSize(12f) color(Color(0xFF9E9E9EL)) } } } } } } /** 折叠以下:评论区 / 推荐区,等首屏稳了再挂 */ private fun renderDeferredSection(): ViewBuilder = { View { attr { marginTop(16f); padding(16f) backgroundColor(Color.WHITE); borderRadius(8f) } Text { attr { text("📝 评论区(延后渲染)") fontSize(15f); fontWeightBold() color(Color.BLACK); marginBottom(8f) } } Text { attr { text("此区域在 pageDidAppear 后 800ms 才挂载,避免阻塞首屏渲染。") fontSize(12f) color(Color(0xFF757575L)) lineHeight(18f) } } } } }