Vue.js 3设计与实现(4)什么是组件?简单的渲染器

2025/08/29

组件就是一组DOM元素的封装,这组DOM元素就是组件要渲染的内容

无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟DOM渲染成真实DOM

渲染器的功能 vnode –> 真实DOM

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

  const renderer = (vnode, root) => {
    const { tag, props, children } = vnode
    let d = document.createElement(tag)
    for (let key in props) {
      let event = key.slice(2).toLowerCase()
      if (key.startsWith('on')) {
        d.addEventListener(event, props[key])
      }
    }
    if (typeof children === 'string') {
      d.appendChild(document.createTextNode(children))
    } else if (Array.isArray(children)) {
      for (let child of children) {

        renderer(child, d)
      }
    }
    root.appendChild(d)
  }
  renderer(vnode, document.querySelector('#app2'))

自定义组件渲染

使用tag用来区分 原生HTML标签,function组件和类组件

<div id="app3"></div>
// 函数式组件
const MyComponent = () => {
    return {
      tag: 'div',
      props: {
        onClick() {
          alert('click my component')
        }
      },
      children: 'click my component'
    }
  }
// 类组件
  const MyStateComponent = {
    render() {
      return {
        tag: 'div',
        props: {
          onClick() {
            alert('click my MyStateComponent')
          }
        },
        children: 'click my MyStateComponent'
      }
    }
  }
// renderer渲染器
  const renderer2 = (vnode, container) => {
    if (typeof vnode.tag === 'object') {
      mountStateComponent(vnode, container)
    } else if (typeof vnode.tag === 'function') {
      mountComponent(vnode, container)
    } else if (typeof vnode.tag === 'string') {
      mountElement(vnode, container)
    }
  }
// 挂载节点
  const mountElement = (vnode, container) => {
    const { tag, props, children } = vnode
    let dom = document.createElement(tag)
    for (let key in props) {
      if (key.startsWith('on')) {
        const event = key.slice(2).toLowerCase()
        dom.addEventListener(event, props[key])
      }
    }
    if (typeof children == 'string') {
      dom.appendChild(document.createTextNode(children))
    } else if (Array.isArray(children)) {
      for (let child of children) {
        renderer2(child, dom)
      }
    }
    container.appendChild(dom)
  }
// 挂载函数式组件
  const mountComponent = (vnode, dom) => {
    const subtree = vnode.tag()
    renderer2(subtree, dom)
  }
// 挂载类组件
  const mountStateComponent = (vnode, dom) => {
    const subtree = vnode.tag.render()
    renderer2(subtree, dom)
  }

  const vnode2 = {
    tag: 'div',
    children: [
      { tag: MyComponent },
      { tag: MyStateComponent },
    ]
  }
  renderer2(vnode2, document.querySelector('#app3'))

Post Directory