内容简介:我觉得现在学习前端的人都知道MVVM是什么意思,现在主流的框架React、Vue都使用的MVVM的原理,今天我们就来实现一下MVVM其中的一小部分查看是否是元素效果
我觉得现在学习前端的人都知道MVVM是什么意思,现在主流的框架React、Vue都使用的MVVM的原理,今天我们就来实现一下MVVM其中的一小部分 Compie指令解析 。
- compile就是解析的意思,解析我们使用的模板语法。我这篇文章主要是仿照实现Vue的Compile,
效果
- Vue实现的效果
<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>"
}
});
复制代码
模拟实现
- 我们自定义一个MVVM的类,来模拟new Vue的过程。
let vm = new MVVM({
el: "#app",
data: {
a: 1,
b: {
c: 2
},
c: "<p>56165</p>"
}
});
复制代码
- MVVM
class MVVM {
constructor(options) {
this.$el = options.el; // 挂载点
this.$data = options.data; // 数据
new Compile(this.$el, this.$data); // 解析模板
}
}
复制代码
- 解析模板
- 查看传入的el是元素还是字符串
- 如果是字符串,根据class或id查找元素
- 如果是元素直接使用
- 如果元素存在,将元素加入到内存中,解析模板
- 查看传入的el是元素还是字符串
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;
}
复制代码
效果
元素都加入到了内存中,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 解析文本
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 获取文本指令的值
- 如果为级联属性,a.b.c需要一层一层的获取值
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 解析元素属性指令的值
- 获取元素的所有属性
- 如果有遍历每一个属性
- 获取到属性的名字 如: class、v-model(这里没有实现v-bind以及缩写的形式)
- 根据名字获取对应处理的函数,获取对应的值
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); 复制代码
总结
- 简单的实现了一个Compile的过程,知识是实现了一些简单的指令解析,还没有实现v-bind、v-on以及它们的缩写形式:、@。
- 重点再于理解,不只是知道和会用,还要学会灵活的运用以及知道他们简单的原理并能加以实现。
- 如果有兴趣的可以看一下我的另两篇文章
以上所述就是小编给大家介绍的《实现一个简单的MVVM(Compile)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- php如何实现session,自己实现session,laravel如何实现session
- AOP如何实现及实现原理
- webpack 实现 HMR 及其实现原理
- Docker实现原理之 - OverlayFS实现原理
- 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?
- 自己实现集合框架(十):顺序栈的实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
500 Lines or Less
Amy Brown、Michael DiBernardo / 2016-6-28 / USD 35.00
This book provides you with the chance to study how 26 experienced programmers think when they are building something new. The programs you will read about in this book were all written from scratch t......一起来看看 《500 Lines or Less》 这本书的介绍吧!