本文主要简单聊聊 lit-html 的核心原理


在之前,我了解到的只有 React 的虚拟 dom。
"每次渲染生成新树 -> 对比两棵树 -> 找出差异 -> 更新真实 dom"

但是, lit-html 走了一条完全不同的路。没有虚拟 dom,不进行复杂的树对比,却能够实现极高的性能


# 核心一:ES6 的 -- 标签模板字面量

ES6 的一个新特性,标签模板字面量。
当写出如下代码时

const name = "ay";
html`<h1>Hello, ${name}</h1>`;

html 其实是一个函数。浏览器在处理这段代码时,会把模板拆解成两部分传给这个函数

  1. Strings: ["<h1>Hello, ", "</h1>"] ;
  2. Values: ["ay"] ;

关键:无论这个组件重新渲染多少次,静态字符串数组在内存中时同一个引用,永远不变

lit-html 区分出了静态字符串和动态数据,只处理后者

# 核心二:原生 <template> 元素

有了静态字符串,就可以用原生的 <template> 元素来存储这些字符串
<template> 是 HTML 标准中的一个特殊标签。它里面的内容会被浏览器解析,但不会渲染,也不会执行脚本。就类似于一个模板

# 第一次渲染流程

假设有如下代码

let count = 0;
const renderTemplate = () => html`<div>Count: ${count}</div>`;

第一次调用 render 时,lit-html 会做如下几步

  1. 预处理与占位
    lit-html 拿到静态字符串,他会把变量的位置用一个特殊的占位符填上
    生成的模板为
    <template>
      <div>Count: </div>
    </template>
  2. 缓存
    lit-html 会用那个静态字符串数组作为 key,缓存这个 <template> 元素
    下次渲染时就不需要解析 HTML 字符串了
  3. 克隆与寻址
    现在要显示内容。lit-html 会把 <template> 的内容克隆一份,然后通过 TreeWalker 来遍历这个克隆后的 DOM 树,找到所有的占位符
  4. 创建 Parts
    当找到占位符时,它会创建一个 Part 对象,这个对象手里拿着对这个具体 DOM 节点的直接引用
    此时,他就不需要去 DOM 树中找节点, Part 对象就直达节点

# 核心三:精确更新

当状态改变, count 变成 1,再次调用 render

  1. 识别: html 函数运行,检查静态字符串数组是否变化
  2. 跳过:完全跳过 HTML 解析、DOM 创建、DOM 遍历
  3. 对比数据
  4. 直达更新:找到这个位置的 Part 对象,直接更新节点的内容

# 总结

  1. 它利用 ES6 模板字符串 来区分静态和动态
  2. 它利用 HTML <template> 来高效克隆
  3. 它利用 闭包和对象引用 (Parts) 来避免昂贵的 DOM 查询

就好比一个有着明确目标的修理工,他会直达在 DOM 树中需要修复的地方,然后直接对这个节点进行操作,而不是去遍历整个树

更新于 阅读次数