setup函数的执行时机
setup函数的执行时机
setup函数是Vue3组合式API的核心,它在组件实例创建过程中扮演着关键角色。理解其执行时机对于掌握组件生命周期和响应式系统工作原理至关重要。
组件初始化阶段
setup函数的执行发生在组件实例化过程中,具体在setupComponent
函数调用时触发。这个阶段早于模板编译和DOM挂载,是组合式API的入口点。
// 伪代码展示组件初始化流程
function mountComponent(vnode, container) {
const instance = createComponentInstance(vnode)
setupComponent(instance) // 在这里调用setup函数
setupRenderEffect(instance, container)
}
在setupComponent
内部,会执行以下关键步骤:
- 初始化props和slots
- 执行setup函数
- 处理setup返回结果
与选项式API的生命周期对比
与Vue2的选项式API相比,setup函数的执行时机相当于在beforeCreate
和created
之间:
// 选项式API生命周期顺序
beforeCreate() {
// 此时组件实例刚创建,数据观测/事件等未初始化
},
created() {
// 已完成数据观测,但DOM未挂载
}
// 组合式API等效表现
setup() {
// 相当于beforeCreate和created之间的阶段
onBeforeCreate(() => { /*...*/ })
onCreated(() => { /*...*/ })
}
执行上下文环境
setup函数执行时,组件实例已经部分初始化,但某些特性尚未完全就绪:
setup(props, context) {
// 此时可以访问:
// - props(已响应式处理)
// - context包含attrs/slots/emit
// 但以下内容不可用:
// - this(未绑定组件实例)
// - 模板refs(DOM尚未创建)
// - 子组件(未挂载)
}
响应式系统的建立
setup函数是声明响应式状态的主要场所,其执行时机确保了响应式系统在渲染前完成初始化:
setup() {
const count = ref(0) // 响应式声明
// 这个effect会在setup完成后被收集
watchEffect(() => {
console.log('count changed:', count.value)
})
return { count }
}
异步组件的特殊处理
对于异步组件,setup函数的执行时机会延迟到组件加载完成后:
const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
)
// 实际调用时机:
// 1. 父组件挂载时开始加载
// 2. 资源加载完成后执行setup
// 3. 然后继续后续渲染流程
与渲染函数的关系
setup函数执行完成后,其返回值将用于渲染函数的执行:
setup() {
const msg = ref('Hello')
// 这个返回值会被注入到渲染上下文中
return {
msg
}
}
// 编译后的渲染函数可以访问setup返回的内容
function render(_ctx) {
return h('div', _ctx.msg)
}
服务端渲染场景
在SSR环境下,setup函数的执行时机会有特殊处理:
// 服务端执行流程:
// 1. 创建组件实例
// 2. 同步执行setup
// 3. 执行render生成字符串
// 客户端激活时:
// 1. 重用服务端状态
// 2. 不会再次执行setup
// 3. 直接进行DOM修补
错误处理时机
setup函数内部的同步错误会被立即捕获,而异步错误则需要额外处理:
setup() {
// 同步错误 - 会被Vue错误处理器捕获
const value = riskyOperation()
// 异步错误 - 需要手动.catch
fetchData().catch(err => {
// 需要自行处理
})
}
多次执行的情况
正常情况下setup函数只会执行一次,但在开发模式下热更新时可能重新执行:
// 开发环境热更新示例
let setupCount = 0
setup() {
setupCount++
console.log(`Setup executed ${setupCount} times`)
// 热更新时会看到计数增加
return {}
}
与provide/inject的配合
setup函数是使用provide的最佳位置,因为其执行时机早于子孙组件的创建:
// 祖先组件
setup() {
provide('theme', 'dark') // 在子孙组件创建前就已可用
}
// 子孙组件
setup() {
const theme = inject('theme') // 可以立即获取
}
性能优化考量
由于setup函数在组件创建时同步执行,应避免其中的重型操作:
// 不推荐的做法
setup() {
// 同步执行耗时操作会阻塞组件初始化
processLargeData()
// 推荐将异步操作放在onMounted中
onMounted(async () => {
await loadHeavyResources()
})
}
TypeScript类型推断
setup函数的早期执行也影响了类型系统的工作方式:
interface Props {
id: number
}
setup(props: Props) {
// props类型在setup执行时就已经确定
const doubleId = computed(() => props.id * 2)
return { doubleId }
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:自定义指令的编译处理
下一篇:生命周期钩子的注册机制