Vue原理:HTML至AST核心流程

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

Vue是怎样将HTML转译为AST的,其大致过程是比较复杂的,在开始真正的编写代码之前,先来看一个简单的编译例子

首先明确这么一段HTML进行编译

<div id="app">
  <p>hello</p>
</div>
复制代码

编译为AST(简易版本)

image-20211113225903811.png

通过结果可以反推出第一个工具函数:创建AST根对象

const createASTElement = (tagName, attrs) => {
  return {
    tag: tagName, // 标签名
    type: ELEMENT_TYPE, // 节点类型
    children: [], // 子节点
    attrs, // 属性
    parent: null, // 父节点
  };
}
复制代码

每个AST的对象都具有type属性,代表当前节点的DOM类型,标签节点为1,文本节点为3

const ELEMENT_TYPE = 1;
const TEXT_TYPE = 3;
复制代码

流程详解

上述的HTML在处理开始时,被传入进来拿到的是一段字符串

let html = '<div id="app"><p>hello</p></div>';
复制代码

在开始解析前需要定义两个变量

let currentParent = null; // 标识当前的parent
let stack = [];
复制代码

开始解析上述字符串,一开始便会通过正则startTagOpen判断其是一个开始标签,可以获取到其标签名,且type为1

这个时候便遇到了问题,如何继续获取其对应的属性?

Vue中的处理方式是:截取字符串,通过截取html,然后将截取后到数据继续递归的处理,比如上述解析到<div是一个开始标签,可以明确知道其需要截取长度是4,得到新的html字符串

 html = 'id="app"><p>hello</p></div>';
复制代码

由于在解析的过程中需要不断的对html进行截取,可将其进行封装

const advance = (n) => {
  html = html.substring(n);
};
复制代码

继续对html进行处理,正则attribute匹配到数据,可以得到其属性的namevalue

let attr = html.match(attribute);
const name = arr[1];
const value = attr[3] || attr[4] || attr[5];
复制代码

至此对于开始标签的处理完成,通过上述可以创建对应的AST节点,对于所有的开始标签处理提取到函数start

const start = (tagName, attrs = []) => {
  // 遇到开始标签 创建一个 ast 元素
  let element = createASTElement(tagName, attrs);

  if (!root) {
    root = element;
  }

  // 把当前元素标记为 parent
  currentParent = element;
  // 将开始标签 存放到栈中
  stack.push(element); 
};
复制代码

完成之后需要再次调用advance进行截取html,此时html

 html = '><p>hello</p></div>';
复制代码

继续处理,正则startTagClose匹配到数据,说明此时起是一个标签的结束符号>,代表当前这次解析完成

继续解析遇到p标签,与上述流程一致,一直到将其解析完成,这个时候html

 html = 'hello</p></div>';
复制代码

此时可以判断其为文本节点,直接创建对应的AST,将其提取为函数chars

const chars = (text) => {
  // 移除多余的空格
  text = text.replace(/\s/g, "");

  if (text) {
    return {
      text,
      type: TEXT_TYPE,
    };
  }
};
复制代码

此时stack

stack = ['divAST', 'pAST'];
复制代码

目前html

 html = '</p></div>';
复制代码

此时通过判断其为结束标签,且可以在stack找到对应的开始标签节点的位置,依次进行收尾的处理,至此整个HTMLAST的大致流程便树立完成,其中还有大量的细节并没有进行处理,例如对于注释节点的处理等

Guess you like

Origin juejin.im/post/7032099879065387044