初识虚拟DOM渲染器

文章介绍了虚拟DOM的概念,它是一个用于描述真实DOM的JavaScript对象。渲染器的作用是将虚拟DOM转换为实际的DOM元素,文中提供了一个简单的渲染器实现示例,包括`mountElement`函数。此外,文章还讨论了组件,组件是可复用的DOM元素封装,通过组件的`render`方法生成虚拟DOM进行复用。

什么是虚拟DOM

首先简单说一下什么是虚拟DOM,虚拟DOM就是一个描述真实DOM的JS对象
例如:

真实的DOM元素

<div onClick="alert('click me')">click me</div>

可以用下面的JS对象(虚拟DOM)描述,再通过渲染器渲染成真实的DOM

// 描述虚拟DOM对象
const vnode = {
	tag: "div",
	props: {
		onClick: () => {
			alert("click, me")
		}
	},
	children: "click me"
}

什么是渲染器

渲染器的作用就是把虚拟DOM渲染成真实的DOM,如下图所示:
渲染器

渲染器的实现

详细看代码描述:

重点看 mountElement 函数部分,描述如何通过js对象渲染出真实的DOM元素

/*
 * File Created: Monday, 6th March 2023 6:34:24 pm
 * Author: hotsuitor (hotsuitor@qq.com)
 * -----
 * Last Modified: Monday, 6th March 2023 6:34:50 pm
 * Modified By: hotsuitor (hotsuitor@qq.com>)
 * -----
 * Copyright 2022 - 2023 Your Company, Your Company
 */

/**
 * 渲染器
 * @param {*} vnode 虚拟DOM对象
 * @param {*} container 真实的DOM元素,作为挂载点,
 * 渲染器会把虚拟DOM渲染到该挂载点下
 */
function renderer(vnode, container) {
  if (vnode.tag === 'string') {
    // 说明描述的是标签元素
    mountElement(vnode, container)
  }
}

/**
 * 渲染标签元素
 * @param {*} vnode
 * @param {*} container
 */
function mountElement(vnode, container) {
  // 使用vnode.tag作为标签名创建DOM元素
  const el = document.createElement(vnode.tag)
  // 遍历 vnode.props ,将属性、事件添加到DOM元素
  for (const key in vnode.props) {
    // on开头表示是事件
    if (/^on/.test(key)) {
      // 添加事件,onClick->click, 事件注册
      el.addEventListener(key.substring(2).toLowerCase(), vnode.props[key])
    }
  }

  // 递归处理 children
  if (typeof vnode.children === 'string') {
    // 是字符串,说明是文本节点,直接添加到父元素
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    // 遍历处理children子节点
    vnode.children.forEach((child) => renderer(child, el))
  }

  // 将元素添加到挂在点下
  container.appendChild(el)
}

// demo
const vnode = {
  tag: 'div',
  props: {
    onClick: () => {
      alert('click me!')
    },
  },
  children: 'click me',
}

renderer(vnode, document.body)

组件是什么

现在项目开发基本离不开组件的封装,我们再看一下组件是什么?

组件的本质是一组DOM元素的封装,组件是由一组虚拟DOM元素组成的内容。目的是使代码可复用性提高,不必写冗余代码

下面看代码实现,重点看 mountComponent 函数部分,组件是一个封装了一组虚拟DOM的对象。通过复用这个对象的虚拟DOM,就可以实现了组件的复用。

/*
 * File Created: Monday, 6th March 2023 6:34:24 pm
 * Author: hotsuitor (hotsuitor@qq.com)
 * -----
 * Last Modified: Monday, 6th March 2023 6:34:50 pm
 * Modified By: hotsuitor (hotsuitor@qq.com>)
 * -----
 * Copyright 2022 - 2023 Your Company, Your Company
 */

/**
 * 渲染器
 * @param {*} vnode 虚拟DOM对象
 * @param {*} container 真实的DOM元素,作为挂载点,
 * 渲染器会把虚拟DOM渲染到该挂载点下
 */
function renderer(vnode, container) {
  if (vnode.tag === 'string') {
    // 说明描述的是标签元素
    mountElement(vnode, container)
  } else if (vnode.tag === 'object') {
    // 描述的是组件
    mountComponent(vnode, container)
  }
}

/**
 * 渲染标签元素
 * @param {*} vnode
 * @param {*} container
 */
function mountElement(vnode, container) {
  // 使用vnode.tag作为标签名创建DOM元素
  const el = document.createElement(vnode.tag)
  // 遍历 vnode.props ,将属性、事件添加到DOM元素
  for (const key in vnode.props) {
    // on开头表示是事件
    if (/^on/.test(key)) {
      // 添加事件,onClick->click, 事件注册
      el.addEventListener(key.substring(2).toLowerCase(), vnode.props[key])
    }
  }

  // 递归处理 children
  if (typeof vnode.children === 'string') {
    // 是字符串,说明是文本节点,直接添加到父元素
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    // 遍历处理children子节点
    vnode.children.forEach((child) => renderer(child, el))
  }

  // 将元素添加到挂在点下
  container.appendChild(el)
}

/**
 * 渲染组件
 * @param {*} vnode
 * @param {*} container
 */
function mountComponent(vnode, container) {
  // tag是组件对象,调用render方法得到渲染的内容(虚拟DOM)
  const subtree = vnode.tag.render()
  renderer(subtree, container)
}


/** 组件虚拟DOM */
const MyConpoment = {
  render() {
    return {
      tag: 'div',
      props: {
        onClick: () => {
          alert('hello')
        }
      },
      children: 'click me component'
    }
  }
}

const vnode = {
  tag: MyConpoment
}

renderer(vnode, document.body)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值