我以爲如今學習前端的人都知道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
元素都加入到了內存中,DOM中沒有子元素。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);
複製代碼