返回

轻松实现MiniVue渲染:核心渲染器攻略

前端

手写简易的MiniVue(02-渲染器实现)

MiniVue 02 渲染器实现

前言

在上一篇文章中,我们实现了响应式系统。现在,我们将实现渲染器,这是 MiniVue 的核心部分,负责将虚拟节点挂载到真实的 DOM 上,并进行更新。在这一部分,我们将实现三个函数:

  • h 函数:返回虚拟节点对象
  • mount 函数:用于将虚拟节点挂载到真实的 DOM 上
  • patch 函数:用于更新虚拟节点

1. h 函数

h 函数是创建虚拟节点对象的函数。虚拟节点是 MiniVue 中用来 DOM 元素的轻量级对象,它包含了元素的标签名、属性和子元素。

// h函数的实现
function h(tag, props, children) {
  return {
    tag,
    props,
    children
  }
}

h 函数的第一个参数是元素的标签名,例如 "div""p" 等。第二个参数是元素的属性,是一个对象,例如 { id: "app" }。第三个参数是元素的子元素,可以是虚拟节点对象、字符串或数组。

例如,以下代码创建了一个带有 id 属性的 div 元素,并包含两个子元素:一个带有 class 属性的 p 元素和一个文本节点:

const vnode = h("div", { id: "app" }, [
  h("p", { class: "text-center" }, "Hello, MiniVue!"),
  "World"
])

2. mount 函数

mount 函数用于将虚拟节点挂载到真实的 DOM 上。它首先创建一个真实的 DOM 元素,然后将虚拟节点的属性和子元素附加到这个真实的 DOM 元素上。

// mount函数的实现
function mount(vnode, container) {
  // 创建真实的 DOM 元素
  const el = createElement(vnode.tag)

  // 设置元素属性
  for (const key in vnode.props) {
    el.setAttribute(key, vnode.props[key])
  }

  // 递归挂载子元素
  vnode.children.forEach(child => {
    mount(child, el)
  })

  // 将元素添加到容器中
  container.appendChild(el)
}

mount 函数的第一个参数是虚拟节点对象,第二个参数是将虚拟节点挂载到的容器元素。

mount 函数首先创建一个真实的 DOM 元素,然后将虚拟节点的属性和子元素附加到这个真实的 DOM 元素上。最后,将元素添加到容器中。

3. patch 函数

patch 函数用于更新虚拟节点。当虚拟节点发生变化时,patch 函数会将变化应用到真实的 DOM 上。

// patch函数的实现
function patch(oldVnode, newVnode) {
  // 比较标签名
  if (oldVnode.tag !== newVnode.tag) {
    // 替换元素
    const newEl = createElement(newVnode.tag)
    oldVnode.el.parentNode.replaceChild(newEl, oldVnode.el)

    // 递归更新子元素
    newVnode.children.forEach(child => {
      mount(child, newEl)
    })
  } else {
    // 更新属性
    for (const key in newVnode.props) {
      if (newVnode.props[key] !== oldVnode.props[key]) {
        oldVnode.el.setAttribute(key, newVnode.props[key])
      }
    }

    // 更新子元素
    const oldChildren = oldVnode.children
    const newChildren = newVnode.children

    // 比较子元素的长度
    if (oldChildren.length !== newChildren.length) {
      // 更新子元素
      updateChildren(oldChildren, newChildren)
    } else {
      // 递归更新子元素
      for (let i = 0; i < oldChildren.length; i++) {
        patch(oldChildren[i], newChildren[i])
      }
    }
  }
}

patch 函数的第一个参数是旧的虚拟节点对象,第二个参数是新的虚拟节点对象。

patch 函数首先比较两个虚拟节点的标签名。如果标签名不同,则替换元素。如果标签名相同,则更新属性和子元素。

总结

在本文中,我们实现了 MiniVue 的渲染器,包括 h 函数、mount 函数和 patch 函数。这些函数使我们能够将虚拟节点挂载到真实的 DOM 上,并进行更新。