lit-virtualizer 开发指南:从源码构建、测试到基准与发布的完整贡献流程
2026/9/13 15:06:55 网站建设 项目流程

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:rangeChangedvisibilityChanged等事件定义;
  • 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.tsScrollerController.tslayouts/等模块——这正体现了"把虚拟化能力下沉为独立包"这一设计方向的演进。阅读代码时若看到与旧文档不一致的路径,以 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.jslit-virtualizer.jsVirtualizer.jsLitVirtualizer.jsScrollerController.jsvirtualize.jspolyfillLoaders/support/等),该任务还依赖internal-scripts:buildlit: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 提供的三个工具函数(setupIgnoreWindowResizeObserverLoopErrorsignoreWindowResizeObserverLoopErrorspreventResizeObserverLoopErrorEventDefaults)在测试夹具中屏蔽该良性错误。

截图回归测试

截图测试用于捕获虚拟化渲染与滚动行为的像素级回归。测试用例页面位于 test/screenshot/cases,每个子目录(如lit-virtualscroll)对应一个独立测试页。由于 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>元素并注入itemsrenderItem

运行截图测试:

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 才算通过。

新增一个截图测试用例

按贡献指南的步骤,新增截图测试的完整流程如下:

  1. 复用现有页面:先判断 cases 下是否已有满足需求的页面设置,能复用就复用;
  2. 新建页面目录:若没有,在test/screenshot/cases/下新建目录,放入index.htmlmain.js搭建测试页。main.js会在构建阶段被自动打包,因此在index.html中引用构建产物(当前仓库中实际产物名为build/main.js,以实际构建输出为准);
  3. 注册用例:在 test/screenshot/screenshot.js 中新增describe/it测试用例,设置页面跳转地址、等待选择器与滚动动作;
  4. 生成基准图:为便于只生成新页面的基准截图,可以临时在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 的startstop回调获得更贴近真实渲染成本的指标。

如果你关注性能优化或想改进基准的准确性,可以围绕 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.jsonfiles字段精确圈定发布内容:

  • 根级入口:lit-virtualizer.jsvirtualize.jsVirtualizer.jsLitVirtualizer.jsScrollerController.jsevents.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 testnpm 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询