12.5 setup 函数的作用与实现
setup是组合式 API 的入口,只在组件被挂载时执行一次;它的返回值要么当渲染函数,要么当暴露给渲染函数的数据。
一、setup 的定位与参数
- Vue.js 3 新增的组件选项,用于配合组合式 API:建立组合逻辑、创建响应式数据、创建通用函数、注册生命周期钩子;
- 在组件的整个生命周期中只在被挂载时执行一次;
- 接收两个参数:
| 参数 | 说明 |
|---|---|
props | 外部为组件传递的 props;实现中传入的是只读版本(shallowReadonly),避免用户意外修改 |
setupContext | 与组件接口相关的能力:slots(插槽)、emit(发射事件)、attrs(未声明为 props 的属性)、expose(显式对外暴露数据) |
不建议把
setup与data/watch/methods这类「传统」选项混用:语义与理解成本都会变高,Vue.js 3 更提倡组合式 API。
二、返回值决定渲染内容
返回值的两种情况:
- 返回函数 → 该函数作为组件的
render函数,常用于组件不用模板表达渲染内容的场景;如果组件已经用模板 /render表达内容,则不能再返回函数(会与模板编译出的渲染函数冲突); - 返回对象 → 对象中的数据暴露给模板 / 渲染函数使用,可在渲染函数中通过
this.xxx访问。
const { render, data, setup } = componentOptions
// setupContext 目前只有 attrs,emit / slots 分别在 12.6 / 12.7 补上
const setupContext = { attrs }
// 第一个参数传只读 props,第二个参数传 setupContext
const setupResult = setup && setup(shallowReadonly(instance.props), setupContext)
// setupState 用来存储由 setup 返回的数据
let setupState = null
if (typeof setupResult === 'function') {
// 返回函数 → 作为渲染函数
if (render) console.error('setup 函数返回渲染函数,render 选项将被忽略')
render = setupResult
} else {
// 返回对象 → 作为数据状态
setupState = setupResult
}
三、渲染上下文增加 setupState
const renderContext = new Proxy(instance, {
get(t, k) {
const { state, props } = t
if (state && k in state) {
return state[k]
} else if (k in props) {
return props[k]
} else if (setupState && k in setupState) { // 新增分支:支持 setup 返回的数据
return setupState[k]
} else {
console.error('不存在')
}
},
set(t, k, v) {
const { state, props } = t
if (state && k in state) {
state[k] = v
} else if (k in props) {
console.warn(`Attempting to mutate prop "${k}". Props are readonly.`)
} else if (setupState && k in setupState) { // 新增分支
setupState[k] = v
} else {
console.error('不存在')
}
}
})
读取优先级为 state → props → setupState,同名时取到的是顺序靠前的那一个。
setup在实例创建之后、created钩子之前调用;setupContext是同一个对象交给用户,所以后面往里面补emit、slots不需要改调用方式。 本例直接把返回值原样当作setupState,没有像真实实现那样对返回值做「自动脱 ref」处理。
四、测试
const MyComponent = {
data() { return { foo: 'hello world' } },
render() {
// 使用 setup 暴露的数据
return { type: 'div', children: `foo 的值是: ${this.bar}` }
},
setup(props, context) {
console.log('props', props) // 只读的 props 代理
console.log('context', context) // { attrs }
return { bar: 'hello setUp' }
}
}
相关
- 12.4 props 与组件的被动更新:setup 的第一个参数就是那里解析出的 props
- 12.6 组件事件与 emit 的实现:往 setupContext 里补上 emit