
lit-html 模板引擎原理首次渲染与增量更新各做了什么【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit给一个计数器组件加交互点一下按钮数字就变界面不卡。背后的原因值得拆开看lit-html 这类模板引擎在状态变化时并不会重写整段 DOM而是把解析一次、更新多次做到了机制层面。本文从它的首次渲染与增量更新流程入手讲清楚 lit-html 的 DOM 更新机制。它为什么轻lit-html 的定位很小一个 HTML 模板库只负责把静态 HTML 字符串 动态 JS 值变成 DOM并管理后续更新。它的全部性能设计都来自两个浏览器原生特性。一是标签模板字面量tagged template literals提供了把静态字符串和动态值显式分离的语法二是template元素提供了可以存放 HTML、之后整体克隆进文档的离屏容器。前者让哪些部分是固定的有了稳定可复用的表示后者让批量创建 DOM 可以交给浏览器解析器而不是逐个createElement。一次渲染到底发生了什么首次渲染模板解析与 Part 绑定第一次对某段模板调用render()时需要做三件事解析模板、实例化绑定、提交数值。const ui (n) htmlspan class${n % 2 ? odd : }${n}/span button click${() render(ui(n 1), host)}Increment/button; render(ui(0), host);html标签函数本身几乎不做事它把静态字符串数组和动态值数组打包成一个TemplateResult。真正的解析发生在准备阶段_$getTemplate把字符串数组拼接成带标记的 HTML——文本位置插入形如!--?lit$xxx$--的注释标记属性位置则把属性名改写成class$lit$这类带哨兵字符串的形式后者还能防止style等属性在含标记时被浏览器提前解析。然后设置template元素的innerHTML让浏览器一次性解析出完整节点树再深度优先遍历这棵树把每个标记记录成TemplatePart元数据节点下标、绑定类型、属性名。这一步只用静态字符串不碰任何动态值所以也天然没有 XSS 风险。接下来克隆模板内容到一个文档片段为每个TemplatePart实例化对应的Part对象它们持有对真实节点的引用等着接收值。整个过程封装为TemplateInstance。后续更新新旧值对比与精准写入点击按钮触发第二次render()时流程完全不同render()发现容器上已挂有 lit-html 的ChildPart直接取缓存的Template发现模板相同就跳过克隆剩下的只有遍历 Part 数组、逐个_$setValue(新值)。每个 Part 都记着上次提交的值_$committedValue新值与旧值做比较相等就直接跳过。你会发现一次渲染里真正落到 DOM 上的可能只有几次setAttribute、一次文本节点更新或者什么都不是。列表滚动这类高频场景下这就是页面没卡的直接原因。下面这种长列表渲染的效果正建立在只提交变化的基础上——滚动时 lit-html 只需更新被滚出视野的条目不同绑定位置的不同处理Part 类型由绑定在模板中的位置决定每种解决一个具体问题Part一句话解释写法EventPart管理事件监听用包装对象复用同一个 listener函数引用变了也不重复增删监听器click${fn}ChildPart管这里放什么内容字符串转文本节点、节点直接插入、数组逐项渲染、嵌套TemplateResult递归渲染${...}在子节点位置AttributePart管属性值单绑定或多段拼接最终都是一次setAttributeid${...}PropertyPart直接给 DOM 属性赋值而不是写字符串.value这类会绕过输入校验的场景靠它.value${...}BooleanAttributePart布尔开关真值加空属性假值删属性不产生多余写入?disabled${...}ElementPart元素标签位置的表达式本身不提交内容留给指令如动画、ref介入input ${ref(...)}同一个模板只用一次不会也做不到——同一个模板字面量在页面上每次求值都会产生新的TemplateResult但准备阶段只做一次靠的就是缓存。缓存键选字符串数组关键在于它的引用稳定性JS 引擎对同一个模板字面量同一位置、同一字符串内容会复用同一个 strings 对象即使动态值每次都不同。所以点击第 100 次按钮产生的TemplateResult其strings与第 1 次完全同引用直接命中缓存跳过解析与克隆。反过来不同字面量的 strings 天然是不同对象按引用作键也不会冲突。这就是同一处 UI 代码 一份模板准备成本的底层保证。把原理用起来的三个建议保持模板位置固定。缓存键来自字面量本身所以让同一段 UI 始终走同一个字面量不要在运行时拼字符串再喂给unsafeHTML否则每次都是缓存未命中等于重新解析模板。留意哪些更新会真正触达 DOM。ChildPart 对TemplateResult会递归比较值没变不重渲染但html\${{fresh: true}}这类每次都新建对象的位置配合classMap 等指令可以精确控制写入范围避免整段重排。别期待值变了等于DOM 变了。相等的值一律跳过而 EventPart 对函数引用变了也不做增删监听器。理解这条边界能帮你判断某次状态变化为什么没有产生真实 DOM 写入。小结lit-html 把昂贵的工作解析模板、克隆 DOM压在首次渲染把高频路径留给比较 提交全部构建在标签模板字面量和template之上几乎没有额外抽象。想继续深入可以读设计文档或直接翻 lit-html 源码中Template、TemplateInstance与各 Part 类的实现。【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考