ARTICLE DETAIL

资讯详情

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

一文讲清 lit-html 模板引擎的 4 个渲染阶段与 6 类 Part

一文讲清 lit-html 模板引擎的 4 个渲染阶段与 6 类 Part 一文讲清 lit-html 模板引擎的 4 个渲染阶段与 6 类 Part【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit点一次按钮只勾选了一项页面上另外 99 个 DOM 节点纹丝不动。lit-html 这个模板引擎是怎么做到的它把渲染拆成 4 个阶段只在有变化的地方写 DOM。下面逐段拆开看。先跑起来一个最小例子写一个买牛奶的小任务import {html, render} from lit; let done false; const app () html labelinput typecheckbox ?checked${done} 买牛奶/label p class${done ? off : on}${done ? 已完成 : 待办}/p button click${() {done !done; render(app(), box);}}切换/button; render(app(), box);lit-html 是一个 HTML 模板库用模板字面量写 UI静态的部分是 HTML 字符串动态的部分是 JS 值两者写在同一个模板里。它的卖点是状态变化时 DOM 操作数量最少。这段代码里的分工值得留意买牛奶、按钮文字是静态串只写一次。三个${}标记的是动态位勾选状态、class、状态文字。这种分工靠两个 Web 原生特性撑着。一是模板字面量标签函数在解析时就把静态串和动态值分开而且静态字符串数组每次求值都是同一个引用。二是template元素一个可以存 HTML 内容、之后克隆进文档的容器里面的脚本不执行、样式不生效。它是怎么渲染的首帧与更新两条不同的路官方把流程叫 4 个阶段Define、Prepare、Create、Update。按首帧 vs 更新重新归组其实是两条路。首帧两次准备一次落地Define 最轻。html标签函数只做记录静态字符串数组strings、动态值数组values、类型标识_$litType$打包成一个TemplateResult返回。一次函数调用加一次对象分配不碰 DOM。Prepare 最重只在第一次跑把静态字符串数组拼成带标记的 HTML。文本位的表达式变成注释标记!--?lit$xxx$--属性位的标记追加在属性名后后缀是$lit$。用这段 HTML 设置一个新template元素的 innerHTML让浏览器完成解析。这一步只涉及静态字符串没有动态输入不存在注入问题。遍历模板树逐个找到标记为每个动态位生成一条TemplatePart元数据记录节点的深度优先索引、表达式类型、属性名。产物是一个Template对象一个template元素加一份TemplatePart清单。Create 是把模板落到真实 DOM。克隆模板内容到 DocumentFragment再沿着克隆出来的节点走按TemplatePart逐一实例化对应的 Part。每个 Part 直接持有节点引用等后续更新。整体称为TemplateInstance。这两个阶段只在首次渲染时发生。准备和落地都是一次性成本这是 lit-html 快的来源。更新只碰变化的值之后每次状态变化只走 Update// 更新阶段每个 Part 比对新旧值 // 相等直接返回不碰 DOM AttributePart._$setValue(off); ChildPart._$setValue(已完成); EventPart._$setValue(newListener);每个 Part 记录上次提交值_$committedValue和新值做严格相等比较。相等就什么都不做不等才按各自规则写 DOMsetAttribute、属性赋值、或 addEventListener。没有整页 diff没有从虚拟 DOM 重推只有对变化值的定点写入。lit-html 的精准更新就是这么来的。注意第二条分支后续更新里Prepare 和 Create 整个被跳过。动态值落在哪六类 Part 各管一摊模板里每个${}最终都对应一个 Part 对象。lit-html 一共 6 种Part职责模板写法示意ChildPart子节点位置的动态值htmlp${name}/pAttributePart属性值位置的动态绑定htmlinput id${uid}BooleanAttributePart?前缀布尔属性真则设置、假则移除htmlinput ?checked${ok}PropertyPart.前缀直接给元素属性赋值htmlinput .value${v}EventPart前缀事件绑定htmlbutton click${fn}ElementPart元素标签位置的动态绑定html${tag}拿EventPart举例它更新时不摘掉旧监听器再重新挂而是用一个监听器代理对象包住用户函数函数变了DOM 上的监听位也不变只有 options 对象不同才真正移除重加。六类 Part 的实现都在 packages/lit-html/src/lit-html.ts 里。同一个模板只备一次货缓存机制Prepare 是最贵的阶段lit-html 保证它对每个模板只跑一次。缓存键就是TemplateResult的strings数组。JS 里模板字面量的字符串数组在同一份源码里引用稳定每次求值htmlimg src${url}拿到的都是同一个对象。第一次渲染时建好template存进缓存之后再渲染同一模板直接查表。看一个典型用法const img (url) htmlimg src${url}; render(html${img(a.jpg)} ${img(b.jpg)} ${img(c.jpg)}, root);首次渲染这一行时第一个img()走 Prepare 建出Template第二、三个查缓存拿到同一个Template各自 Create 出自己的TemplateInstance。第二次更新时三个都只走 Update。本该三次 Prepare实际一次。副作用是模板能跨组件复用只要源码里是同一个标签模板字面量就共享同一条缓存。这也是 lit-html 处理模板复用的方式。写代码时记住的三件事模板写一次多处用把模板定义成函数如上例的app()在多个容器里复用准备阶段只做一次。动态值粒度尽量小把长串拆成多个${}Update 时没变的位可以直接跳过整段则每次重建。前缀选对绑定方式给事件、.给属性、?给布尔属性、普通${}给属性各自对应不同 Part提交方式不同。小结lit-html 用模板字面量和template把渲染分成一次性准备和逐值更新Prepare 与 Create 只跑首帧之后的更新只遍历 Part、写入变化的值。细节可以看设计文档 dev-docs/design/how-lit-html-works.md 和源码 packages/lit-html/src/lit-html.ts。下一篇我们看自定义指令怎么写给 Part 扩展行为。【免费下载链接】litLit is a simple library for building fast, lightweight web components.项目地址: https://gitcode.com/GitHub_Trending/li/lit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表