Vue原理解析:手写编译器(节点解析) —— Compile

由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏。等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码

一、声明式HTML

<div id="app">
	<h3>Hello,{{personName}},你在{{msg}}吗?</h3>
	<div v-text="msg" v-on:click="handleShowTip"></div>
	<div v-text="msg" @click="handleShowMsg(msg, personName)"></div>
	<div v-html="htmlStr"></div>
</div>

<script src="src/MVue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	let vm = new MVue({
		el: "#app",
		data: {
			msg: "学习MVVM原理",
			htmlStr: "<h4>你学的怎么样?</h4>",
			personName: "张三",
			test: {
				name: "张三"
			}
		},
		methods: {
			handleShowMsg(key) {
				this.msg = "hhx";
			},
			handleShowTip() {
				console.log(this);
			}
		}
	})
</script>

二、编译器处理实例

class Compile {
	constructor(el, vm) {
		this.vm = vm;
		this.el = this.isElementNode(el) ? el : document.querySelector(el);
		// 1.获取文档碎片对象,放入内存中会减少页面的回流和重绘
		const fragment = this.node2Fragment(this.el);
		// 2.编译模板
		this.compile(fragment);
		// 3.追加子元素到根元素
		this.el.appendChild(fragment)
	}

	compile(fragment) {
		// 1.获取子节点
		const childNodes = fragment.childNodes;
		childNodes.forEach(child => {
			if (this.isElementNode(child)) {
				// 编译元素节点
				this.compileElement(child);
			} else {
				// 编译文本节点
				this.compileText(child);
			}

			if (child.childNodes && child.childNodes.length) {
				this.compile(child);
			}
		})
	}

	compileText(node) {
		const content = node.textContent;
		if (/\{\{(.+?)\}\}/.test(content)) {
			compileUntil["text"](node, content, this.vm);
		}
	}

	compileElement(node) {
		const attrs = node.attributes;
		[...attrs].forEach((attr) => {
			const {
				name,
				value
			} = attr;

			console.log(attr);

			if (this.isDirective(name)) {
				const [, diractive] = name.split("-");
				const [dirName, eventName] = diractive.split(":");

				// 更新数据,数据驱动视图
				compileUntil[dirName](node, value, this.vm, eventName);

				// 删除标签上的指令
				node.removeAttribute("v-" + diractive);
			}

			if (this.isEvent(name)) {
				const [, eventName] = name.split("@");
				compileUntil["on"](node, value, this.vm, eventName);
			}
		});
	}

	isEvent(attrName) {
		return attrName.startsWith("@");
	}

	isDirective(attrName) {
		return attrName.startsWith("v-");
	}

	node2Fragment(el) {
		// 创建文档碎片
		const f = document.createDocumentFragment();
		let firstChild;
		while (firstChild = el.firstChild) {
			f.appendChild(firstChild);
		}
		return f;
	}

	isElementNode(node) {
		return node.nodeType === 1
	}
}

三、编译器解析工具

const compileUntil = {
	getValue(expr, vm) {
		// 切分对象属性,不断的累加到目的属性
		return expr.split(".").reduce((data, currentVal) => {
			return data[currentVal];
		}, vm.$data);
	},
	text(node, expr, vm) {
		let value;
		// 判断是否存在变量参数, 如果存在则将变量对象值替换掉声明变量
		if (expr.indexOf("{{") !== -1) {
			value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
				return this.getValue(args[1], vm);
			});
		} else {
			value = this.getValue(expr, vm);
		}
		// 更新视图
		this.updater.textUpdater(node, value);
	},
	html(node, expr, vm) {
		const value = this.getValue(expr, vm);
		this.updater.htmlUpdater(node, value);
	},
	model(node, expr, vm) {
		const value = this.getValue(expr, vm);
		this.updater.modelUpdater(node, value);
	},
	bind(node, expr, vm, attrName) {

	},
	on(node, expr, vm, eventName) {
		let param = [];
		let fnName = expr.replace(/\((.+?)\)/g, (...args) => {
			param = args[1].split(",");
			return "";
		});
		let fn = vm.$options.methods && vm.$options.methods[fnName];
		node.addEventListener(eventName, fn.bind(vm, ...param), false);
	},
	updater: {
		textUpdater(node, value) {
			node.textContent = value;
		},
		htmlUpdater(node, value) {
			node.innerHTML = value;
		},
		modelUpdater(node, value) {
			node.value = value;
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值