阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 组件定义的内部表示

组件定义的内部表示

作者:陈川 阅读数:33650人阅读 分类: Vue.js

组件定义的内部表示

Vue3中组件的定义通过对象描述,这个对象被称为组件选项对象。组件选项对象包含组件的各种配置项,如datamethodsprops等。当Vue处理组件时,会将这些选项转换为内部表示形式。

const MyComponent = {
  name: 'MyComponent',
  props: {
    msg: String
  },
  setup() {
    const count = ref(0)
    return { count }
  },
  template: `<div>{{ msg }} - {{ count }}</div>`
}

组件选项的标准化

在组件被创建之前,Vue会对组件选项进行标准化处理。这个过程主要做两件事:将不同形式的组件定义统一为标准格式,以及应用全局配置和混入。

// 函数式组件标准化示例
function FunctionalComp(props) {
  return h('div', props.msg)
}

// 标准化后会被转换为
{
  functional: true,
  render: function(props) {
    return h('div', props.msg)
  }
}

组件内部表示结构

标准化后的组件选项会被转换为内部表示,这个内部表示是一个包含多个属性的对象。主要属性包括:

  • uid: 组件唯一ID
  • type: 组件类型
  • name: 组件名称
  • render: 渲染函数
  • setup: setup函数
  • props: 规范化后的props定义
  • emits: 规范化后的emits定义
interface InternalComponentOptions {
  uid: number
  type: Component
  name?: string
  render?: InternalRenderFunction
  setup?: (
    props: Data,
    setupContext: SetupContext
  ) => RenderFunction | Data
  props?: ComponentPropsOptions
  emits?: ComponentEmitsOptions
  // ...其他属性
}

props的规范化处理

props定义可以有多种形式,Vue会将其统一为标准化格式。例如:

// 数组形式
props: ['size', 'name']

// 对象形式
props: {
  size: Number,
  name: {
    type: String,
    default: 'anonymous'
  }
}

// 标准化后都会转换为
props: {
  size: {
    type: Number
  },
  name: {
    type: String,
    default: 'anonymous'
  }
}

setup函数的处理

setup函数是Vue3组合式API的核心。在内部表示中,setup函数会被特殊处理:

  1. 接收props和context参数
  2. 可以返回渲染函数或响应式数据
  3. 执行时机在组件实例创建之前
const Comp = {
  setup(props, { attrs, slots, emit }) {
    const count = ref(0)
    const increment = () => count.value++
    
    return () => h('div', [
      h('span', count.value),
      h('button', { onClick: increment }, '+')
    ])
  }
}

渲染函数的内部表示

组件的渲染函数最终会被转换为VNode。Vue3使用了一个优化的表示方式:

// 模板编译后的渲染函数
function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("span", null, _ctx.count),
    _createVNode("button", { onClick: _ctx.increment }, "+")
  ]))
}

组件实例与内部表示的关系

组件实例是基于内部表示创建的运行时对象。每个组件实例都持有对内部表示的引用:

interface ComponentInternalInstance {
  uid: number
  type: Component
  vnode: VNode
  // 指向内部表示
  appContext: AppContext
  // 其他实例属性...
}

函数式组件的特殊处理

函数式组件在内部有特殊的表示方式:

// 函数式组件定义
const FunctionalComp = (props, { slots, attrs }) => h('div', props.msg)

// 内部表示
{
  functional: true,
  props: ['msg'],
  render: (ctx) => h('div', ctx.msg)
}

异步组件的内部表示

异步组件会被转换为一个特殊的对象表示:

const AsyncComp = defineAsyncComponent(() => import('./Comp.vue'))

// 内部表示
{
  __asyncLoader: () => import('./Comp.vue'),
  __asyncResolved: null, // 解析后的组件
  suspensible: true
}

内置组件的内部表示

Vue3的内置组件如<KeepAlive><Transition>等也有特殊的内部表示:

