From Co-location to the Pain Points of Vue Maintenance
What is Co-location
This sharing originated from reading an article where the author pondered something we take for granted: why are comments always right next to the code?
Why not create a giant README.md, or a docs directory at the same level as src?
The author gave three reasons:
Synchronization: When modifying code, it's convenient to update comments synchronously, but if they are separated, comments will likely become outdated over time.
Applicability: Separating code and comments may cause people to overlook the existence of comments/documentation, because not every piece of code has comments.
Usability: Switching back and forth between files to match code with comments/documentation is really painful.
This makes us wonder: Is splitting JS/HTML/CSS in frontend projects a good practice?
It seems developers like to categorize different things into different directories, calling it "separation of concerns."
You can still see many examples of separation of concerns today:
Test cases are all placed in the
testdirectoryApplication state is all in the
storedirectoryUtility functions for dirty work are all in the
utilsdirectory
I’m not saying the practice of separation of concerns is always bad—putting test cases into a directory you rarely open does bring some peace (not).
But splitting project files this way can easily lead to a lot of code redundancy: when a component is deleted, the state it referenced may still remain in store, the utility functions used are still in utils, and in the CSS stylesheet, many style classes pile up without anyone knowing which are useful and which are not—future developers just keep adding new style classes on top.
The shit mountain grows higher and higher; you dare not delete or touch it, you can only keep piling on. Just like love.
Progress
Fortunately, modern frontend frameworks have identified the shortcomings of separation of concerns and proposed the concept of components.
We put the previously separated HTML/CSS/JS into a single file and gave it a new name: "component." It acts like a class, with inputs, properties, methods, and can be reused or packaged into a library.
From then on, we happily split UI design drafts into several components, then built them like building blocks into a complete UI, letting state flow through the components—this was the implementation logic of our early frontend projects.
But as the business logic of frontend projects becomes more complex, we find that merely abstracting and reusing around the UI no longer meets our needs. We also want to reuse logic.
So later React proposed React Hooks, and Vue 3 followed with "composables." It seems we are separating UI and logic, but separating logic is certainly not separation of concerns—it is further co-location.
Take a look at this React code!
// Code from React Query's Overview
function Example() {
const { isPending, error, data } = useQuery({})
if (isPending) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{' '}
<strong>✨ {data.stargazers_count}</strong>{' '}
<strong>🍴 {data.forks_count}</strong>
</div>
)
}
In this function, we co-locate UI and logic even further. Different logical branches return different UIs, greatly improving code readability and maintainability.
But unfortunately, I personally think it's hard to achieve such clear logic separation in Vue projects. Because Vue components are still based on a separation mindset, splitting a component into <script>, <template>, and <style> sections.
Let's try it:
<template>
<div v-if="isPending">Loading...</div>
<div v-else-if="error">An error has occurred: {{ error.message }}</div>
<div v-else>
<h1>{{ data.name }}</h1>
<p>{{ data.description }}</p>
<strong>👀 {{ data.subscribers_count }}</strong>
<strong>✨ {{ data.stargazers_count }}</strong>
<strong>🍴 {{ data.forks_count }}</strong>
</div>
</template>
<script setup>
import { useQuery } from '@tanstack/vue-query'; // Vue version of TanStack Query
// Logic part (directly co-located with template, similar to React Hooks)
const { isPending, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: () =>
fetch('https://api.github.com/repos/TanStack/query').then((res) =>
res.json()
),
});
</script>
To understand the role of isPending and error, I have to jump back and forth between the template and script sections. Why can't the UI be right next to the state? (I know Vue can use JSX, but that's not mainstream, nor the point.)
As the amount of code gradually increases, one screen can no longer hold both the template and the script simultaneously. To see the effect of a certain state, you have to scroll back and forth, and the drawbacks of separation of concerns come back. This is what I mean by the pain points of Vue code maintenance.
Fun Fact: Tailwind is More Popular than Vue
Although the vast majority of frontend developers in China use Vue, and Chinese developers generally dislike Tailwind—seeing it as the resurrection of inline styles—it has to be admitted that, judging by NPM weekly downloads, Vue and Tailwind are not in the same league. (No intention to belittle Vue, just an objective statement that Tailwind has become a mainstream technology choice.)
Why is Tailwind CSS so popular? I think it solves a problem that other style frameworks haven't solved: how to achieve co-location.
Imagine, before using Tailwind, what were we using? Preprocessors like SCSS/Less/Stylus. These preprocessors undoubtedly make CSS more powerful, but using them still forces us to switch screens back and forth to see the specific style definitions.
Tailwind CSS solves this problem: we only need to glance at the class content to see how the style is defined, no more switching screens or searching for definitions.
Conclusion
That concludes my sharing for this session. If you've read this far, I sincerely thank you.
I'd like to give you a suggestion: if you only know Vue (especially Vue 2), if you haven't used Tailwind CSS, and you don't follow the latest developments in this area, my advice is to start learning now. I don't intend to spread anxiety—this industry is like this: if you don't move forward, you fall behind.
Otherwise, you will also be shouting "The frontend is dead."