虚拟dom是如何挂载到真实dom上的

14 篇文章 3 订阅

1.先使用createElement()方法生成虚拟dom节点

createElement(‘ul’, {class: ‘list’}, [
	createElement(‘li’, {class: ‘item’}, [1])
] )

1.1. 确定VNode的结构

class Element {
   constructor(tagName, props, children) {
       this.tagName = tagName
       this.props = props
       this.children = children
   }
}

1.2. createElement - 创建一个节点的方法

/**
 * 创建一个结点
 * @param {*} type 
 * @param {*} props 
 * @param {*} children 
 */
function createElement(type, props, children) {
    return new Element(type, props, children);
}

2.使用render函数生成真实dom

function render(eleObj) {
	let el = document.createElement(eleObj.type)
	for ( let key in eleObj.props) {
		// 设置属性的方法
		setAttr(el, key, eleObj[props].key) 
	}
	eleObj.children.forEach(child => {
		child = (child instanceof Element) ? render(child) : document.createTextNode(child)
		el.appendChild(child)
	})
	return el;
}

3.使用setAttribute方法给dom结点设置属性

function setAttr(node, key, value) {
	swith(key) {
		case ‘value’: // node是一个input或者textarea单独设置属性
			if(node.targetName.toUpperCase() === ‘input’ || node.targetName.toUpperCase() === ‘textarea’) {
				node.value = value;
			} else {
				node.setAttribute(key, value);
			}
			break;
		case ‘style’ :	
			node.style.cssText = value;
			break;
		default: 
			node.setAttribute(key, value)
			break
	}
}

4.渲染函数,把生成的真实结点绑定到target根元素

function renderDom(el, target) {
	target.appendChild(render(el))
}

完整代码

<!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>
        class Element {
            constructor(tagName, props, children) {
                this.tagName = tagName
                this.props = props
                this.children = children
            }
        }

        /**
         * 创建一个结点
         * @param {*} type 
         * @param {*} props 
         * @param {*} children 
         */
        function createElement(type, props, children) {
            return new Element(type, props, children);
        }


        /**
         * 给结点设置properties
         * @param {*} node 
         * @param {*} prop 
         * @param {*} value 
         */
        function setAttrs(node, prop, value) {
            switch (prop) {
                case 'value':
                    if (node.tagName == 'INPUT' || node.tagName === 'TEXTAREA') {
                        node.value = value;
                    } else {
                        node.setAttribute(prop, value);
                    }
                    break;
                case 'style':
                    node.style.cssText = value;
                    break;
                default:
                    node.setAttribute(prop, value);
                    break;
            }
        }


        /**
         *  将一个virtualdom转换成真实dom对象
         * @param {*} vDom 
         */
        function render(vDom) {
            const {
                tagName,
                props,
                children
            } = vDom

            const el = document.createElement(tagName);

            for (let key in props) {
                setAttrs(el, key, props[key]);
            }
            children.map((c) => {
                if (c instanceof Element) {
                    c = render(c);
                } else {
                    c = document.createTextNode(c);
                }

                el.appendChild(c)
            })

            return el;
        }


        /**
         * 将一个节点添加到root元素上
         * @param {*} rDom 
         * @param {*} rootEl 
         */
        function renderDom(rDom, rootEl) {
            rootEl.appendChild(rDom);
        }


        // 开始测试

        const rootEl = document.getElementById("app")

        const vDom = createElement(
            'ul', {
                class: 'list',
                style: 'width:300px;height:300px;background-color:orange'
            },
            [
                createElement(
                    'li', {
                        class: 'item',
                        data_id: 0,
                    },
                    [
                        createElement(
                            'p', {
                                class: 'text',
                            },
                            ["第一个列表项"]
                        )
                    ]
                ),
                createElement(
                    'li', {
                        class: 'item',
                        data_id: 1,
                    },
                    [
                        createElement(
                            'p', {
                                class: 'text',
                            },
                            [
                                createElement(
                                    'span', {
                                        class: "title"
                                    },
                                    [
                                        '第二个列表项'
                                    ]
                                )
                            ]
                        )
                    ]
                ),
                createElement(
                    'li', {
                        class: "item",
                        data_id: 2
                    },
                    [
                        '第三个列表项'
                    ]
                )
            ]
        )
    
    

        trueDom = render(vDom)
        renderDom(trueDom, rootEl)


    </script>
</body>

</html>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值