实现一个简单的MVVM(Compile)

介绍

我以为如今学习前端的人都知道MVVM是什么意思,如今主流的框架React、Vue都使用的MVVM的原理,今天咱们就来实现一下MVVM其中的一小部分Compie指令解析html

  • 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查找元素
      • 若是是元素直接使用
    • 若是元素存在,将元素加入到内存中,解析模板
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;
}
复制代码

效果 node

元素都加入到了内存中,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 解析文本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

  • 若是为级联属性,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);
        });
    }
}
复制代码
  1. 插入到文档中
this.$el.appendChild(fragment);
复制代码

总结

  • 简单的实现了一个Compile的过程,知识是实现了一些简单的指令解析,尚未实现v-bind、v-on以及它们的缩写形式:、@。
  • 重点再于理解,不仅是知道和会用,还要学会灵活的运用以及知道他们简单的原理并能加以实现。
  • 若是有兴趣的能够看一下个人另两篇文章
相关文章
相关标签/搜索