Interoperability with the Options API
Coexistence Mechanism of Options API and Composition API
Vue 3 was designed to retain the Options API for backward compatibility while introducing the Composition API as a new development paradigm. This dual-mode coexistence requires the framework to implement interoperability between the two API styles internally. At the source code level, Vue achieves seamless collaboration between the two APIs through a unified reactivity system and component instance management mechanism.
// Options API component
export default {
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
}
The Bridging Role of the setup() Function
The entry function of the Composition API, setup()
, is internally transformed into an equivalent form of the Options API. During component initialization, Vue prioritizes processing the object returned by setup()
and merges its contents into the component instance context. This process occurs in the setupComponent
function in packages/runtime-core/src/component.ts
:
function setupComponent(instance) {
const { props, children } = instance.vnode
const setupResult = callSetup(instance)
if (isFunction(setupResult)) {
instance.render = setupResult
} else if (isObject(setupResult)) {
instance.setupState = proxyRefs(setupResult)
}
finishComponentSetup(instance)
}
Proxy Mechanism for Property Access
When accessing properties exposed by setup()
in the Options API, Vue implements property resolution through a proxy layer. The proxy created on instance.ctx
searches for properties in the following order:
setupState
(state exposed by the Composition API)data
optionsprops
computed
propertiesmethods
// Mixed usage example
const instance = {
setup() {
const message = ref('Hello')
return { message }
},
data() {
return { count: 0 }
},
methods: {
show() {
console.log(this.message, this.count) // Accessing state from both APIs
}
}
}
Merging Strategy for Lifecycle Hooks
Vue 3 uses the mergeLifecycleHook
function to handle the merging of lifecycle hooks with the same name. For hooks like created
and mounted
, if they exist in both the Options API and setup()
, both will be called with a deterministic execution order:
- Composition API hooks like
onMounted
are registered first. - Options API hooks like
mounted
are executed afterward.
// packages/runtime-core/src/componentOptions.ts
function mergeLifecycleHook(
hooks: Function[] | null,
hook: Function
) {
if (hooks) {
hooks.push(hook)
} else {
return [hook]
}
}
Unified Handling of Computed Properties
The computed
option in the Options API is converted into calls to the Composition API's computed
function. In the applyOptions
function, Vue iterates over each property in the computed
option and creates the corresponding computed ref:
// Options API computed conversion example
const options = {
computed: {
doubled() {
return this.count * 2
}
}
}
// Converted to a Composition API-like implementation
const computedDoubled = computed(() => instance.count * 2)
instance.proxy.doubled = computedDoubled
Context Handling for Method Binding
Methods in the Options API are forcibly bound to the component instance context using bind()
, while functions in the Composition API retain their original references. Vue handles this difference in the initMethods
function:
function initMethods(instance, methods) {
for (const key in methods) {
instance.proxy[key] = methods[key].bind(instance.proxy)
}
}
Unified Reactivity System
Regardless of the API style, both ultimately share the same reactivity system. The object returned by the Options API's data()
is processed by reactive()
, while the Composition API explicitly uses ref
/reactive
:
// Reactivity conversion for Options API data
function applyData(instance, dataFn) {
const data = dataFn.call(instance.proxy)
instance.data = reactive(data)
}
// Direct usage in Composition API
setup() {
const state = reactive({ count: 0 })
return { state }
}
Adaptation Layer for Template Compilation
During template compilation, Vue resolves access paths in templates into property accesses on the component proxy object. The render function generated by the compiler is agnostic to the source of the properties:
// Template: {{ count }}
// Compiled to:
function render(_ctx) {
return _ctx.count
}
Type System Compatibility Handling
For TypeScript users, Vue ensures type hints for both APIs through complex type definitions. The ComponentOptions
and SetupContext
types facilitate type flow via generic parameters:
interface ComponentOptions {
setup?: (
props: object,
ctx: SetupContext
) => object | () => VNode
}
// Automatic inference of setup return types
const component = defineComponent({
setup() {
return { count: ref(0) }
},
mounted() {
this.count // Correctly inferred as number
}
})
Mixed Usage of Custom Directives
Custom directives can be used in both APIs. Vue internally handles them uniformly through the withDirectives
function, mapping directive lifecycles to component lifecycles:
// Options API
const directive = {
mounted(el, binding) {
/* ... */
}
}
// Composition API
setup() {
const vMyDirective = {
mounted(el, binding) {
/* ... */
}
}
return { vMyDirective }
}
Cross-API Communication with provide/inject
The provide
/inject
options in the Options API are converted into corresponding Composition API calls. Vue handles this conversion during the applyOptions
phase:
// Options API provide
const options = {
provide: {
theme: 'dark'
}
}
// Converted to
setup() {
provide('theme', 'dark')
}
Unified Loading Logic for Async Components
Regardless of whether async components are defined using the Options API or Composition API, they are ultimately implemented via defineAsyncComponent
. Loading states and error handling are abstracted uniformly:
// Options API async component
const AsyncComp = {
async setup() {
const data = await fetchData()
return { data }
}
}
// Composition API equivalent
const AsyncComp = defineAsyncComponent({
loader: async () => {
const data = await fetchData()
return { setup() { return { data } } }
}
})
Adaptation Scheme for Global APIs
Vue 3's global APIs like mixin
need to support both API styles. In the applyMixins
function, Vue detects the component type and processes it accordingly:
function applyMixins(component, mixins) {
if (component.setup) {
// Handling mixin logic for Composition API components
} else {
// Traditional option merging logic
}
}
Unified Encapsulation of Rendering Context
The final render function receives a unified rendering context object that consolidates all available properties from both APIs. This encapsulation occurs in the createRenderContext
function:
function createRenderContext(instance) {
return new Proxy(instance, {
get(target, key) {
// Resolve properties in order: setupState, data, props, etc.
}
})
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:组合式API的性能考量
下一篇:组件定义的内部表示