我以为如今学习前端的人都知道MVVM是什么意思,如今主流的框架React、Vue都使用的MVVM的原理,今天咱们就来实现一下MVVM其中的一小部分Compie指令解析。html
<div id="app">
<input type="text" v-model="a">
<p>{{ b.c }}</p>
<div v-html="c"></div>
</div>
let vm = new Vue({
el : "#app",
data: {
a: 1,
b: {
c: 1
},
c: "<p>165165</p>"
}
});
复制代码
<div id="app">
<ul>
<input type="text" v-model="b.c">
<li>{{ a }}</li>
<li>
<span>9</span>
</li>
<div v-html="c"></div>
<div v-html="<b>56156</b>"></div>
{{ b.c }}
</ul>
</div>
let vm = new MVVM({
el: "#app",
data: {
a: 1,
b: {
c: 2
},
c: "<p>56165</p>"
}
});
复制代码
let vm = new MVVM({
el: "#app",
data: {
a: 1,
b: {
c: 2
},
c: "<p>56165</p>"
}
});
复制代码
class MVVM {
constructor(options) {
this.$el = options.el; // 挂载点
this.$data = options.data; // 数据
new Compile(this.$el, this.$data); // 解析模板
}
}
复制代码
class Compile {
constructor(el, data) {
this.$el = this.isElement(el) ? el : document.querySelector(el);
this.$data = data;
if (this.$el) {
// 第一步:加入内存
let fragment = this.toFragment(this.$el);
// 第二步:解析指令
this.compileDirect(fragment);
// 第三步:加入到DOM中
this.$el.appendChild(fragment);
}
}
}
复制代码
查看是不是元素前端
isElement(node) {
// 1表明是元素
return node.nodeType === 1;
}
复制代码
toFragment(element) {
// 建立文档碎片
let fragment = document.createDocumentFragment();
let firstChild;
// 逐个获取第一个元素加入到碎片中,直到文档中没有子元素
while (firstChild = element.firstChild) {
fragment.appendChild(firstChild);
}
return fragment;
}
复制代码
效果 node
compileDirect(el) {
let children = el.childNodes;
[...children].forEach(child => {
if (this.isElement(child)) {
// 解析元素
this.compileNode(child);
// 遍历子元素
this.compileDirect(child);
} else {
// 解析文本
this.compileText(child);
}
});
}
复制代码
2.1 建立工具类算法
compileUtil = {
// v-text || 文本
text(node, value) {
node.textContent = value;
},
// v-model
model(node, value) {
node.value = value;
},
// v-html
html(node, value) {
node.innerHTML = value;
}
}
复制代码
2.2 解析文本bash
compileText(node) {
// 获取文本内容,{{ a }}
let text = node.textContent;
if (text) {
// 正则匹配, 是否包含指令,{{ a }}
let value = text.match(/{{([^}])+}}/g);
if (value) {
// 获取 {{ a }} 里面的数据: a
value = value[0].replace(/{{([^}]+)}}/g, '$1').trim();
compileUtil["text"](node, this.getValue(value));
}
}
}
复制代码
2.2.1 获取文本指令的值app
getValue(value) {
value = value.split(".");
// 第一次为 this.$data["a"]
// 第二次为 this.$data["a"]["b"]
// 第三次为 this.$data["a"]["b"]["c"]
return value.reduce((prevRes, next) => {
return prevRes[next];
}, this.$data);
}
复制代码
2.3 解析元素属性指令的值框架
compileNode(node, data) {
let attrs = [...node.attributes];
if (attrs.length > 0) {
attrs.forEach(attr => {
// v-model → model
// v-text → text
// v-html → html
let type = attr.name.slice(2);
// 若是有对应的函数则处理
compileUtil[type] && compileUtil[type](node, this.getValue(attr.value) || attr.value);
});
}
}
复制代码
this.$el.appendChild(fragment);
复制代码