这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战
在Vue
的渲染流程中,若是用户没有传递render
函数,则需要通过template
或者el
的DOM结构编译处理生成render
函数,生成render
函数是一项繁杂且重要的事情,其主要做的事情就是将模版编译为AST
,基于AST
进行操作生成虚拟DOM
,最终返回需要的render
函数
得到compileToFunction
函数的核心流程
export function compileToFunction(template) {
// 1. 解析HTML 生成AST
const ast = parseHTML(template);
// optimize(ast, options)
// 2. 根据 AST 生成 render函数
const { render } = generator(ast);
// render 返回的是虚拟DOM
return {
ast,
render,
};
}
复制代码
可以看到将模版转换为render
函数的过程并不复杂,并没有想象的那么多操作,接下来便根据核心流程继续
第一步需要做的是将HTML
编译生成AST
,在Vue
中采用的是通过正则匹配解析模板生成最终需要的AST
,在开始parseHTML
函数之前,有必要对其中使用的一些正则表达式进行了解,知道大致使用了哪些正则,每个正则做了什么事情
解析HTML元素
匹配解析html
,最常用的正则便是可以解析标签,html
元素的标签大多数是正对出现的:开始标签和结束标签(闭合标签),也存在img
这类不存在结束标签的单标签。在vue
中肯定存在组件,那么对于组件这类的编写方式也需要匹配
用于匹配html元素,例如:div、span这类,也可以匹配自定义组件,例如:el-button、el-input-number这类
const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`;
复制代码
匹配命名空间标签,例如:<div:name></div>
会将div:name
匹配
const qnameCapture = `((?:${ncname}\\:)?${ncname})`; // <asd:asd>
复制代码
有了上述的正则,便可以开始快乐的匹配开始标签和结束标签了
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 </div>
复制代码
属性
用于配置html
标签上的属性
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
复制代码
用于匹配当前是否存在属性,并取出键值
const temp = `class="btn-blue"`;
const attr = temp.match(attribute);
复制代码
若是attr
有值,则表示存在属性,然后便可以将属性取出
const name = attr[1]; // class
const value = attr[3] || attr[4] || attr[5]; // btn-blue
复制代码
注释
代码中经常会添加注释(虽然很多人不在HTML
中写注释),需要将注释进行匹配处理
const comment = /^<!\--/;
const conditionalComment = /^<!\[/;
复制代码
上述的这些便是在解析HTML生成AST时需要的正则表达式