Refactoring a Vue Component for Improved Readability and Reusability
This challenge focuses on refactoring a moderately complex Vue component to improve its readability, maintainability, and reusability. You'll be given a component with intertwined logic and styling, and your task is to restructure it using best practices like component composition, computed properties, and potentially extracting smaller, reusable components. This is a common task in real-world Vue development, and mastering it is crucial for building scalable applications.
Problem Description
You are given a Vue component that displays a list of products, allowing users to filter them by category and sort them by price. The component currently has all its logic and styling tightly coupled within a single file. Your task is to refactor this component to adhere to the following principles:
- Separate Concerns: Extract the filtering and sorting logic into computed properties. This will make the component's template cleaner and easier to understand.
- Component Composition (Optional but Recommended): If possible, identify parts of the component that could be extracted into smaller, reusable components (e.g., a filter dropdown, a sorting control, a product item). This will improve code reusability and testability.
- Improved Readability: Use meaningful variable names and comments to enhance the code's clarity.
- Maintainability: Structure the code in a way that makes it easy to modify and extend in the future.
Expected Behavior:
The refactored component should maintain the same functionality as the original:
- Display a list of products.
- Allow users to filter products by category.
- Allow users to sort products by price (ascending or descending).
- The displayed list should update dynamically based on the selected filter and sort order.
Edge Cases to Consider:
- Empty product list: Handle the case where there are no products to display gracefully.
- Invalid filter/sort options: Ensure the component handles invalid user input without errors.
- Performance: While not a primary focus, consider the performance implications of your refactoring, especially if dealing with a large product list.
Examples
Example 1:
Input:
products = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
{ id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
{ id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
{ id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
],
filterCategory = 'Electronics',
sortOrder = 'asc'
Output:
[
{ id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
{ id: 1, name: 'Laptop', category: 'Electronics', price: 1200 }
]
Explanation: The products are filtered by 'Electronics' and sorted in ascending order by price.
Example 2:
Input:
products = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
{ id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
{ id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
{ id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
],
filterCategory = 'Clothing',
sortOrder = 'desc'
Output:
[
{ id: 4, name: 'Jeans', category: 'Clothing', price: 75 },
{ id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 }
]
Explanation: The products are filtered by 'Clothing' and sorted in descending order by price.
Example 3: (Empty Product List)
Input:
products = [],
filterCategory = 'Electronics',
sortOrder = 'asc'
Output:
[]
Explanation: An empty array is returned as there are no products to display.
Constraints
- Language: TypeScript
- Framework: Vue 3 (Composition API preferred, but Options API is acceptable)
- Data Size: The
productsarray can contain up to 100 items. - Performance: The filtering and sorting operations should complete within a reasonable timeframe (e.g., less than 500ms). While optimization isn't the primary goal, avoid excessively inefficient algorithms.
- Input Format:
productsis an array of objects, each withid(number),name(string),category(string), andprice(number) properties.filterCategoryis a string representing the category to filter by.sortOrderis a string, either 'asc' or 'desc'.
Notes
- You are provided with the initial component code (see below). Your task is to refactor it.
- Focus on improving the structure and readability of the code.
- Consider using Vue's reactivity system to ensure the component updates efficiently.
- Feel free to add any necessary helper functions or utility methods.
- The provided initial component code is intentionally verbose and lacks proper structure to highlight the benefits of refactoring.
Initial Component Code (to be refactored):
<template>
<div>
<h2>Product List</h2>
<select v-model="filterCategory">
<option value="">All Categories</option>
<option value="Electronics">Electronics</option>
<option value="Clothing">Clothing</option>
</select>
<select v-model="sortOrder">
<option value="asc">Price (Ascending)</option>
<option value="desc">Price (Descending)</option>
</select>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.name }} - ${{ product.price }} - {{ product.category }}
</li>
</ul>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
export default defineComponent({
name: 'ProductList',
setup() {
const products = ref([
{ id: 1, name: 'Laptop', category: 'Electronics', price: 1200 },
{ id: 2, name: 'T-Shirt', category: 'Clothing', price: 25 },
{ id: 3, name: 'Headphones', category: 'Electronics', price: 150 },
{ id: 4, name: 'Jeans', category: 'Clothing', price: 75 }
]);
const filterCategory = ref('');
const sortOrder = ref('asc');
const filteredProducts = computed(() => {
let filtered = products.value;
if (filterCategory.value !== '') {
filtered = filtered.filter(product => product.category === filterCategory.value);
}
return filtered;
});
const sortedProducts = computed(() => {
let sorted = [...filteredProducts.value]; // Create a copy to avoid mutating the original
sorted.sort((a, b) => {
if (sortOrder.value === 'asc') {
return a.price - b.price;
} else {
return b.price - a.price;
}
});
return sorted;
});
return {
products,
filterCategory,
sortOrder,
filteredProducts,
sortedProducts
};
}
});
</script>