组件定义的内部表示
组件定义的内部表示
Vue3中组件的定义通过对象描述,这个对象被称为组件选项对象。组件选项对象包含组件的各种配置项,如data
、methods
、props
等。当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
: 组件唯一IDtype
: 组件类型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函数会被特殊处理:
- 接收props和context参数
- 可以返回渲染函数或响应式数据
- 执行时机在组件实例创建之前
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
上一篇:与选项式API的互操作性
下一篇:组件实例的创建过程