vue组件实现原理解析(中)

简介: 组件机制的设计,可以让开发者把一个复杂的应用分割成一个个功能独立组件,降低开发的难度的同时,也提供了极好的复用性和可维护性。本文我们一起从源码的角度,了解一下组件的底层实现原理。

以文中的button-counter组件为例,由于button-counter标签不是合法的HTML标签,不能直接new VNode()创建vnodeVue 会通过resolveAsset函数检查该标签是否为自定义组件的标签。


export function resolveAsset (
  options: Object,
  type: string,
  id: string,
  warnMissing?: boolean
): any {
  /* istanbul ignore if */
  if (typeof id !== 'string') {
    return
  }
  const assets = options[type]
  // 首先检查vue实例本身有无该组件
  if (hasOwn(assets, id)) return assets[id]
  const camelizedId = camelize(id)
  if (hasOwn(assets, camelizedId)) return assets[camelizedId]
  const PascalCaseId = capitalize(camelizedId)
  if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
  // 如果实例上没有找到,去查找原型链
  const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
      options
    )
  }
  return res
}


button-counter是我们全局注册的组件,显然可以在this.$options.components找到其定义。因此,Vue会执行createComponent函数来生成组件的vnode


// createComponent
export function createComponent (
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
  if (isUndef(Ctor)) {
    return
  }
  // 获取Vue的构造函数
  const baseCtor = context.$options._base
  // 如果Ctor是一个选项对象,需要使用Vue.extend使用选项对象,创建将组件选项对象转换成一个Vue的子类
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }
  // 如果Ctor还不是一个构造函数或者异步组件工厂函数,不再往下执行。
  if (typeof Ctor !== 'function') {
    if (process.env.NODE_ENV !== 'production') {
      warn(`Invalid Component definition: ${String(Ctor)}`, context)
    }
    return
  }
  // 异步组件
  let asyncFactory
  if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
    if (Ctor === undefined) {
      // return a placeholder node for async component, which is rendered
      // as a comment node but preserves all the raw information for the node.
      // the information will be used for async server-rendering and hydration.
      return createAsyncPlaceholder(
        asyncFactory,
        data,
        context,
        children,
        tag
      )
    }
  }
  data = data || {}
  // 重新解析构造函数的选项对象,在组件构造函数创建后,Vue可能会使用全局混入造成构造函数选项对象改变。
  resolveConstructorOptions(Ctor)
  // 处理组件的v-model
  if (isDef(data.model)) {
    transformModel(Ctor.options, data)
  }
  // 提取props
  const propsData = extractPropsFromVNodeData(data, Ctor, tag)
  // 函数式组件
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }
  const listeners = data.on
  data.on = data.nativeOn
  if (isTrue(Ctor.options.abstract)) {
    const slot = data.slot
    data = {}
    if (slot) {
      data.slot = slot
    }
  }
  // 安装组件hooks
  installComponentHooks(data)
  // 创建 vnode
  const name = Ctor.options.name || tag
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
    data, undefined, undefined, undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )
  return vnode
}


由于Vue允许通过一个选项对象定义组件,Vue需要使用Vue.extend将组件的选项对象转换成一个构造函数。


/**
   * Vue类继承,以Vue的原型为原型创建Vue组件子类。继承实现方式是采用Object.create(),在内部实现中,加入了缓存的机制,避免重复创建子类。
   */
  Vue.extend = function (extendOptions: Object): Function {
    // extendOptions 是组件的选项对象,与vue所接收的一样
    extendOptions = extendOptions || {}
    // Super变量保存对父类Vue的引用
    const Super = this
    // SuperId 保存父类的cid
    const SuperId = Super.cid
    // 缓存构造函数
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }
    // 获取组件的名字
    const name = extendOptions.name || Super.options.name
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name)
    }
    // 定义组件的构造函数
    const Sub = function VueComponent (options) {
      this._init(options)
    }
    // 组件的原型对象指向Vue的选项对象
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    // 为组件分配一个cid
    Sub.cid = cid++
    // 将组件的选项对象与Vue的选项合并
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    // 通过super属性指向父类
    Sub['super'] = Super
    // 将组件实例的props和computed属代理到组件原型对象上,避免每个实例创建的时候重复调用Object.defineProperty。
    if (Sub.options.props) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }
    // 复制父类Vue上的extend/mixin/use等全局方法
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use
    // 复制父类Vue上的component、directive、filter等资源注册方法
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }
    // 保存父类Vue的选项对象
    Sub.superOptions = Super.options
    // 保存组件的选项对象
    Sub.extendOptions = extendOptions
    // 保存最终的选项对象
    Sub.sealedOptions = extend({}, Sub.options)
    // 缓存组件的构造函数
    cachedCtors[SuperId] = Sub
    return Sub
  }
}


相关文章
|
2月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
290 2
|
1月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
274 137
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
755 0
|
4月前
|
人工智能 JSON JavaScript
VTJ.PRO 首发 MasterGo 设计智能识别引擎,秒级生成 Vue 代码
VTJ.PRO发布「AI MasterGo设计稿识别引擎」,成为全球首个支持解析MasterGo原生JSON文件并自动生成Vue组件的AI工具。通过双引擎架构,实现设计到代码全流程自动化,效率提升300%,助力企业降本增效,引领“设计即生产”新时代。
399 1
|
4月前
|
JavaScript 安全
在 Vue 中,如何在回调函数中正确使用 this?
在 Vue 中,如何在回调函数中正确使用 this?
251 0
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
340 2
|
8月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
810 29
|
8月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
317 4
|
8月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。

推荐镜像

更多
  • DNS