由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏。等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码
一、声明式HTML
<div id="app">
<h3>Hello,{{personName}},你在{{msg}}吗?</h3>
<div v-text="msg" v-on:click="handleShowTip"></div>
<div v-text="msg" @click="handleShowMsg(msg, personName)"></div>
<div v-html="htmlStr"></div>
</div>
<script src="src/MVue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
let vm = new MVue({
el: "#app",
data: {
msg: "学习MVVM原理",
htmlStr: "<h4>你学的怎么样?</h4>",
personName: "张三",
test: {
name: "张三"
}
},
methods: {
handleShowMsg(key) {
this.msg = "hhx";
},
handleShowTip() {
console.log(this);
}
}
})
</script>
二、编译器处理实例
class Compile {
constructor(el, vm) {
this.vm = vm;
this.el = this.isElementNode(el) ? el : document.querySelector(el);
// 1.获取文档碎片对象,放入内存中会减少页面的回流和重绘
const fragment = this.node2Fragment(this.el);
// 2.编译模板
this.compile(fragment);
// 3.追加子元素到根元素
this.el.appendChild(fragment)
}
compile(fragment) {
// 1.获取子节点
const childNodes = fragment.childNodes;
childNodes.forEach(child => {
if (this.isElementNode(child)) {
// 编译元素节点
this.compileElement(child);
} else {
// 编译文本节点
this.compileText(child);
}
if (child.childNodes && child.childNodes.length) {
this.compile(child);
}
})
}
compileText(node) {
const content = node.textContent;
if (/\{\{(.+?)\}\}/.test(content)) {
compileUntil["text"](node, content, this.vm);
}
}
compileElement(node) {
const attrs = node.attributes;
[...attrs].forEach((attr) => {
const {
name,
value
} = attr;
console.log(attr);
if (this.isDirective(name)) {
const [, diractive] = name.split("-");
const [dirName, eventName] = diractive.split(":");
// 更新数据,数据驱动视图
compileUntil[dirName](node, value, this.vm, eventName);
// 删除标签上的指令
node.removeAttribute("v-" + diractive);
}
if (this.isEvent(name)) {
const [, eventName] = name.split("@");
compileUntil["on"](node, value, this.vm, eventName);
}
});
}
isEvent(attrName) {
return attrName.startsWith("@");
}
isDirective(attrName) {
return attrName.startsWith("v-");
}
node2Fragment(el) {
// 创建文档碎片
const f = document.createDocumentFragment();
let firstChild;
while (firstChild = el.firstChild) {
f.appendChild(firstChild);
}
return f;
}
isElementNode(node) {
return node.nodeType === 1
}
}
三、编译器解析工具
const compileUntil = {
getValue(expr, vm) {
// 切分对象属性,不断的累加到目的属性
return expr.split(".").reduce((data, currentVal) => {
return data[currentVal];
}, vm.$data);
},
text(node, expr, vm) {
let value;
// 判断是否存在变量参数, 如果存在则将变量对象值替换掉声明变量
if (expr.indexOf("{{") !== -1) {
value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
return this.getValue(args[1], vm);
});
} else {
value = this.getValue(expr, vm);
}
// 更新视图
this.updater.textUpdater(node, value);
},
html(node, expr, vm) {
const value = this.getValue(expr, vm);
this.updater.htmlUpdater(node, value);
},
model(node, expr, vm) {
const value = this.getValue(expr, vm);
this.updater.modelUpdater(node, value);
},
bind(node, expr, vm, attrName) {
},
on(node, expr, vm, eventName) {
let param = [];
let fnName = expr.replace(/\((.+?)\)/g, (...args) => {
param = args[1].split(",");
return "";
});
let fn = vm.$options.methods && vm.$options.methods[fnName];
node.addEventListener(eventName, fn.bind(vm, ...param), false);
},
updater: {
textUpdater(node, value) {
node.textContent = value;
},
htmlUpdater(node, value) {
node.innerHTML = value;
},
modelUpdater(node, value) {
node.value = value;
}
}
}