全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

组件化与渲染函数底层实现

Vue组件化通过 Vue.extend 创建子类,渲染函数执行返回VNode树,经 patch 挂载到真实DOM。

Vue.extend 组件子类化

JavaScript
Vue.extend = function(extendOptions) {
  const Super = this
  const SuperId = Super.cid
  const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
  
  if (cachedCtors[SuperId]) return cachedCtors[SuperId]
  
  const name = extendOptions.name || Super.options.name
  
  const Sub = function VueComponent(options) {
    this._init(options)
  }
  
  Sub.prototype = Object.create(Super.prototype)
  Sub.prototype.constructor = Sub
  Sub.cid = cid++
  Sub.options = mergeOptions(Super.options, extendOptions)
  Sub['super'] = Super
  
  // 继承全局API
  Sub.extend = Super.extend
  Sub.mixin = Super.mixin
  Sub.use = Super.use
  
  return Sub
}

组件注册时创建Vue子类,继承父类原型和全局API。

组件实例化流程

JavaScript
// 父组件渲染函数
function render() {
  return _c('child-component', { props: { msg: 'hello' } })
}

// createElement 处理组件
function createComponent(Ctor, data, context, children, tag) {
  // Ctor是Vue构造函数
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
  return vnode
}

createElement 识别组件时创建组件VNode,存储构造函数引用。

组件VNode创建与挂载

JavaScript
// patch过程
function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
  const i = vnode.data
  if (isDef(i)) {
    const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
    
    // 创建组件实例
    i.hook.init(vnode)
    
    if (isDef(vnode.componentInstance)) {
      // 调用子组件mount
      initComponent(vnode, insertedVnodeQueue)
      insert(parentElm, vnode.elm, refElm)
      return true
    }
  }
}

// init钩子
init(vnode) {
  const child = vnode.componentInstance = createComponentInstanceForVnode(vnode)
  child.$mount(undefined)
}

组件VNode patch时调用 init 钩子创建实例并挂载。

渲染函数执行

JavaScript
// 组件渲染函数
function render() {
  with(this) {
    return _c('div', { attrs: { id: 'app' } }, [
      _c('h1', [_v(_s(title))]),
      _c('p', [_v(_s(content))])
    ])
  }
}

// 执行返回VNode树
{
  tag: 'div',
  data: { attrs: { id: 'app' } },
  children: [
    { tag: 'h1', children: [{ text: '标题' }] },
    { tag: 'p', children: [{ text: '内容' }] }
  ]
}

渲染函数通过 with(this) 绑定组件实例,调用辅助函数构建VNode树。

patch 挂载过程

JavaScript
Vue.prototype._update = function(vnode, hydrating) {
  const vm = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  
  vm._vnode = vnode  // 更新当前VNode
  
  if (!prevVnode) {
    // 首次渲染
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating)
  } else {
    // 更新
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
  
  if (prevEl) prevEl.parentNode?.removeChild(prevEl)
}

_update 对比新旧VNode,首次渲染调用 __patch__ 挂载DOM。

辅助函数映射

JavaScript
// 渲染函数辅助函数
{
  '_c': (tag, data, children) => createElement(tag, data, children),
  '_v': text => createTextVNode(text),
  '_e': () => createEmptyVNode(),
  '_s': val => toString(val),
  '_l': (list, fn) => renderList(list, fn),
  '_t': (name, props) => renderSlot(name, props)
}

辅助函数封装VNode创建逻辑,简化渲染函数代码。

要点总结

  • Vue.extend 创建Vue子类继承原型和全局API,缓存构造函数
  • 组件识别时 createElement 创建组件VNode存储构造函数引用
  • 组件VNode patch时调用 init 钩子创建实例并 $mount
  • 渲染函数通过 with(this) 绑定实例,调用辅助函数构建VNode树
  • _update 对比新旧VNode,首次渲染调用 __patch__ 挂载
  • 辅助函数 _c/_v/_e/_s/_l/_t 封装VNode创建逻辑

📝 发现内容有误?点击此处直接编辑

← 上一篇 插槽与作用域插槽底层实现
下一篇 → keep-alive原理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库