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
dataproperties toreforreactive. - Convert
computedproperties tocomputed()functions. - Convert
methodsto regular functions within thesetup()function. - Convert lifecycle hooks to their Composition API equivalents (e.g.,
mountedtoonMounted). - Convert Vue 2 specific lifecycle hooks to their Vue 3 equivalents.
- Convert
watchproperties towatch()functions. - Handle
propsandinjectconversions. - Replace
thisreferences with direct references to reactive variables. - Convert writable computed properties.
- Handle reactive object reassignments using
Object.assign - Handle correct usage of
refand 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
$refsusage.
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
refusage. - Complex data structures: While simple
dataproperties 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 theuseStorecomposition 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
shallowReforshallowReactiveare 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
thiscontext 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
$emitin the Options API may need manual conversion todefineEmitsin the Composition API and then using theemitfunction.
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.