HTML模板
一:
二:
三:
4:
class Compiler {
constructor(el, vm) {
this.el =this.isElementNode(el) ? el : document.querySelector(el);
this.vm = vm;
const fragment =this.compileFragment(this.el);
this.compile(fragment);
this.el.appendChild(fragment);
}
compile(fragment) {
const childNodes = Array.from(fragment.childNodes);
childNodes.forEach(childNode => {
if (this.isElementNode(childNode)) {
// 标签节点 h1 / input,读取属性,查看是否有 v- 开头的内容
this.compileElement(childNode);
}else if (this.isTextNode(childNode)) {
// 内容文本节点 {{ msg }} 是否有双括号语法
this.compileText(childNode);
}
if (childNode.childNodes && childNode.childNodes.length) {
this.compile(childNode);
}
});
}
compileElement(node) {
// v-model v-text v-on:click
const attributes = Array.from(node.attributes);
attributes.forEach(attr => {
const { name, value } = attr;
if (this.isDirector(name)) {
// 指令v-model, v-text, v-bind, v-on:click
const [, directive] = name.split('-');
const [compileKey, eventName] = directive.split(':');
utils[compileKey](node, value, this.vm, eventName);
}else if (this.isEventName(name)) {
// @ 方法执行
const [, eventName] = name.split('@');
utils['on'](node, value, this.vm, eventName);
}
})
}
isDirector(name) {
return name.startsWith('v-');
}
isEventName(name) {
return name.startsWith('@');
}
compileText(node) {
// {{ msg }}
const content = node.textContent;
if (/\{\{(.+)\}\}/.test(content)) {
utils['text'](node, content, this.vm);
}
}
compileFragment(el) {
const f = document.createDocumentFragment();
let firstChild;
while(firstChild = el.firstChild) {
f.appendChild(firstChild);
}
return f;
}
isTextNode(el) {
return el.nodeType ===3;
}
isElementNode(el) {
return el.nodeType ===1;
}
}
五:
六:
let TARGET =null;
const utils = {
getValue(expr, vm) {
return vm.$data[expr.trim()];
},
setValue(expr, vm, newValue) {
vm.$data[expr] = newValue;
},
model(node, value, vm) {
const initValue =this.getValue(value, vm);
new Watcher(value, vm, (newValue) => {
this.modelUpdater(node, newValue);
});
node.addEventListener('input', (e) => {
const newValue = e.target.value;
this.setValue(value, vm, newValue);
});
this.modelUpdater(node, initValue);
},
text(node, value, vm) {
let result;
if (value.includes('{{')) {
// {{ xxx }}
result = value.replace(/\{\{(.+?)\}\}/g, (...args) => {
const expr = args[1];
new Watcher(expr, vm, (newVal) => {
this.textUpdater(node, newVal);
})
return this.getValue(args[1], vm);
});
}else {
// v-text="xxx"
result =this.getValue(value, vm);
}
this.textUpdater(node, result);
},
on(node, value, vm, eventName) {
const fn = vm.$options.methods[value];
node.addEventListener(eventName, fn.bind(vm), false);
},
textUpdater(node, value) {
node.textContent = value;
},
modelUpdater(node, value) {
node.value = value;
}
}