内容简介:我觉得现在学习前端的人都知道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 属性?如何正确实现这个属性?
- 自己实现集合框架(十):顺序栈的实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming Ruby中文版
托马斯 / 孙勇、姚延栋、张海峰 / 电子工业出版社 / 2007-3 / 99.00元
《Programming Rudy》(中文版)(第2版)是它的第2版,其中包括超过200页的新内容,以及对原有内容的修订,涵盖了Ruby 1.8中新的和改进的特性以及标准库模块。它不仅是您学习Ruby语言及其丰富特性的一本优秀教程,也可以作为日常编程时类和模块的参考手册。Ruby是一种跨平台、面向对象的动态类型编程语言。Ruby体现了表达的一致性和简单性,它不仅是一门编程语言,更是表达想法的一种简......一起来看看 《Programming Ruby中文版》 这本书的介绍吧!