由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏。等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码
一、声明式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;}}
}