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

组件实例化与挂载流程

Vue组件通过 _init 初始化选项、注入、响应式、事件,$mount 编译模板、创建渲染Watcher、执行patch挂载DOM。

_init 初始化

JavaScript
Vue.prototype._init = function(options?: Object) {
  const vm: Component = this
  
  // 合并选项
  vm.$options = mergeOptions(
    resolveConstructorOptions(vm.constructor),
    options || {},
    vm
  )
  
  // 初始化生命周期
  initLifecycle(vm)
  // 初始化事件系统
  initEvents(vm)
  // 初始化渲染系统
  initRender(vm)
  // 调用beforeCreate钩子
  callHook(vm, 'beforeCreate')
  // 初始化注入
  initInjections(vm)
  // 初始化响应式数据
  initState(vm)
  // 初始化提供
  initProvide(vm)
  // 调用created钩子
  callHook(vm, 'created')
  
  // 自动挂载
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

按顺序初始化选项、生命周期、事件、渲染、注入、状态、提供,调用生命周期钩子。

initState 状态初始化

JavaScript
function initState(vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) initData(vm)
  else observe(vm._data = {}, true)
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

按props -> methods -> data -> computed -> watch顺序初始化。

initData 数据响应式

JavaScript
function initData(vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
  
  // 代理到vm实例
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (props && hasOwn(props, key)) {
      warn(`props与data冲突`)
    } else if (methods && hasOwn(methods, key)) {
      warn(`methods与data冲突`)
    } else {
      proxy(vm, `_data`, key)  // vm.key -> vm._data.key
    }
  }
  
  // 观测数据
  observe(data, true)
}

data代理到vm实例,避免每次访问 vm._data,然后观测数据实现响应式。

$mount 挂载

JavaScript
Vue.prototype.$mount = function(
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  const options = this.$options
  
  // 编译模板(仅runtime+compiler版本)
  if (!options.render) {
    let template = options.template
    if (template) {
      // 从template编译render函数
    } else if (el) {
      // 从el.outerHTML编译render函数
    }
  }
  
  // 挂载核心逻辑
  return mountComponent(this, el, hydrating)
}

runtime+compiler版本编译模板,runtime-only版本直接使用render函数。

mountComponent 挂载核心

JavaScript
function mountComponent(
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  
  // 创建更新组件
  updateComponent = () => {
    vm._update(vm._render(), hydrating)
  }
  
  // 创建渲染Watcher
  new Watcher(vm, updateComponent, noop, {
    before() {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true)
  
  hydrating = false
  
  // 手动挂载实例调用mounted
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  
  return vm
}

创建渲染Watcher,执行 vm._render() 生成VNode,vm._update() patch到DOM。

_render 渲染函数执行

JavaScript
Vue.prototype._render = function(): VNode {
  const vm: Component = this
  const { render, _parentVnode } = vm.$options
  
  vm.$scopedSlots = normalizeScopedSlots(
    _parentVnode.data.scopedSlots,
    vm.$slots
  )
  
  const vnode = render.call(vm._renderProxy, vm.$createElement)
  vnode.parent = _parentVnode
  return vnode
}

执行render函数返回VNode树,_renderProxy 提供访问不存在属性的警告。

_update DOM更新

JavaScript
Vue.prototype._update = function(vnode: VNode, hydrating?: boolean) {
  const vm: Component = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  
  vm._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)
  }
}

首次渲染调用 __patch__ 挂载,更新时对比新旧VNode diff。

完整挂载流程

JavaScript
// 1. new Vue(options)
//    -> _init()
//       -> mergeOptions()
//       -> initLifecycle/Events/Render
//       -> beforeCreate
//       -> initInjections/State/Provide
//       -> created
//       -> $mount(el)

// 2. $mount(el)
//    -> 编译模板(如有)
//    -> mountComponent()
//       -> new Watcher(vm, updateComponent)
//          -> watcher.get()
//             -> updateComponent()
//                -> vm._render()  // 生成VNode
//                -> vm._update()  // patch挂载
//                   -> __patch__(el, vnode)

// 3. 首次渲染完成
//    -> mounted

new Vuemounted 钩子触发的完整流程。

要点总结

  • _init 按顺序初始化选项、生命周期、事件、渲染、注入、状态
  • initState 按props -> methods -> data -> computed -> watch顺序初始化
  • initData 代理数据到vm实例,观测数据实现响应式
  • $mount 编译模板(仅runtime+compiler版本),调用 mountComponent
  • mountComponent 创建渲染Watcher,执行 updateComponent
  • _render 执行render函数生成VNode,_update 执行patch挂载DOM
  • 完整流程:new Vue -> _init -> $mount -> mounted

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

← 上一篇 异步更新队列与nextTick
下一篇 → 虚拟DOM与VNode
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

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

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