--- title: Colocate Template, Script, and Style in SFCs for Maintainability impact: MEDIUM impactDescription: Separating template, logic, and styles into different files reduces component cohesion and maintainability type: best-practice tags: [vue3, sfc, architecture, separation-of-concerns, maintainability] --- # Colocate Template, Script, and Style in SFCs for Maintainability **Impact: MEDIUM** - Vue SFCs are designed to colocate template, logic, and styles because these concerns are inherently coupled within a component. Separating them into different files based on file type (not concern) makes components harder to understand and maintain. ## Task Checklist - [ ] Keep template, script, and style in the same `.vue` file by default - [ ] Only use `src` imports when files become extremely large (500+ lines) - [ ] Group related components in folders rather than separating by file type - [ ] Use component composition to manage complexity instead of file splitting **Not Recommended:** ``` components/ ├── UserCard.vue # Just template ├── UserCard.js # Logic ├── UserCard.css # Styles ``` **Recommended:** ```vue ``` ## Why Colocation is Preferred ### 1. Coupled Concerns Should Stay Together Template, logic, and styles within a component are inherently coupled: - Template references reactive data from the script - Styles target classes used in the template - Changes in one often require changes in others ```vue ``` ### 2. Easier Navigation and Understanding Single file = single place to look: ```vue ``` ### 3. Better Refactoring Experience Renaming a class? Everything is in one file: ```vue ``` ## When Src Imports Are Acceptable For genuinely large components (rare), use src imports: ```vue ``` But first, consider if the component should be split into smaller components. ## Managing Complexity Without File Splitting ### Extract Logic to Composables ```typescript // composables/useDataTable.ts export function useDataTable(initialData: Ref) { const sortColumn = ref(null) const sortDirection = ref<'asc' | 'desc'>('asc') const currentPage = ref(1) const sortedData = computed(() => { ... }) const paginatedData = computed(() => { ... }) function sort(column: string) { ... } function goToPage(page: number) { ... } return { sortColumn, sortDirection, currentPage, sortedData, paginatedData, sort, goToPage } } ``` ```vue ``` ### Break Into Smaller Components ```vue ``` ## The Real Separation of Concerns True separation of concerns in Vue means: - **Components** handle their own template/logic/style (coupled by nature) - **Composables** handle reusable stateful logic - **Utilities** handle pure functions - **Stores** handle global state This is more maintainable than separating HTML/CSS/JS into different files. ## Reference - [Vue.js SFC Introduction](https://vuejs.org/guide/scaling-up/sfc.html#what-about-separation-of-concerns) - [Vue.js SFC Src Imports](https://vuejs.org/api/sfc-spec.html#src-imports)