VUE中的模板编译是把模板通过模板编译器生成渲染函数的过程。
模板编译分为三个步骤
这三个部分分别由解析器、优化器、生成器实现。
本文主要介绍解析器,如何将模板解析,然后替换,再输出到页面上。
Compiler构造函数,用于获取指定元素的内容,并进行解析,根据内容的不同使用不同的函数进行处理。
根据node.nodeType判断节点的类型,然后使用对应的解析方法进行解析,将解析后的结果给元素赋值。
对于元素节点使用compileNode处理,对于文本节点使用compileText处理。
compileNode()方法读取元素的attribute,根据前缀判断是否为指令,如果是指令就把指令名称截取出来,并交给对应的指令函数执行,然后将函数执行结果赋值给元素。
compileText()方法读取节点的nodeContent内容,使用replace将{{}}中的数据进行替换,然后把替换后的内容赋值给nodeContent。
class Compiler{
constructor(el, vm){
// 判断el属性是不是一个元素,如果不是元素就获取元素
this.el = this.isElementNode() ? el : document.querySelector(el);
this.vm = vm;
let fragment = this.nodeToFragment(this.el);
// 把内容进行替换
// 编译模板用数据编译
this.compile(fragment);
// 把内容塞到页面中去
this.el.appendChild(fragment)
}
//是不是元素节点
isElementNode(node){
return node.nodeType === 1;
}
// 把节点移动到内存中
nodeToFragment(node){
let fragment = document.createDocumentFragment();
let firstChild ;
while(firstChild = node.firstChild){
fragment.appendChild(firstChild);
}
return fragment;
}
// 编译内存中的dom节点
compile(){
let chileNodes = node.chileNodes;
[...chileNodes].forEach(child => {
if(this.isElementNode(child)){
this.compileElement(child);
this.compile(child);
} else {
this.compileText();
}
})
}
// 解析文本节点
compileText(node){
// 判断节点的内容中是否存在{{}}
let content = node.textContent;
if(/\{\{(.+?)\}\}/.test(content)){
CompileUtil['text'](node, content, this);
}
}
// 判断是否是指令
isDirective(attrName){
return attrName.startWith('v-');
}
// 解析元素节点
compileElement(node){
let attributes = node.attributes;
[...attributes].forEach(attribute => {
let {name,value:expr} = attribute;
if(this.isDirective(name)){
let [, directive] = name.split('-');
CompileUtil[directive](node, expr, this.vm);
}
})
}
}
CompilUtil对象是一个工具对象,为Compiler提供辅助函数,主要对解析元素中的指令和插值表达式操作进行分解并对部分操作进行复用。
CompileUtil = {
// 获取指定元素的value
getValue(vm, expr){
expr.split('.').reduce((data, current) => {
return data[current];
}, vm.$data);
},
// v-model指令解析工具函数
model(node, expr, vm){
let fn = this.updater['modelUpdater'];
let value = this.getValue(vm, expr);
fn(node, value);
},
// 文本节点解析工具函数
text(node,expr, vm){
let content = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
return this.getValue(vm, args[1]);
});
},
// 更新元素的值
updater:{
modelUpdater(node,value){
node.value = value;
}
}
}
将编译器应用于Vue中
class Vue{
constructor(options){
this.$el = options.el;
this.$data = options.data;
// 根元素存在编译模板
if(this.$el){
new Compiler(this.$el, this);
}
}
}