vue的渲染器的实现

28 篇文章 0 订阅
该文章演示了如何使用JavaScript实现虚拟DOM的核心功能,包括创建虚拟节点(h函数),将虚拟节点挂载到实际DOM(mount函数),以及更新DOM树的patch算法。通过这些函数,可以高效地对比和更新DOM结构,优化Web应用性能。
摘要由CSDN通过智能技术生成
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Document</title>
	</head>

	<body>
		<div id="app">
		</div>
		<script src="js/renderer.js"></script>
		<script>
			/* 1.通过h函数创建vnode */
			const vnode = h('div', {calss: 'pei',id:'aaa'}, [
				h('h2', null, '当前计数:100'),
				h('button', null, '+1')
			])

			/* 2.通过mount函数,将vnode挂载到div#app上 */

			mount(vnode, document.querySelector("#app"))
			
			/* 3、创建一个新的vnode */
			// const vnode1 = h('h2', {calss: 'peijj',id:'bbb'},"哈哈哈")
			const vnode1 = h('h2', {calss: 'peijj',id:'bbb'},[
				h('h2', null, '呵呵呵'),
				h('button',{onclick:function(){}}, '11')
			])
			
			patch(vnode,vnode1)
		</script>
	</body>
</html>

// vnode=>javascript对象=>{}
const h = (tag, props, children) => {
	return {
		tag,
		props,
		children
	}
}

const mount = (vnode, container) => {
	//1、创建出真是的原生,并且在vnode上保留el;
	const el = vnode.el = document.createElement(vnode.tag);

	//2、处理props
	if (vnode.props) {
		for (const key in vnode.props) {
			const value = vnode.props[key];
			if (key.startsWith("on")) {
				//对事件监听的判断
				el.addEventListener(key.slice(2).toLowerCase(), value)
			} else {
				el.setAttribute(key, value)
			}
		}
	}

	//3、处理children
	if (vnode.children) {
		if (typeof vnode.children === 'string') {
			el.textContent = vnode.children;
		} else {
			vnode.children.forEach(item => {
				mount(item, el)
			})
		}
	}

	//4、将el挂载到container
	container.appendChild(el)
}

const patch = (n1, n2) => {
	if (n1.tag !== n2.tag) {
		const n1ElParent = n1.el.parentElement;
		n1ElParent.removeChild(n1.el);
		mount(n2, n1ElParent)
	} else {
		//1、取出element对象,并且在n2中进行保存
		const el = n2.el = n1.el;
		// debugger;

		//2、处理props
		const oldProps = n1.props || {};
		const newProps = n2.props || {};
		//2.1获取所有的newProes添加到el;
		for (let key in newProps) {
			const oldValue = oldProps[key];
			const newValue = newProps[key];
			if (newProps !== oldValue) {
				if (key.startsWith("on")) {
					//对事件监听的判断
					const value = newProps[key];
					el.addEventListener(key.slice(2).toLowerCase(), value);
				} else {
					el.setAttribute(key, value);
				}
			}
		}

		//2.2删除旧的props;
		for (let key in oldProps) {
			if (!(key in newProps)) {
				if (key.startsWith("on")) {
					//对事件监听的判断
					const value = oldProps[key];
					el.removeEventListener(key.slice(2).toLowerCase(), value)
				} else {
					el.removeAttribute(key)
				}
			}
		}

		// 3、处理children
		const oldChildren = n1.children || [];
		const newChildren = n2.children || [];
		if (typeof newChildren === 'string') { //情况一:newChildren本身就是一个string
			// 边界判断(edge case)
			if (typeof oldChildren === 'string') {
				if (newChildren !== oldChildren) {
					el.textContent = newChildren;
				}
			} else {
				el.innerHTML = newChildren;
			}
		} else { //情况二:newChildren本身是一个数组
			if (typeof oldChildren === 'string') {
				el.innerHTML = '';
				newChildren.forEach(item => {
					mount(item, el)
				})
			} else {
				//oldChildren :[v1,v2,v3]
				//newChildren :[v1,v5,v6,v8,v9]
				// 1、前面有相同节点的原生进行patch操作
				const commonLength = Math.min(oldChildren.length, newChildren.length);
				for (let i = 0; i < commonLength; i++) {
					patch(oldChildren[i], newChildren[i])
				}

				// 2、newChildren>oldChildren
				if (newChildren.length > oldChildren.length) {
					newChildren.slice(oldChildren.length).forEach(item => {
						mount(item, el)
					})
				}

				// 3、newChildren<oldChildren
				if (newChildren.length < oldChildren.length) {
					oldChildren.slice(newChildren.length).forEach(item => {
						el.removeChild(item.el)
					})
				}
			}
		}


	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

裴嘉靖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值