Vue 2 Options API to Vue 3 Composition API Conversion Codemod
INFO
This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository. You can find the original version here
Context
Our Vue.js application currently uses the Options API, which is the traditional way of writing Vue components in Vue 2. With the release of Vue 3, the Composition API was introduced, offering improved code organization, better TypeScript support, and enhanced reusability of component logic. For more detailed information about the reasons for migrating to the Composition API, see the documentation entry.
To modernize our codebase and take advantage of these benefits, we need to migrate our existing Vue 2 Options API components to use the Vue 3 Composition API. Manual conversion of numerous components would be time-consuming and error-prone. Therefore, we need an automated solution to assist in this migration process.
Decision
We have decided to implement a Codemod in the form of an ESLint rule to automatically convert Vue 2 Options API components to Vue 3 Composition API. This Codemod will:
Identify Vue component definitions in the codebase.
Convert the following Options API features to their Composition API equivalents:
- Convert
data
properties toref
orreactive
. - Convert
computed
properties tocomputed()
functions. - Convert
methods
to regular functions within thesetup()
function. - Convert lifecycle hooks to their Composition API equivalents (e.g.,
mounted
toonMounted
). - Convert Vue 2 specific lifecycle hooks to their Vue 3 equivalents.
- Convert
watch
properties towatch()
functions. - Handle
props
andinject
conversions. - Replace
this
references with direct references to reactive variables. - Convert writable computed properties.
- Handle reactive object reassignments using
Object.assign
- Handle correct usage of
ref
and replace the access to the value with.value
.
- Convert
Generate a
setup()
function containing the converted code.Add necessary imports for Composition API functions (e.g.,
ref
,reactive
,computed
,watch
).
The Codemod will be implemented as an ESLint rule to leverage the existing ESLint ecosystem and allow for easy integration into our development workflow.
Consequences
Positive Consequences
- Automated conversion will significantly reduce the time and effort required to migrate components to the Composition API.
- Consistent conversion patterns will be applied across the codebase, ensuring uniformity.
- The risk of human error during manual conversion is minimized.
- Developers can gradually adopt the Composition API, as the Codemod can be run on a per-file or per-component basis.
- The Codemod can be easily shared and used across different projects within the organization.
Negative Consequences
- The Codemod may not cover all edge cases or complex component structures, requiring manual intervention in some scenarios.
- Developers will need to review and potentially refactor the converted code to ensure optimal usage of the Composition API.
- The Codemod does not handle template changes, such as adjusting
$refs
usage.
Limitations and Manual Steps
While the Codemod handles many aspects of the conversion, some parts will still require manual attention:
- Template modifications: The Codemod doesn't update the component's template. Developers will need to manually adjust template bindings, event handlers, and
ref
usage. - Complex data structures: While simple
data
properties are converted toref()
orreactive()
, more complex nested structures might require manual optimization. - Vuex store interactions: The Codemod doesn't automatically convert Vuex
mapState
,mapGetters
,mapActions
, etc. These will need to be manually converted to use theuseStore
composition function. - Mixins: The Codemod doesn't handle the conversion of mixins. These will need to be manually refactored into composable functions.
- Plugin usage: Certain plugins or third-party libraries that rely on the Options API might require manual updates or replacements.
- TypeScript annotations: If the project uses TypeScript, type annotations for props, computed properties, and methods will need to be manually added or adjusted in the
setup()
function. - Spread operators in computed properties: The Codemod identifies these but doesn't fully convert them. A TODO comment is added for manual attention.
- Components using render functions or JSX will need manual conversion.
- Performance optimizations like
shallowRef
orshallowReactive
are not automatically applied. - The converted code may benefit from further refactoring to extract reusable composables.
- Error handling and edge cases in lifecycle hooks may need manual review.
- Usage of plugins, etc. in the
this
context may need manual conversion, e.g.$tc
,$t
, etc. - Sometimes the reassignment of reactive objects over multiple lines may not be handled correctly every time.
- Usage of
$emit
in the Options API may need manual conversion todefineEmits
in the Composition API and then using theemit
function.
By implementing this Codemod, we take a significant step towards modernizing our Vue.js codebase while acknowledging that some manual work will still be required to complete the migration process.