## Component Splitting\n\nThe App.vue currently has complete functionality, but all the code is packed together.\n\nThe next step is to split it into components. You will learn:\n\n* How to write highly reusable child components in a Tailwind v4 environment.\n* How to use defineProps and defineEmits to pass data between JS components.\n\nNow create the following four files in the `src/components` directory:\n\n* `TaskHeader.vue` (pure display)\n* `TaskInput.vue` (input logic)\n* `TaskFilter.vue` (state toggle)\n* `TaskItem.vue` (single task)\n\n!(#)\n\n### TaskHeader.vue β Static Display Component\n\nThis component doesnβt require any JS logic; it only needs to display the UI.\n\nIn Tailwind v4, we continue using the new gradient syntax.\n\n## Example\n\n\n\n
TaskHub
\n\n
Vue 3 + Tailwind v4 Combat
\n\n\n\n### TaskInput.vue β Use defineEmits to Send Data\n\nThis component is responsible for capturing user input. When a user clicks add, it doesn't directly modify the data but notifies the parent component via emit.\n\n## Example\n\n import { ref } from 'vue';\n\n// Define the event name emitted by the component\n\n const emit = defineEmits(['add-task']);\n\nconst title = ref('');\n\nconst handleSubmit = () => {\n\n const value = title.value.trim();\n\n if (!value) return;\n\n// Emit the event and pass the data as an argument\n\n emit('add-task', value);\n\n title.value = '';\n\n };\n\n
\n\n\n\n
\n\n### TaskFilter.vue β v-model Communication Between Components\n\nIn Vue 3.4+, it's recommended to use defineModel() to implement two-way binding. This is efficient for UI toggling in Tailwind v4.\n\n## Example\n\n // Define v-model; values passed from the parent component will automatically synchronize\n\n const modelValue = defineModel();\n\nconst filters = ['all', 'active', 'completed'];\n\n
\n\n\n\n
\n\n### TaskItem.vue β Receive Data Using defineProps\n\nIt is the core child component, responsible for displaying a single task and reflecting modify/delete actions.\n\n## Example\n\n // Receive the task object passed from the parent component\n\n defineProps({\n\n task: {\n\n type: Object,\n\n required: true\n\n }\n\n });\n\n// Define events to notify the parent component\n\n const emit = defineEmits(['toggle', 'remove']);\n\n
\n\n
\n\n\n\n {{ task.title }}\n\n\n\n
\n\n\n\n
\n\n### Refactored App.vue (Logic Controller)\n\nThe App.vue now becomes a clean commander, only responsible for managing raw data and handling instructions passed from child components.\n\n## Example\n\n import { ref, computed } from 'vue';\n\n // Import child components\n\n import TaskHeader from './components/TaskHeader.vue';\n\n import TaskInput from './components/TaskInput.vue';\n\n import TaskFilter from './components/TaskFilter.vue';\n\n import TaskItem from './components/TaskItem.vue';\n\n// --- Data State ---\n\n const tasks = ref([\n\n { id: '1', title: 'Experience Tailwind v4 New Features', isCompleted: false },\n\n { id: '2', title: 'Master the Composition API', isCompleted: true }\n\n ]);\n\n const filter = ref('all');\n\n// --- Logic Handling ---\n\n const handleAddTask = (title) => {\n\n tasks.value.unshift({ id: crypto.randomUUID(), title, isCompleted: false });\n\n };\n\nconst handleRemoveTask = (id) => {\n\n tasks.value = tasks.value.filter(t => t.id !== id);\n\n };\n\nconst handleToggleTask = (id) => {\n\n const task = tasks.value.find(t => t.id === id);\n\n if (task) task.isCompleted = !task.isCompleted;\n\n };\n\n// --- Computed Properties ---\n\n const filteredTasks = computed(() => {\n\n if (filter.value === 'active') return tasks.value.filter(t => !t.isCompleted);\n\n if (filter.value === 'completed') return tasks.value.filter(t => t.isCompleted);\n\n return tasks.value;\n\n });\n\n
\n\n
\n\n\n\n
\n\n
\n\n
\n\n No related tasks available...\n\n
\n\n\n\n
\n\n
\n\n .list-enter-active, .list-leave-active { transition: all 0.4s cubic-bezier(0.18, 0.89, 0.32, 1.28); }\n\n .list-enter-from, .list-leave-to { opacity: 0; transform: translateX(30px); }\n\n### Core Knowledge Points Summary\n\n**`defineProps` (Parent β‘ Child)**:\n\n* `TaskItem` receives task data via `props`. In Vue, Props are **read-only**. Child components should never directly modify `props` values; this is "one-way data flow".\n\n**`defineEmits` (Child β‘ Parent)**:\n\n* When a child component needs to change data (e.g., delete, check), it signals via `emit`, and the parent component performs the actual modification logic. This ensures data is modified from a single source, making debugging easier.\n\n**`defineModel` (Two-Way Binding Magic)**:\n\n* In `TaskFilter`, we used `defineModel` introduced in Vue 3.4. It greatly simplifies state synchronization code between parent and child components, eliminating the need to manually write `props` and listen for `@update:modelValue`.\n\n**Tailwind v4 Reusability**:\n\n* Even after splitting components, Tailwind's atomic classes remain effective. This is because the Vite plugin automatically scans all `.vue` files under `src` and generates corresponding styles.