拒绝虚拟dom!lit-html模板原理探索
前言
lite-html模板是简单的JavaScript,它结合了编写HTML的熟悉程度和JavaScript的强大功能。lite-html负责高效地将模板呈现给DOM,包括高效地用新值更新DOM。
这是从readme里复制出来,然后有道翻译了一下。因为lit就是使用lit-html来写的,所以花了一定时间去阅读了源码,看看大佬们是怎么实现这个模板语言的。
开始
模板字符串知识
模板字符串作为es6较为常用的功能,相信大家都不陌生。但是平时使用的时候,可能就是为了书写方便,把变量用${}
包起来直接书写。但是其实,它还有分割变量和字符串的功能。
从控制台输入这段代码,可以发现,使用简单的tag
函数,就能将模板中的字符串和变量分离出来。
由此可以看出,模板字符串中是内置了这些属性的。因此同一个模板字符串分离出来的的strings
数组对象肯定是相等的,这就是后面会说到的lit-html
用来缓存模板信息的原理。
简单示例分析
const a = 'aaaa'; const b = 'bbbb'; const c = 111; const t = (name) => html`<div id="div" ${b}="222" data-name="111" .obj=${c} title="111${b}222"> <!--name${name}--> ${name}${name} <span obj=${c}>${a}</span> <input id="ipt" type="text" .value=${c}> <script> console.log(1111) ${a} console.log(2222) ${a} console.log(3333) </script> </div>`; const values = t('dd'); render(values, document.body); 复制代码
这段模板中包含了lit-html
中的一些简单应用。
lit-html 中的 render
在把模板字符串的信息传入到render
函数中后,首先它会把strings
重新拼接,并把变量的位置,用lit-html
生成的随机字符串替换出来。
接着,再把变量相关的Attr
从模板中分离出来。区分出不同的type
,推入到parts
的数组中,在后面根据不同的type
用对应的函数对元素上的属性进行操作。index
对应的则是node
的下标。
到这一步可以说是已经将所有静态的元素模板分离出来了。
剩下要做的就是循环parts
数组,将他们中的part
和最开始模板字符串中values
相组合,然后修改元素的内容和attrs
就可以。
其实这个思想和vue3
中很相似,把静态的dom看成常量,只分离出那些需要变更的那些元素,然后进行更新。
缓存
缓存是肯定要做的。但是理解了上面的做法之后,其实就很好理解。所有模板相关的内容都可以缓存下来,在需要渲染时候,调用_update(values)
更新dom就可以了。
可以看看代码中是怎么做缓存的。
首先会在,父级元素中保存一份childPart
的实例。控制台中打印一下也能看到。
其次,在模板实例的储存上,
因为只有同一个模板字符串的strings
是相同,所以跟template
实例也是一一对应的,可以放心的存入cache
中,在更新时候读取。
最后,如果template
实例,和上次templateInstance
中的template
实例相同时,可以说明是同一个模板,只需要调用_update(values)
就可以实现dom的更新了,和开头说的一样。
结语
其实忽略了很多中间转化过程中的代码,包括怎么标记变量的位置,怎么将不同type
的part
转换成dom中所需要的样子,只讲了大致的思路和实现方法。有兴趣的大哥们可以去看下源码,就1000多行,也不是很长。
其实对我来说,最大的收获是学会了模板字符串的新用法和使用document.createTreeWalker
遍历node元素的方法,和这种分离出静态常量和变量,从而对指定内容进行更新的思路和方法。
lit-element
实际上就是对web-components
各种生命周期的重新封装和扩展,应该不会专门再水一篇文章进行分析了。空的话还是继续写一写组件,毕竟已经一个多月没写了。人类果然无法战胜懒惰=-。
作者:懒狗小前端
链接:https://juejin.cn/post/7056291294397759519