Refactoring Vue Components: From Options API to Composition API with TypeScript
This challenge focuses on refactoring existing Vue 2 Options API components into the more modern and flexible Vue 3 Composition API using TypeScript. Refactoring to Composition API offers improved code organization, reusability, and better TypeScript integration, making it an essential skill for modern Vue development.
Problem Description
You are tasked with refactoring several Vue 2 components that are currently written using the Options API and JavaScript into Vue 3 Composition API components using TypeScript. The goal is to maintain the existing functionality while improving the code structure and leveraging the benefits of Composition API and TypeScript.
Key Requirements:
- Component Refactoring: Convert each provided Options API component into a Composition API component.
- TypeScript Implementation: Ensure all refactored components are written in TypeScript, leveraging types for props, data, methods, computed properties, and lifecycle hooks.
- Functionality Preservation: The refactored components must behave identically to their original JavaScript counterparts. All emitted events and rendered outputs should remain the same.
- Composition API Best Practices: Utilize the Composition API features like
setup(),ref,reactive,computed,watch, and lifecycle hooks (onMounted,onUpdated, etc.) appropriately. - Code Organization: Group related logic together within the
setup()function. - Reusability (Optional but Recommended): If applicable, identify opportunities to extract reusable logic into separate composable functions.
Expected Behavior:
Each refactored component should render and interact with the user exactly as the original component did. For instance, if a component displays a counter and has a button to increment it, the refactored version should do the same.
Edge Cases to Consider:
- Complex Data Structures: Handling nested objects or arrays in reactive state.
- Custom Directives: Ensure any custom directives are correctly applied in the Composition API context.
- Event Handling: Verify that all emitted events are correctly handled and passed.
- Lifecycle Hook Timing: Ensure equivalent lifecycle hooks are used and called at the correct times.
Examples
Example 1: Counter Component
Original Vue 2 Options API (JavaScript):
// Counter.vue
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
},
};
</script>
Input for Refactoring: The above Counter.vue file.
Expected Output (Vue 3 Composition API with TypeScript):
// Counter.vue
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const count = ref<number>(0);
const increment = (): void => {
count.value++;
};
</script>
Explanation:
The data property count is replaced by ref<number>(0). The methods property increment is transformed into a function directly within the setup scope. this.count is replaced by count.value.
Example 2: Prop and Emit Component
Original Vue 2 Options API (JavaScript):
// Emitter.vue
<template>
<div>
<p>Message: {{ message }}</p>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script>
export default {
props: {
initialMessage: {
type: String,
required: true,
},
},
data() {
return {
message: this.initialMessage,
};
},
methods: {
sendMessage() {
this.$emit('message-sent', this.message.toUpperCase());
},
},
};
</script>
Input for Refactoring: The above Emitter.vue file.
Expected Output (Vue 3 Composition API with TypeScript):
// Emitter.vue
<template>
<div>
<p>Message: {{ message }}</p>
<button @click="sendMessage">Send Message</button>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue';
// Define props with explicit types
interface Props {
initialMessage: string;
}
const props = defineProps<Props>();
// Define emits with explicit types
const emit = defineEmits<{
(e: 'message-sent', payload: string): void;
}>();
const message = ref<string>(props.initialMessage);
const sendMessage = (): void => {
emit('message-sent', message.value.toUpperCase());
};
</script>
Explanation:
props are defined using defineProps<Props>(). data property message is replaced by ref<string>(props.initialMessage). this.$emit is replaced by emit(). The emit signature is explicitly defined using defineEmits.
Constraints
- You will be provided with Vue 2 Options API components written in JavaScript.
- Your refactored solution must be in Vue 3 Composition API using TypeScript.
- The components should be single-file components (
.vuefiles). - Focus on the core logic of the components; complex third-party library integrations are out of scope unless they are fundamental to the component's logic.
- Ensure all types are correctly inferred or explicitly defined in TypeScript.
Notes
- Pay close attention to how
thiscontext is handled in the Options API and how it translates to Composition API. - Consider using
script setupfor a more concise Composition API syntax. - Remember to import necessary functions from
vue(e.g.,ref,computed,watch,onMounted). - Think about how computed properties and watchers are implemented in the Composition API.
- For prop and emit definitions,
definePropsanddefineEmitsare highly recommended. - The goal is to demonstrate understanding of the refactoring process and the capabilities of Vue 3 Composition API with TypeScript.