实现一个简单的MVVM(Compile)

栏目: 编程工具 · 发布时间: 5年前

内容简介:我觉得现在学习前端的人都知道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>"
    }
});
复制代码
实现一个简单的MVVM(Compile)
实现一个简单的MVVM(Compile)
  • 我们模拟实现的效果
<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(Compile)
实现一个简单的MVVM(Compile)

模拟实现

  • 我们自定义一个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查找元素
      • 如果是元素直接使用
    • 如果元素存在,将元素加入到内存中,解析模板
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;
}
复制代码
  1. 加入到内存
toFragment(element) {
    // 创建文档碎片
    let fragment = document.createDocumentFragment();
    let firstChild;
    // 逐个获取第一个元素加入到碎片中,直到文档中没有子元素
    while (firstChild = element.firstChild) {
    	fragment.appendChild(firstChild);
    }
    return fragment;
}
复制代码

效果

实现一个简单的MVVM(Compile)

元素都加入到了内存中,DOM中没有子元素。

  1. 解析指令
  • 获取子节点
    • 如果子节点是元素,解析元素,递归遍历子元素。
    • 如果是文本,解析文本。
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 解析元素属性指令的值

  • 获取元素的所有属性
    实现一个简单的MVVM(Compile)
  • 如果有遍历每一个属性
    实现一个简单的MVVM(Compile)
  • 获取到属性的名字 如: class、v-model(这里没有实现v-bind以及缩写的形式)
    实现一个简单的MVVM(Compile)
  • 根据名字获取对应处理的函数,获取对应的值
实现一个简单的MVVM(Compile)
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);
        });
    }
}
复制代码
  1. 插入到文档中
this.$el.appendChild(fragment);
复制代码
实现一个简单的MVVM(Compile)

总结

  • 简单的实现了一个Compile的过程,知识是实现了一些简单的指令解析,还没有实现v-bind、v-on以及它们的缩写形式:、@。
  • 重点再于理解,不只是知道和会用,还要学会灵活的运用以及知道他们简单的原理并能加以实现。
  • 如果有兴趣的可以看一下我的另两篇文章

以上所述就是小编给大家介绍的《实现一个简单的MVVM(Compile)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Ruby中文版

Programming Ruby中文版

托马斯 / 孙勇、姚延栋、张海峰 / 电子工业出版社 / 2007-3 / 99.00元

《Programming Rudy》(中文版)(第2版)是它的第2版,其中包括超过200页的新内容,以及对原有内容的修订,涵盖了Ruby 1.8中新的和改进的特性以及标准库模块。它不仅是您学习Ruby语言及其丰富特性的一本优秀教程,也可以作为日常编程时类和模块的参考手册。Ruby是一种跨平台、面向对象的动态类型编程语言。Ruby体现了表达的一致性和简单性,它不仅是一门编程语言,更是表达想法的一种简......一起来看看 《Programming Ruby中文版》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具