mirror of
https://github.com/2234839/web-font.git
synced 2026-09-04 23:02:27 +08:00
问题(1M Text 节点实测): - v0 字体注册回调里全树改写 fontFamily:百万次 property.change 污染 dirty 集,打字场景退化 20s;单点改动 978ms - 公共 setter 路径(v2):百万次完整属性变更管线 = 2.2s 风暴 方案(v4): - 贡献者回记:commit 时合并累加每个 family 的贡献节点(不覆盖, 迟到 chunk 仍能找到贡献者),chunk 回调只把贡献者标脏——链应用 范围从全树缩小到实际用字节点,打字场景 O(1) - 静默/公共写分流:溢出(初始全量等贡献者>512)走静默写 __ 数据层 (~50ns/次,不触发事件不污染 dirty);少量贡献者走公共 setter (leafer 自动局部重绘该节点) - ready() 语义闭环:字体全部落地后同步消化残留 dirtyAll/renderPending, 否则会被下一个不相关的用户操作捡走——表现为「改一个字卡 800ms」 - forceRender 收敛:仅静默写/浏览器模式标记 renderPending,flush 尾部 无 in-flight chunk 时合并成一次执行 实测(1M Text,真实 leafer + 本地后端): - 单点改动 flush: 856ms → 0.15ms - 随机100节点 flush: 0.9ms - 新增 Text: 0.08ms - 打字同步按键成本: 0.2ms/次(与无插件基线 0.012ms 同数量级) - 拖拽等无文字变化操作: 0 请求 0 提交
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
/**
|
||
* 归因对照:百万节点下连续赋值 text 的成本,插件开 vs 关
|
||
*
|
||
* 疑问:压测里「连续打字50次」1M 节点耗时 ~3s,需确认这是 leafer 自身
|
||
* setter(text 改动 → 布局失效)的固有成本,还是插件监听引入的。
|
||
*
|
||
* 运行:npx tsx scripts/test-leafer-million-baseline.mts
|
||
*/
|
||
import { Leafer, Text, Group, useCanvas } from '@leafer-ui/node'
|
||
import { Canvas as NapiCanvas, loadImage } from '@napi-rs/canvas'
|
||
import { performance } from 'node:perf_hooks'
|
||
|
||
useCanvas('napi', { Canvas: NapiCanvas, loadImage })
|
||
|
||
const POOL = '静心茶舍天地玄黄宇宙洪荒'
|
||
|
||
async function buildTree(total: number) {
|
||
const leafer = new Leafer({ width: 800, height: 600, fill: '#fff' })
|
||
const GROUPS = 100
|
||
const PER = Math.floor(total / GROUPS)
|
||
const allTexts: InstanceType<typeof Text>[] = []
|
||
for (let g = 0; g < GROUPS; g++) {
|
||
const group = new Group()
|
||
for (let i = 0; i < PER; i++) {
|
||
const t = new Text({ text: POOL[i % POOL.length]!, fontFamily: 'sans-serif', fontSize: 12, fill: '#000', x: (i % 80) * 10, y: Math.floor(i / 80) * 14 })
|
||
group.add(t)
|
||
allTexts.push(t)
|
||
}
|
||
leafer.add(group)
|
||
}
|
||
return { leafer, allTexts }
|
||
}
|
||
|
||
async function main(): Promise<void> {
|
||
const TOTAL = 1_000_000
|
||
console.log(`=== 无插件基线:${TOTAL.toLocaleString()} 节点下 50 次 text 赋值 ===`)
|
||
const { leafer, allTexts } = await buildTree(TOTAL)
|
||
const victim = allTexts[Math.floor(allTexts.length / 2)]!
|
||
await new Promise((r) => setTimeout(r, 200))
|
||
|
||
const t0 = performance.now()
|
||
for (let i = 0; i < 50; i++) {
|
||
victim.text = '连续打字测试' + POOL[i % POOL.length]! + i
|
||
}
|
||
const noPluginMs = performance.now() - t0
|
||
console.log(`[无插件] 50 次赋值: ${noPluginMs.toFixed(1)}ms (${(noPluginMs / 50).toFixed(1)}ms/次)`)
|
||
leafer.destroy()
|
||
}
|
||
|
||
await main()
|