mini-vue代码已放到github上。
compiler类功能
- 负责编译模版,解析指令/差值表达试
- 负责页面的首次渲染
- 当数据变化后重新渲染视图
实现compiler类
下面我们将这个类的基本方法实现
class Compiler {
constructor(vm) {
this.el = vm.$el;
this.vm = vm;
this.complie(this.el);
}
// 编译模版,处理文本节点以及元素节点
compile(el) {
}
// 编译文本节点以及差值表达式
compileText(node) {
}
// 编译元素节点,处理指令
compileNodeElement() {
}
// 判断是否为指令
isDirective(attrName) {
return attrName && attrName.startsWith('v-');
}
// 判断是否为文本节点
isTextNode(node) {
return node.nodeType === 3
}
isElementNode(node) {
return node.nodeType === 1
}
}
compile方法实现
首先我们将el下面的childNodes拿到,之后循环处理childNodes,通过node的类型来判断,如果是text类型则调用compileText方法,如果是元素类型则调用compileNodeElement方法.
compiler(el) {
let childNodes = el.childNodes;
// 此时childNodes是一个类数组,我们将他转化成数组,然后进行遍历。
Array.from(childNodes).forEach(node => {
if(this.isTextNode(node)) {
this.compileText(node);
} else if(this.isElementNode(node)) {
this.compileNodeElement(node)
}
// 判断当前节点是否有子节点,如果存在子节点则递归调用compiler方法进行处理
if(node.childNodes && node.childNodes.length > 0) {
this.compiler(node)
}
})
}
compileText方法实现
compileText方法的主要功能是处理处理差值表达式,通过正则判断,如果是差值表达试,则将真正对应的值赋值到当钱节点上。
compileText(node) {
// 通过textContent属性
let value = node.textContent;
let reg = /\{\{(.+?)\}\}/;
if(reg.test(value)) {
let key = RegExp.$1.trim();
node.textContent = value.replace(reg, this.vm[key]);
}
}
compileNodeElement方法实现
compileNodeElement方法的主要功能是处理指令,通过获取指令,进行不同的指令操作。
compileNodeElement(node) {
let attributes = node.attributes;
Array.from(attributes).forEach((attr) => {
let attrName = attr.name;
if(this.isDirecive(attrName)) {
// 处理指令的v-。例如v-text 得出text
attrName = attrName.substr(2);
// 通过attr.value获取attr的值,例如v-text='msg' 则获取msg,之后用来替换成真正的值
let key = attr.value;
this.update(node, key, attrName)
}
})
}
update(node, key, attrName) {
let updateFn = this[`${attrName}Updater`];
updateFn && updateFn(node, this.vm[key]);
}
// 处理v-text指令
textUpdater(node, value) {
node.textContent = value;
}
// 处理v-model指令
modelUpdater(node, value) {
node.value = value;
}
这里我们只处理了v-model以及v-text,如果之后还有其他指令,则只需添加方法来处理相应的指令。