Vue3系列 - setup上下文的创建
setup上下文的创建
在Vue3的组合式API中,setup函数接收两个参数:props和上下文(context)。上下文对象提供了与组件交互的方法和属性。让我们深入了解Vue2.7中setup上下文的创建过程。
createSetupContext函数
setup上下文通过createSetupContext函数创建:
function createSetupContext(vm) {
let exposeCalled = false
return {
get attrs() {
if (!vm._attrsProxy) {
const proxy = (vm._attrsProxy = {})
def(proxy, '_v_attr_proxy', true)
syncSetupProxy(proxy, vm.$attrs, emptyObject, vm, '$attrs')
}
return vm._attrsProxy
},
get listeners() {
if (!vm._listenersProxy) {
const proxy = (vm._listenersProxy = {})
syncSetupProxy(proxy, vm.$listeners, emptyObject, vm, '$listeners')
}
return vm._listenersProxy
},
get slots() {
return initSlotsProxy(vm)
},
emit: bind(vm.$emit, vm) as any,
expose(exposed) {
// expose API实现...
}
}
}上下文对象包含以下属性和方法:
- attrs:非prop的属性
- listeners:事件监听器
- slots:插槽
- emit:触发事件的方法
- expose:暴露公共属性的方法
attrs和listeners的代理
attrs和listeners是通过代理实现的,它们会在访问时才创建代理对象:
get attrs() {
if (!vm._attrsProxy) {
const proxy = (vm._attrsProxy = {})
def(proxy, '_v_attr_proxy', true)
syncSetupProxy(proxy, vm.$attrs, emptyObject, vm, '$attrs')
}
return vm._attrsProxy
}syncSetupProxy函数负责同步代理对象和原始对象:
export function syncSetupProxy(
to: any,
from: any,
prev: any,
instance: Component,
type: string
) {
let changed = false
for (const key in from) {
if (!(key in to)) {
changed = true
defineProxyAttr(to, key, instance, type)
} else if (from[key] !== prev[key]) {
changed = true
}
}
for (const key in to) {
if (!(key in from)) {
changed = true
delete to[key]
}
}
return changed
}slots的代理
slots也是通过代理实现的:
function initSlotsProxy(vm) {
if (!vm._slotsProxy) {
syncSetupSlots((vm._slotsProxy = {}), vm.$scopedSlots)
}
return vm._slotsProxy
}
export function syncSetupSlots(to, from) {
for (const key in from) {
to[key] = from[key]
}
for (const key in to) {
if (!(key in from)) {
delete to[key]
}
}
}emit方法
emit方法是组件实例的$emit方法的绑定版本:
emit: bind(vm.$emit, vm)这使得在setup函数中可以直接使用emit触发事件,而不需要使用this.$emit。
expose方法
expose方法允许组件有选择地暴露公共属性:
expose(exposed) {
if (__DEV__) {
if (exposeCalled) {
warn(`expose() should be called only once per setup().`, vm)
}
exposeCalled = true
}
if (exposed) {
Object.keys(exposed).forEach(key =>
proxyWithRefUnwrap(vm, exposed, key)
)
}
}这在使用<script setup>时特别有用,因为默认情况下<script setup>中的变量不会暴露给模板之外。
辅助函数
Vue2.7还提供了一些辅助函数,用于在setup函数中访问上下文的各个部分:
export function useSlots() {
return getContext().slots
}
export function useAttrs() {
return getContext().attrs
}
export function useListeners() {
return getContext().listeners
}总结
setup上下文提供了与组件交互的方法和属性,使组合式API能够完全替代选项式API的功能。理解setup上下文的创建过程,有助于我们更好地使用组合式API,编写更加模块化、可维护的Vue组件。