ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在 Lit 中使用 @lit-labs/observers:用 Reactive Controller 集成 Web 平台 Observer API

在 Lit 中使用 @lit-labs/observers:用 Reactive Controller 集成 Web 平台 Observer API 在 Lit 中使用 lit-labs/observers用 Reactive Controller 集成 Web 平台 Observer API【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/litlit-labs/observers是 Lit 官方实验室Lit Labs发布的一组 Reactive Controller它把MutationObserver、ResizeObserver、IntersectionObserver、PerformanceObserver四个 Web 平台观察器封装为与 Lit 响应式更新生命周期无缝对接的控制器。本文以该包在仓库 packages/labs/observers 中的文档、源码与测试为基准逐一讲解四个控制器的安装、构造参数、公开属性与方法并结合实现细节与测试用例说明其生命周期管理与状态处理原理帮助你在自己的 Lit 组件中直接落地观察 DOM/尺寸/可见性/性能指标 → 触发响应式更新 → 渲染结果的完整链路。[!NOTE] 该包属于 Lit Labs 系列以公开形式发布是为了收集设计反馈未来可能引入破坏性变更或停止维护。在生产环境使用前请先阅读 Lit Labs 相关说明。概述为什么需要为 Observer 写 Controller现代 Web 平台提供了多种 observer 工具MutationObserver、ResizeObserver、IntersectionObserver、PerformanceObserver应用可以用它们探测各种变化并作出反应。但直接使用这些 observer 时开发者需要自己处理几类繁琐问题手动把变化结果搬运进组件状态再触发一次渲染在元素连接/断开时手动开始或停止观察避免内存泄漏与无效回调在渲染期间发生的变更需要及时取回takeRecords否则会丢失或延迟。lit-labs/observers的思路是用 Lit 的 ReactiveController 管理一个平台 observer把上述样板逻辑全部收进控制器内部。控制器通过hostConnected/hostDisconnected/hostUpdated等生命周期钩子与宿主通常是一个ReactiveElement或LitElement联动宿主连接时自动开始观察宿主断开时自动disconnect()清理宿主更新后把观察到的 pending 记录及时投递。每一次观察到变化控制器都会调用宿主的requestUpdate()把变化无缝并入 Lit 的响应式更新周期。每个控制器还支持一个可选的callback函数观察器报告的变化会先交给callback处理成一个任意类型的值存放在控制器的value属性上。这样在render()里可以直接读取this._observer.value作为渲染依据做到UI f(state)。安装在项目目录内执行$ npm install lit-labs/observers该包的依赖为lit/reactive-element^1.0.0 || ^2.0.0并以lit、lit-html作为开发依赖见 package.json因此可以与 Lit 2/3 配合使用。包采用 ESM 模块格式并在exports中为每个控制器提供了独立的子路径导出lit-labs/observers/intersection-controller.jslit-labs/observers/mutation-controller.jslit-labs/observers/performance-controller.jslit-labs/observers/resize-controller.js从源码结构看包入口 src/index.ts 目前仅包含许可声明四个控制器分别维护在 src/intersection-controller.ts、src/mutation-controller.ts、src/performance-controller.ts、src/resize-controller.ts实际使用时按子路径导入即可。IntersectionController检测元素可见性与交叉状态IntersectionController把一个IntersectionObserver挂到宿主上每当 IntersectionObserver 观察到目标元素的交叉状态发生变化时控制器就请求宿主更新。它也可以把每次变化处理成一个任意值存储起来。Importimport {IntersectionController} from lit-labs/observers/intersection-controller.js;构造函数constructor( host: ReactiveControllerHost Element, {target, config, callback, skipInitial}: IntersectionControllerConfigT )注意第一个参数是ReactiveControllerHost Element控制器需要宿主同时满足可响应更新与可作为默认观察目标两个条件。配置项IntersectionControllerConfig配置项类型说明configIntersectionObserverInit传给 IntersectionObserver 的配置对象如root、rootMargin、threshold。targetElement \| null可选要观察的元素。不指定时默认观察host显式设为null时不自动观察任何元素除配置的 target 外还可以通过observe方法观察更多目标。只有配置的这个 target 会在宿主断开后又重新连接时被重新观察。callbackIntersectionValueCallbackT可选用于把观察到的变化处理成存储在value属性上的值。skipInitialboolean可选默认情况下当 target 被观察时会以无变化调用一次callback用于初始化状态设为true可跳过这一步。从源码看IntersectionValueCallback的类型为(...args: ParametersIntersectionObserverCallback) T即回调可以拿到IntersectionObserverEntry[]与IntersectionObserver两个参数见 intersection-controller.ts。属性与方法成员说明value?: T通过callback处理观察器变化后得到的结果。observe(target: Element)观察指定元素。宿主连接时配置的target会被自动观察。unobserve(target: Element)停止观察指定元素。disconnect()断开观察器。宿主断开时会自动调用。实现要点与生命周期从源码intersection-controller.ts可以看到它与普通 IntersectionObserver 用法之间的关键差异连接即观察hostConnected()遍历_targets集合逐个observehostDisconnected()调用disconnect()。因此把元素移出文档再放回观察会自动恢复。更新期不丢事件hostUpdated()中调用this._observer.takeRecords()把宿主更新期间产生的交叉记录急切投递避免记录积压。初始状态特殊处理IntersectionObserver 在observe时就会上报一次初始交叉状态。控制器用_unobservedUpdate标志配合skipInitial跳过这次观察即触发的初始回调observe()中注释也明确指出调用 observe 总会触发一次 callback因为初始交叉状态会被上报。SSR 与降级构造时先判断isServer来自lit-html/is-server.js服务端直接返回再检查window.IntersectionObserver是否存在不存在时输出console.warn提示浏览器不支持。仓库测试src/test/intersection-controller_test.ts覆盖了这些行为默认上报初始状态、skipInitial: true时初始值为undefined且后续变化仍被上报、元素 remove/re-connect 期间不报变化、可观察外部元素target: d、通过callback取entries[0]?.isIntersecting管理布尔值、target: null时仅通过observe()观察指定元素等。MutationController响应 DOM 变化MutationController把一个MutationObserver挂到宿主上每当 MutationObserver 观察到 DOM 变化时请求宿主更新。它同样可以把每次变化处理成一个任意值。示例import {MutationController} from lit-labs/observers/mutation-controller.js; // ... class MyElement extends LitElement { private _observer new MutationController(this, { config: {attributes: true}, }); render() { return html ${this._observer.value ? Attributes set! : } ; } }Importimport {MutationController} from lit-labs/observers/mutation-controller.js;构造函数new MutationControllerT unknown( host: ReactiveControllerHost Element, {target, config, callback, skipInitial}: MutationControllerConfigT )类型参数T既是value属性的类型也是callback选项的返回类型。MutationControllerConfig配置项类型说明configMutationObserverInit必填传给 MutationObserver 的配置对象如attributes、childList、subtree、attributeFilter等。targetElement \| null可选要观察的元素语义与 IntersectionController 相同默认hostnull表示不自动观察重连时只重新观察配置的 target。callbackMutationValueCallbackT可选处理MutationRecord[]为value。skipInitialboolean可选默认在 target 被观察时以无变化调用一次callback设为true跳过。MutationValueCallback的类型为(...args: ParametersMutationCallback) T见 mutation-controller.ts。属性与方法成员说明value通过callback处理观察器变化后得到的结果。observe(target: Element)观察指定元素。宿主连接时配置的target会被自动观察。disconnect()断开观察器。宿主断开时自动调用。与 IntersectionController 不同MutationObserver本身没有unobserve语义因此这里不提供unobserve方法。实现要点初始状态即零变更回调源码 mutation-controller.ts 中一个值得注意的设计在hostUpdated()async hostUpdated() { // Eagerly deliver any changes that happened during update. // And handle initial state as a set of 0 changes. This helps setup initial // state and promotes UI f(state) since ideally the callback does not // rely on changes. const pendingRecords this._observer.takeRecords(); if ( pendingRecords.length || (!this._skipInitial this._unobservedUpdate) ) { this.handleChanges(pendingRecords); } this._unobservedUpdate false; }即MutationObserver在调用observe时不会像 IntersectionObserver 那样上报初始状态因此控制器在observe()中设置_unobservedUpdate true并请求一次宿主更新待hostUpdated时以0 条变更调用一次callback来初始化value。这刻意把初始状态建模成空变更集鼓励 callback 写成不依赖具体 change 记录的纯函数UI f(state)。测试用例can manage value via callback也展示了这种用法callback 从MutationRecord[]中提取addedNodes的标签名数组作为渲染数据见 mutation-controller_test.ts。此外observe(target)会把this._config一并传给底层observer.observe(target, config)因此同一个控制器观察多个目标时使用同一份 MutationObserverInit。PerformanceController采集性能指标PerformanceController把一个PerformanceObserver挂到宿主上每当 PerformanceObserver 收到新的性能指标时请求宿主更新。Importimport {PerformanceController} from lit-labs/observers/performance-controller.js;构造函数constructor( host: ReactiveControllerHost, {config, callback, skipInitial}: PerformanceControllerConfigT )与其余三个控制器不同PerformanceController 没有target概念观察的是全局性能缓冲而非某个元素因此host类型仅为ReactiveControllerHost不必是 Element。PerformanceControllerConfig配置项类型说明configPerformanceObserverInit必填传给 PerformanceObserver 的配置对象例如{entryTypes: [paint, largest-contentful-paint]}或{type: resource}。callbackPerformanceValueCallbackT可选把性能条目处理成value。skipInitialboolean可选默认在开始观察时以无变化调用一次callback设为true跳过。PerformanceValueCallback的签名与其他控制器略有不同(entries: PerformanceEntryList, observer: PerformanceObserver, entryList?: PerformanceObserverEntryList) T即除了条目数组与 observer 外还额外传入原始的PerformanceObserverEntryList见 performance-controller.ts。属性与方法成员说明value通过callback处理观察器变化后得到的结果。observe()开始观察无参数。宿主连接时自动调用。disconnect()断开观察器。宿主断开时自动调用。flush()手动冲刷所有 pending 的 observer 更新内部takeRecords()后处理并请求宿主更新。flush()是 PerformanceController 独有的方法因为性能指标按entryTypes/type过滤且可能批量到达在需要立即读取最新指标而不等待下一轮更新时可以直接调用它。hostUpdated()同样会在更新期间把takeRecords()拿到的 pending 条目立即投递给 callback。测试中也能看到observer.flush()的调用见 performance-controller_test.ts。ResizeController检测元素尺寸变化ResizeController把一个ResizeObserver挂到宿主上每当 ResizeObserver 检测到目标尺寸变化时请求宿主更新。控制器同样可以计算并存储任意值。Importimport {ResizeController} from lit-labs/observers/resize-controller.js;构造函数constructor( host: ReactiveControllerHost Element, {target, config, callback, skipInitial}: ResizeControllerConfigT )ResizeControllerConfig配置项类型说明configResizeObserverOptions可选传给 ResizeObserver 的配置对象目前主要支持box如content-box、border-box。注意这里是可选项。targetElement \| null可选要观察的元素语义同前默认hostnull表示不自动观察。callbackResizeValueCallbackT可选处理ResizeObserverEntry[]为value。skipInitialboolean可选默认在 target 被观察时以无变化调用一次callback设为true跳过。属性与方法成员说明value?: T通过callback处理观察器变化后得到的结果。observe(target: Element)观察指定元素。宿主连接时配置的target会被自动观察。unobserve(target: Element)停止观察指定元素。disconnect()断开观察器。宿主断开时自动调用。target(observe?: boolean)返回一个元素指令directive自动观察指令所应用的元素。亮点target()元素指令ResizeController 独有的target(observe?: boolean)方法返回一个基于AsyncDirective实现的元素指令实现见 resize-controller.ts。它的行为是直接用在模板元素的属性位置例如div ${this.observer.target(selected)}/div指令会自动对该元素调用controller.observe(...)当指令被移除、宿主或目标元素断开、或observe参数为false时自动unobserve该元素disconnected()/reconnected()钩子保证元素移出/回到文档时观察状态同步。测试 resize-controller_test.ts 中this.observer.target(selected)的用法展示了如何把尺寸观察目标绑定到模板中的一个动态元素上适合自适应布局、按容器宽度切换渲染策略等场景。ResizeController 的hostUpdated()不取takeRecords()ResizeObserver回调本身是异步批量派发的只在_unobservedUpdate为真且未设置skipInitial时以空 entries 初始化一次value。生命周期与通用设计四个控制器的共同模式从四份源码可以归纳出这套控制器共有的实现模式可作为自行编写 observer 控制器时的参考范本构造期保存 config默认把target ?? host加入_targets集合创建底层 observer回调统一为handleChanges(entries) → value callback?.(...)再this._host.requestUpdate()随后host.addController(this)把自己注册进宿主。构造期会先判断isServerSSR 下直接返回并检查浏览器 API 是否存在缺失时console.warn降级。连接管理hostConnected()重新观察所有目标hostDisconnected()统一disconnect()避免组件移出文档后仍持有观察器造成泄漏。更新期投递hostUpdated()通过takeRecords()把更新期间产生的事件急切投递保证渲染循环中不丢事件。初始状态以空变更集调用一次callback初始化valueskipInitial: true可跳过IntersectionObserver 因本身会上报初始交叉状态用_unobservedUpdate标志做区分处理。泛型类型所有控制器均为ControllerTT约束value与callback返回值。测试中的类型检查用例如 intersection-controller_test.ts验证了IntersectionControllernumber拒绝返回string的 callback、value赋值受类型约束等编译期行为。典型实战组合以下代码把上述控制器组合到一个元素中同时响应属性变化、尺寸变化与可见性变化import {LitElement, html} from lit; import {MutationController} from lit-labs/observers/mutation-controller.js; import {ResizeController} from lit-labs/observers/resize-controller.js; import {IntersectionController} from lit-labs/observers/intersection-controller.js; class MyElement extends LitElement { // 观察自身属性变化value 记录是否被设置过属性 private _mutation new MutationController(this, { config: {attributes: true}, }); // 观察自身尺寸value 记录最新宽度 private _resize new ResizeController(this, { callback: (entries) entries[0]?.contentRect.width, skipInitial: true, // 初始尺寸在 connectedCallback 后自然会触发一次 }); // 观察自身可见性value 记录是否在视口内 private _intersection new IntersectionController(this, { callback: (entries) entries[0]?.isIntersecting, }); render() { return html pAttributes set: ${this._mutation.value ? yes : no}/p pWidth: ${this._resize.value ?? measuring…}/p pVisible: ${this._intersection.value ? yes : no}/p ; } } customElements.define(my-element, MyElement);要点_mutation默认以空变更初始化value可直接参与渲染_resize用skipInitial避免重复处理初始尺寸_intersection的 callback 直接复用 IntersectionObserverEntry 的isIntersecting。所有控制器都会在元素断开时自动清理观察器无需手动管理。参与贡献欢迎通过仓库的 CONTRIBUTING.md 了解本项目的贡献流程与开发约定。该包自身的构建与测试脚本含 dev/prod 两套 web-test-runner 测试可在 package.json 中查看。【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表