lit-virtualizer 开发指南:从源码构建、测试到基准与发布的完整贡献流程
【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit
本篇指南围绕 Lit 仓库中@lit-labs/virtualizer(基于 Lit 的视口级虚拟滚动库)的 CONTRIBUTING.md 展开,系统讲解该包的源码组织、构建流程、单元/截图测试、性能基准(Tachometer)以及 NPM 发布策略。读完本文,你将掌握如何为 lit-virtualizer 新增功能、编写测试、生成基准截图、运行基准并理解其发布产物边界,可直接按步骤在本地复现整套贡献流程。
一、仓库结构与源码布局
lit-virtualizer 的包目录位于 packages/labs/virtualizer,核心源码集中在src/下。与多数纯工具库不同,这个包的主入口除了自定义元素,还同时暴露指令与控制器 API,因此在动手改代码前,先厘清目录职责非常关键。
src 源码组织
从当前仓库的实际结构看,src/主要包含以下模块:
- src/LitVirtualizer.ts:
<lit-virtualizer>自定义元素的实现,通过 src/lit-virtualizer.ts 中的customElements.define('lit-virtualizer', LitVirtualizer)注册到全局,并声明了HTMLElementTagNameMap以提供类型提示; - src/Virtualizer.ts 与 src/ScrollerController.ts:虚拟化核心引擎与滚动控制器;
- src/virtualize.ts:
virtualize指令,把宿主元素变为虚拟化容器; - src/events.ts:
rangeChanged、visibilityChanged等事件定义; - src/layouts:布局系统,包括
flow(默认流式布局)、grid(网格布局)、masonry(瀑布流)、flexWrap以及共享的 BaseLayout.ts、GridBaseLayout.ts、SizeCache.ts 等基础设施; - src/polyfillLoaders/ResizeObserver.ts 与 src/polyfills/resize-observer-polyfill:
ResizeObserver特性检测加载器与内置 polyfill; - src/support:
resize-observer-errors.ts(处理 "ResizeObserver loop limit exceeded" 报错)与method-interception.ts等辅助工具。
需要说明的是,当前仓库的源码已采用 TypeScript 编写(对应src/**/*.ts),而非贡献指南中记载的"纯 JS ES modules",编译产物才是面向浏览器与 npm 的 ES modules,这一点在构建一节会详细展开。
uni-virtualizer 的历史渊源
贡献指南记载,历史上src/lib/uni-virtualizer存放着从父级 monorepo 的 uni-virtual 包复制而来的 uni-virtualizer 源码,并计划未来将其独立发布为 npm 包,届时 lit-virtualizer 只需直接依赖uni-virtualizer包,而不再把源码内联到lib中。从当前仓库的目录列表看,该内联目录已不存在,虚拟化核心已重构为上文提到的Virtualizer.ts、ScrollerController.ts与layouts/等模块——这正体现了"把虚拟化能力下沉为独立包"这一设计方向的演进。阅读代码时若看到与旧文档不一致的路径,以 src 的实际结构为准。
二、构建流程与 ES Modules 发布策略
npm run build 做了什么
在包目录下执行:
npm run build当前版本通过 package.json 中的 wireit 配置编排构建,它聚合了两个子任务:
build:ts:运行tsc --build --pretty,将src/**/*.ts编译为 ESM 格式的.js与.d.ts类型声明,输出到包根目录(layouts/、events.js、lit-virtualizer.js、Virtualizer.js、LitVirtualizer.js、ScrollerController.js、virtualize.js、polyfillLoaders/、support/等),该任务还依赖internal-scripts:build与lit:build;build:copy-polyfill:执行mkdir -p polyfills/resize-observer-polyfill && cp src/polyfills/resize-observer-polyfill/ResizeObserver.js polyfills/resize-observer-polyfill/ResizeObserver.js,把内置的ResizeObserverpolyfill 同步到构建产物目录。
也就是说,构建产物的"源"被组织为编译输出的 TypeScript 结果 + 原样拷贝的 polyfill 文件,二者共同构成可发布的包内容。
为什么以 ES modules 发布
该包与 Lit 本身一致,以 ES modules 形式发布,把模块解析(module resolution)的责任留给使用者。这样带来的自由度包括:
- 使用者可以自行决定打包与转译方式(是否打包、如何分包);
- 便于实现代码分割(code splitting),按需加载虚拟化逻辑;
- 保持
bare specifier形式的依赖引用,交由构建链或开发服务器解析。
代价是:在浏览器中直接使用裸模块标识符需要开发服务器具备bare specifier解析能力,这一点与 Lit 官方"Tools and Workflows"的指引一致。如果你打算给该包提交修改,请确保产物保持 ESM 形态,不要引入依赖 Node 特定模块机制的代码。
三、测试体系
lit-virtualizer 的测试分为两层:基于 Web Test Runner 的集成/单元测试,以及基于 Puppeteer 的截图回归测试。两者的入口与新增方式不同,下面分别说明。
集成测试与单元测试
运行:
npm run test即执行包内所有单元与集成测试。当前仓库中该命令经由 wireit 调度,实际调用 packages/tests/run-web-tests.js 配合 web-test-runner.config.js 运行,测试框架为Web Test Runner + Mocha + Chai,并额外执行一次 ES5 语法兼容性检查(tsc --target ES5 --noEmit --downlevelIteration)。
测试用例位于 src/test,覆盖了大量真实场景,例如:
- scrolling.test.ts:滚动行为;
- element-and-directive-parity.test.ts:
<lit-virtualizer>元素与virtualize指令的行为一致性; - layout-complete.test.ts:布局完成时机;
- hidden.test.ts、item-changes.test.ts、key-function.test.ts 等边界场景;
- src/test/layouts/flow.test.ts:
flow布局的专项测试; - src/test/support:
ResizeObserver错误处理与method-interception的单元测试。
新增单元/集成测试时,把测试文件放入src/test下对应的场景目录即可被自动收集。若你在测试中遇到 "ResizeObserver loop limit exceeded" 报错导致用例失败,可参考 src/support/resize-observer-errors.ts 提供的三个工具函数(setupIgnoreWindowResizeObserverLoopErrors、ignoreWindowResizeObserverLoopErrors、preventResizeObserverLoopErrorEventDefaults)在测试夹具中屏蔽该良性错误。
截图回归测试
截图测试用于捕获虚拟化渲染与滚动行为的像素级回归。测试用例页面位于 test/screenshot/cases,每个子目录(如lit-virtual、scroll)对应一个独立测试页。由于 lit-virtualizer 使用 ES modules,每个页面在截图测试前会先经过Rollup构建(见 test/screenshot/rollup.config.js:它读取cases/下每个目录的main.js,以cases/<name>/main.js为入口、输出 ESM 格式的cases/<name>/build产物)。
以 cases/lit-virtual/index.html 为例,页面通过<script type="module" src="build/main.js">引用构建产物,而 main.js 负责拉取共享的contacts.json数据、创建<lit-virtualizer>元素并注入items与renderItem。
运行截图测试:
npm run test:screenshot该命令等价于cd test/screenshot && rollup -c && mocha screenshot.js,由Puppeteer + Mocha + Chai驱动。测试逻辑位于 screenshot.js:每个用例启动一个 Puppeteer 浏览器,访问对应用例页,截取actual.<case>.png,再与仓库中保存的expected.<case>.png基准图用pixelmatch(阈值 0.1)逐像素比对,要求差异像素数为 0 才算通过。
新增一个截图测试用例
按贡献指南的步骤,新增截图测试的完整流程如下:
- 复用现有页面:先判断 cases 下是否已有满足需求的页面设置,能复用就复用;
- 新建页面目录:若没有,在
test/screenshot/cases/下新建目录,放入index.html与main.js搭建测试页。main.js会在构建阶段被自动打包,因此在index.html中引用构建产物(当前仓库中实际产物名为build/main.js,以实际构建输出为准); - 注册用例:在 test/screenshot/screenshot.js 中新增
describe/it测试用例,设置页面跳转地址、等待选择器与滚动动作; - 生成基准图:为便于只生成新页面的基准截图,可以临时在
describe上加.only(例如describe.only('lit-virtual', ...)),再运行npm run generate-screenshots,完成后务必移除.only。
当前仓库已含两组基准图,可作为参考:
- lit-virtual 用例基准图(初始渲染 800x600):对应"初始渲染出列表项"的期望画面;
- scroll 用例滚动定位基准图(滚动到指定位置 800x600):对应"滚动到指定索引与位置"的期望画面。
截图用例本身也在验证两个关键行为:一是列表能渲染出足够填满视口的子元素("displays items"),二是滚动后只保留视口附近的元素并正确重排("scrolls");scroll用例还覆盖了?index=100与?index=100&position=end两种 URL 参数驱动的定位场景,与scrollToIndexAPI 的行为相互印证。
重新生成基准截图
如果对 lit-virtualizer 的改动有意改变了期望画面(例如调整了间距、布局或滚动行为),需要重新生成基准图:
npm run generate-screenshots即cd test/screenshot && rollup -c && mocha screenshot.js --generate-screenshots,此时所有用例写出的图片命名为expected.<case>.png并覆盖旧基准。注意:只有确定新画面是预期行为时才应重新生成;无意的渲染回归应当修复代码而不是刷新基准。
四、性能基准测试(Tachometer)
运行基准
在包目录下执行:
npm run bench即运行基础的滚动指令基准。基准页位于 test/benchmarks/basic.html:它构造一个包含 1000 个数字项的数组,用virtualize指令配合FlowLayout渲染到页面中。当前仓库中该命令实际固定为:
tach --root=../../.. --browser=chrome-headless test/benchmarks/basic.html --measure=fcp即使用Tachometer(Polymer 团队的基准工具,仓库依赖中声明为tachometer)在 headless Chrome 中测量FCP(First Contentful Paint,首次内容绘制)。另外,package.json 还提供了npm run bench:scroll,它通过 test/benchmarks/scrollingBenchmarks.json 配置(并强制清理 npm 安装)运行滚动类基准。
贡献指南还记载了通过环境变量选择基准的用法:
BENCH=useShadowDOM npm run bench这是文档中描述的扩展方式;在查看基准相关脚本时,以 package.json 中当前实际定义的bench/bench:scroll/bench:debug脚本为准。
基准指标与改进方向
贡献指南明确指出:目前用FCP 衡量"渲染完成时间"并不理想,因为它包含了与虚拟化无关的准备开销,例如"生成待渲染列表项列表"本身所花的时间。因此理想的做法是:在 lit-virtualizer 的生命周期中找到能判定异步渲染/布局循环即将完成的那个时间点,再借助 Tachometer 的start与stop回调获得更贴近真实渲染成本的指标。
如果你关注性能优化或想改进基准的准确性,可以围绕 src/Virtualizer.ts 与 src/ScrollerController.ts 中的异步渲染/布局循环调度,寻找可暴露为"渲染完成信号"的钩子,并结合 layout-complete.test.ts 对"布局完成"语义的既有定义来设计该时间点。
五、发布到 NPM
该包作为Lit Labs实验性包发布,当前 npm 包名为@lit-labs/virtualizer(见 package.json 的name字段),因此使用npm i @lit-labs/virtualizer安装。需要留意的是,贡献指南中记载的包名lit-virtualizer属于历史版本命名,两者 API 同源但以当前发布名为准;同时它处于 late prerelease 阶段,1.0 前可能仍有(预计是机械性、易迁移的)破坏性变更。
发布产物边界
发布时只发布入口文件与运行时代码目录,不包括src/源码与测试。当前仓库通过package.json的files字段精确圈定发布内容:
- 根级入口:
lit-virtualizer.js、virtualize.js、Virtualizer.js、LitVirtualizer.js、ScrollerController.js、events.js(含对应.d.ts与.map); - 布局产物:
layouts/**(含shared/子目录); - polyfill 相关:
polyfillLoaders/**与polyfills/resize-observer-polyfill/ResizeObserver.js; - 辅助模块:
support/**。
同时exports字段把这些子路径(如./layouts/grid.js、./virtualize.js、./events.js、./polyfillLoaders/ResizeObserver.js、./support/resize-observer-errors.js)显式暴露为可导入入口,并为每个入口提供types声明。也就是说,读者既可以从主入口import '@lit-labs/virtualizer'使用<lit-virtualizer>元素,也可以按子路径引入virtualize指令、具体布局或错误处理工具。
结语
贡献 lit-virtualizer 的完整闭环是:理解 src 的模块分工 → 用npm run build产出 ESM 构建 → 用npm run test与npm run test:screenshot(必要时npm run generate-screenshots)保障行为与像素级回归 → 用npm run bench与 Tachometer 度量渲染性能 → 最后由维护者按 package.json 的files白名单发布到 npm。本文覆盖的每一步都能在当前仓库中找到对应源码或配置作为依据,是上手该包开发与贡献的可靠路线图。
【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考