mirror of
https://github.com/2234839/web-font.git
synced 2026-09-04 23:02:27 +08:00
全量 walk O(全树) 改为事件驱动增量: - property.change 携带 target(PropertyEvent 五参构造)→ 精确记脏节点 - child.add/remove 携带 child → 整棵子树入脏集(构造期赋值不走 setAttr) - 订阅集与 Leafer 自家 Watcher 相同(property.change + child.*), 渲染可靠 = 事件可靠;不再订阅 layout.end(每帧触发,百万级下 周期性全量 walk 是持续卡顿源) - 脏节点 >512 退化全量(模板整体替换场景全量更省) - 全量/增量共用 collect/walkInto/commit,指纹短路保证无变化时 update 调用归零 - 新增 provider 透传(测试/私有部署)、flushNow(跳过防抖立即提交)、 updateCalls 计数(量化指纹短路效果) 压力测试(_prof_million.entry.ts,1M Text 节点): - 初始全量 scan: ~580ms(一次性) - 打字稳态(emit+flush): 0.024ms/键 - 无文字变化 flush ×100: 0.16ms,update=0 - 内存: 指纹表 O(family 数),与节点数无关 真实 leafer 验证(scripts/test-leafer-incremental.mts): 初始添加/改文字/新增子树全自动捕获,拖拽 50 次 0 请求 0 提交。 附带修复 dev 后端启动:llrt_lr.ts 改动态 import + fsReady (createRequire 与 tsx ESM loader 模块图隔离导致 implInterface 注册到孤儿实例,mkdir undefined)。
89 lines
4.1 KiB
TypeScript
89 lines
4.1 KiB
TypeScript
/**
|
||
* 真实 leafer 环境增量路径验证
|
||
*
|
||
* 桩测试(_prof_million.entry.ts)验证调度逻辑,本测试验证真实 leafer
|
||
* 事件驱动链路:真实 @leafer-ui/node + 真实 HTTP 子集 API(localhost:8087)
|
||
* 1. 新增 Text(child.add 子树入脏集)→ 自动触发子集请求
|
||
* 2. 改 text(property.change 增量)→ 只提交新字符
|
||
* 3. 不调 refresh() 的全自动路径(验证事件驱动,非手动全量)
|
||
*
|
||
* 运行:npx tsx scripts/test-leafer-incremental.mts
|
||
* 前置:pnpm dev:backend(8087)
|
||
*/
|
||
import { Leafer, Text, Group, useCanvas } from '@leafer-ui/node'
|
||
import { Canvas as NapiCanvas, GlobalFonts, loadImage } from '@napi-rs/canvas'
|
||
|
||
/** 必须在创建任何 Leafer 实例之前初始化 napi canvas 平台 */
|
||
useCanvas('napi', { Canvas: NapiCanvas, loadImage })
|
||
|
||
const { WebFontPlugin } = await import('../packages/leafer-x-webfont/src/index.ts')
|
||
|
||
const BASE_URL = 'http://localhost:8087'
|
||
|
||
async function main(): Promise<void> {
|
||
console.log('=== 真实 leafer 增量路径验证 ===\n')
|
||
|
||
let fetchCount = 0
|
||
const realFetch = globalThis.fetch
|
||
/** 劫持 fetch 计数子集请求数 */
|
||
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
|
||
if (url.includes('/api?font=')) fetchCount++
|
||
return realFetch(input as never, init as never)
|
||
}) as typeof fetch
|
||
|
||
const leafer = new Leafer({ width: 400, height: 300, fill: '#ffffff' })
|
||
const webfont = new WebFontPlugin(leafer as never, { baseUrl: BASE_URL, debounceMs: 0, debug: false })
|
||
|
||
/* ---------- 1. 事件驱动初始化(不调 refresh,纯 waitViewReady 初始 scan) ---------- */
|
||
const text1 = new Text({ text: '静心', fontFamily: '令东齐伋复刻体.ttf', fontSize: 64, fill: '#000', x: 20, y: 20 })
|
||
leafer.add(text1)
|
||
await webfont.ready()
|
||
console.log(`[1] 初始 Text(waitViewReady 全量 scan): fetch=${fetchCount}, updateCalls=${webfont.updateCalls}`)
|
||
console.log(`[1] fontFamily 改写: ${JSON.stringify(text1.fontFamily)}`)
|
||
|
||
/* ---------- 2. property.change 增量:改文字只提交新字符 ---------- */
|
||
const before = fetchCount
|
||
text1.text = '静心茶舍新'
|
||
await new Promise((r) => setTimeout(r, 300))
|
||
console.log(`[2] 改 text(property.change 增量): 新增 fetch=${fetchCount - before}, updateCalls=${webfont.updateCalls}`)
|
||
console.log(`[2] fontFamily 链: ${JSON.stringify(text1.fontFamily)}`)
|
||
|
||
/* ---------- 3. child.add 增量:新增 Text 自动捕获 ---------- */
|
||
const before3 = fetchCount
|
||
const text2 = new Text({ text: '新品上市', fontFamily: '令东齐伋复刻体.ttf', fontSize: 32, fill: '#333', x: 20, y: 120 })
|
||
leafer.add(text2)
|
||
await new Promise((r) => setTimeout(r, 300))
|
||
console.log(`[3] 新增 Text(child.add 增量): 新增 fetch=${fetchCount - before3}, updateCalls=${webfont.updateCalls}`)
|
||
|
||
/* ---------- 4. Group 子树新增(验证子树 walk) ---------- */
|
||
const before4 = fetchCount
|
||
const group = new Group()
|
||
group.add(new Text({ text: '全场八折', fontFamily: '令东齐伋复刻体.ttf', fontSize: 24, fill: '#666', x: 20, y: 180 }))
|
||
leafer.add(group)
|
||
await new Promise((r) => setTimeout(r, 300))
|
||
console.log(`[4] 新增 Group 子树(子树 walk): 新增 fetch=${fetchCount - before4}, updateCalls=${webfont.updateCalls}`)
|
||
|
||
/* ---------- 5. 无文字变化:拖拽模拟(改 x/y 不触发 update) ---------- */
|
||
const before5 = fetchCount
|
||
const calls5 = webfont.updateCalls
|
||
for (let i = 0; i < 50; i++) {
|
||
text1.x = 20 + i
|
||
await new Promise((r) => setTimeout(r, 2))
|
||
}
|
||
await new Promise((r) => setTimeout(r, 300))
|
||
console.log(`[5] 拖拽 50 次(无文字变化): 新增 fetch=${fetchCount - before5}, 新增 updateCalls=${webfont.updateCalls - calls5}`)
|
||
|
||
/* ---------- 6. 导出验证 ---------- */
|
||
await webfont.ready()
|
||
const out = (await leafer.export('png', { pixelRatio: 1 })) as { data: string }
|
||
console.log(`[6] 导出 PNG: ${Math.round(out.data.length / 1024)}KB (base64)`)
|
||
|
||
globalThis.fetch = realFetch
|
||
webfont.destroy()
|
||
leafer.destroy()
|
||
console.log('\n=== 验证完成 ===')
|
||
}
|
||
|
||
await main()
|