阿里云主机折上折
  • 微信号
Current Site:Index > Interoperability with the Options API

Interoperability with the Options API

Author:Chuan Chen 阅读数:54233人阅读 分类: Vue.js

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:

  1. setupState (state exposed by the Composition API)
  2. data options
  3. props
  4. computed properties
  5. methods
// 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:

  1. Composition API hooks like onMounted are registered first.
  2. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.