Files
codex-bot 2b3b3a1ae4
Verify and deploy Tabularium / verify-and-deploy (push) Successful in 5m47s
Expand document collections with Pretext tutorial and simplify library navigation
2026-10-28 13:42:51 +08:00

4.0 KiB

第九章:把它接进界面和虚拟列表

9.1 一个完整的浏览器模块例子

先在你的前端项目中安装:

npm install @chenglou/pretext

下面是供 Vite 等支持 npm 模块导入的环境使用的 JavaScript 模块。直接打开一个 HTML 文件并不能解析这个包名。

import { prepare, layout } from '@chenglou/pretext'

const text = 'Hello 世界,今天一起读源码。'.repeat(8)
const font = '16px "Helvetica Neue", "PingFang SC", Arial, sans-serif'
const lineHeight = 24

document.documentElement.lang = 'zh-CN'
await document.fonts.load(font, text)

const prepared = prepare(text, font)
const block = document.createElement('div')
block.textContent = text
Object.assign(block.style, {
  font,
  lineHeight: lineHeight + 'px',
  overflowWrap: 'break-word',
  wordBreak: 'normal',
  whiteSpace: 'normal',
  lineBreak: 'auto',
  letterSpacing: '0px',
  wordSpacing: '0px',
  fontKerning: 'auto',
  textWrap: 'wrap',
  hyphens: 'manual',
  textRendering: 'auto',
  boxSizing: 'content-box',
  padding: '0',
  border: '0',
})
document.body.appendChild(block)

function setContentWidth(width) {
  const predicted = layout(prepared, width, lineHeight)
  block.style.width = width + 'px'
  return predicted
}

const predicted = setContentWidth(320)
console.log('预测高度:', predicted.height)
console.log('实际高度:', block.getBoundingClientRect().height)

// 模拟容器变宽:复用相同的 prepared。
setContentWidth(480)

这个例子最后的 DOM 高度读取是开发期核对。生产中如果每次布局都再读一次 DOM,仍会承担那次读取可能触发的工作。示例也假设没有其他 CSS 规则覆盖这些值,所用字体在目标设备上可用;跨平台项目应配置自己的字体。

9.2 让模型与显示使用相同输入

layout(prepared, 320, 24) 中的 320 是内容宽度。如果外框总宽 320px、左右各有 12px padding,再加边框,真正给文字使用的宽度就小于 320px。

可以由应用维护一个尺寸模型:先算出内容宽度,把同一个数同时传给 Pretext 和渲染层。这样可以减少“CSS 用一个值,JS 猜另一个值”的误差。

准备前等待具体文字所需字体加载很重要。只有 document.fonts.ready 不一定覆盖尚未使用、由 unicode-range 分片加载的字体;因此例子把 text 也传给 document.fonts.load()。

9.3 虚拟列表从哪里获得收益

假设有 10,000 条消息,屏幕上只显示 20 条:

每条消息保留 prepared
    → 按当前内容宽度求各条高度
    → 累计高度,确定每条消息顶部的位置
    → 按滚动位置找可见范围
    → 只挂载可见消息与少量缓冲区

Pretext 解决的是“没挂载的文字怎样求高度”。累计位置、查找可见范围、滚动锚定、图片高度、选择与无障碍行为,仍由应用或虚拟列表库负责。

窗口变宽时,每条消息通常仍需要重新布局。因此大量消息的整表 resize 成本不会凭空消失。准备与布局分离,让这次更新的单位成本更低。

9.4 编辑与富文本如何处理

长文本编辑时,可以按段落保留 prepared,只重新准备被编辑影响的段落;有跨段结构变化时,应用还要更新相应块。

混合字体、提及标签和行内图片使用 @chenglou/pretext/rich-inline。这个版本把富文本作为一个段落分析,保留 item 与分段的映射,然后复用布局基础设施,而不是把每个 span 完全独立换行。

富文本还要考虑垂直尺寸:字体大小、上升部与下降部、行内盒子高度都可能影响真实行高。不要直接把所有富文本的行数乘一个固定值,当成任意 CSS 的精确高度。可继续读项目自己的 Markdown 聊天案例说明。

本章检查:Pretext 是否已经替你完成虚拟列表?没有,但它提供了此前很难在挂载前获得的文本几何信息。