// KeepAlive的内部表示
const KeepAliveImpl = {
  __isKeepAlive: true,
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number]
  },
  setup(props, { slots }) {
    // 特殊实现逻辑
  }
}

组件类型的标记

Vue3使用位掩码来标记组件类型,提高类型判断效率:

enum ShapeFlags {
  ELEMENT = 1,
  FUNCTIONAL_COMPONENT = 1 << 1,
  STATEFUL_COMPONENT = 1 << 2,
  COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
  // ...其他标志
}

// 使用示例
const vnode = {
  shapeFlag: ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.COMPONENT
}

组件定义的缓存

Vue3会对组件定义进行缓存优化:

// 缓存已解析的异步组件
const resolvedCache = new WeakMap()

function resolveAsyncComponent(comp) {
  if (resolvedCache.has(comp)) {
    return resolvedCache.get(comp)
  }
  // 解析逻辑...
  resolvedCache.set(comp, resolvedComp)
  return resolvedComp
}

组件继承的实现

组件继承通过原型链实现:

const BaseComponent = {
  data() {
    return { baseData: 'base' }
  }
}

const ChildComponent = {
  extends: BaseComponent,
  data() {
    return { childData: 'child' }
  }
}

// 内部实现类似
const mergedOptions = Object.create(BaseComponent)
Object.assign(mergedOptions, ChildComponent)

指令的内部表示

组件中使用的指令也有对应的内部表示:

const vMyDirective = {
  mounted(el, binding, vnode) {
    // 指令逻辑
  },
  updated(el, binding, vnode) {
    // 更新逻辑
  }
}

// 在组件内部表示中
{
  directives: {
    myDirective: vMyDirective
  }
}

插槽的内部处理

插槽内容在内部被转换为函数:

// 父组件
const Parent = {
  template: `
    <Child>
      <template #default="{ msg }">
        {{ msg }}
      </template>
    </Child>
  `
}

// 内部表示中的插槽
slots: {
  default: (props) => [createTextVNode(props.msg)]
}

组件生命周期的内部注册

生命周期钩子在内部被转换为数组形式:

const Comp = {
  created() {
    console.log('created 1')
  },
  mixins: [{
    created() {
      console.log('created 2')
    }
  }]
}

// 内部表示
{
  created: [
    function() { console.log('created 2') },
    function() { console.log('created 1') }
  ]
}

响应式系统的集成

组件内部表示与响应式系统紧密集成:

const Comp = {
  setup() {
    const state = reactive({ count: 0 })
    return { state }
  }
}

// 内部处理
const setupResult = Comp.setup()
if (isReactive(setupResult)) {
  instance.setupState = setupResult
}

模板编译与内部表示

对于模板定义的组件,会先编译为渲染函数:

const template = `<div @click="count++">{{ count }}</div>`

// 编译后
const render = function(_ctx, _cache) {
  return _openBlock(), _createBlock("div", {
    onClick: _cache[0] || (_cache[0] = $event => (_ctx.count++))
  }, _toDisplayString(_ctx.count), 1)
}

// 最终内部表示
{
  render,
  // 其他选项...
}

组件代理的实现

组件实例通过代理访问数据:

const instance = {
  props: { msg: 'hello' },
  setupState: { count: 0 },
  data: { foo: 'bar' }
}

const proxy = new Proxy(instance, {
  get(target, key) {
    if (key in target.setupState) {
      return target.setupState[key]
    } else if (key in target.props) {
      return target.props[key]
    }
    // 其他属性访问...
  }
})

组件更新的优化标记

内部表示包含优化相关的标记:

interface InternalComponentOptions {
  // ...
  patchFlag?: number
  dynamicProps?: string[]
  dynamicChildren?: VNode[]
}

这些标记用于在更新时进行优化,例如:

// 静态节点
const staticVNode = {
  patchFlag: PatchFlags.HOISTED
}

// 动态props节点
const dynamicPropsVNode = {
  patchFlag: PatchFlags.PROPS,
  dynamicProps: ['title', 'value']
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