This is the best practice I developed when using a large code base for Vue projects. These tips will help you develop more effective code, easier to maintain and share.
In my freelance career this year, I have the opportunity to work on some large Vue applications. The project I am talking about has more than 12 Vuex stores, a large number of components (sometimes hundreds) and many views (pages). In fact, this is a very meaningful experience for me, because I found many interesting patterns to make the code scalable. I must also fix some wrong practices that caused the famous spaghetti code problem. 🍝
So, today, I will share with you 10 best practices, if you are dealing with a large code base, I suggest you follow these best practices. ♀️
1. Use slots to make components easier to understand and more powerful
I recently wrote an article about some important things you need to know about . It focuses on how slots make your components more reusable and easier to maintain, and why you should use them.
🧐But what does this have to do with large Vue.js projects? A picture is worth a thousand words, so I will draw a picture for you. This is the first time I regret not using them.
One day, I just need to create a pop-up window. At first glance, there is nothing really complicated, just a title, description and some buttons. So what I have to do is treat everything as attributes. Finally, I used three properties to customize the component, and an event will be emitted when people click the button. It's very simple! 😅
However, as the project continued to evolve, the team asked us to display many other new content in it: form fields, different buttons (depending on which page is displayed), cards, footers, and lists. I found that if I continue to use attributes to make this component continue to expand, it seems to work. But God, 😩I was wrong! The component quickly became too complex to understand because it contained countless sub-components, used too many properties, and emitted a lot of events. 🌋I went through a terrible situation where when you make a change somewhere, it ends up destroying other content on another page in some way. I made a Frankenstein monster, not a maintainable component! 🤖
However, if I rely on slots from the beginning, things might be better. Finally, I refactored everything to provide this small component. Easy to maintain, faster to understand and more scalable!
<template>
<div class="c-base-popup">
<div v-if="$slots.header" class="c-base-popup__header">
<slot name="header">
</div>
<div v-if="$slots.subheader" class="c-base-popup__subheader">
<slot name="subheader">
</div>
<div class="c-base-popup__body">
<h1>{{ title }}</h1>
<p v-if="description">{{ description }}</p>
</div>
<div v-if="$slots.actions" class="c-base-popup__actions">
<slot name="actions">
</div>
<div v-if="$slots.footer" class="c-base-popup__footer">
<slot name="footer">
</div>
</div>
</template>
<script> export default {
props: {
description: {
type: String,
default: null
},
title: {
type: String,
required: true
}
}
} </script>
My point is that, based on experience, projects built by developers who know when to use slots do have a big impact on their future maintainability. This reduces the number of events that are emitted, makes the code easier to understand, and provides greater flexibility in displaying any required components internally.
⚠️As a rule of thumb, remember that when you finally copy the properties of the child component in the parent component of the child component, you should start using the slot from this point.
2. Organize your Vuex storage properly
Usually, new Vue.js developers start learning Vuex because they stumbled upon the following two problems:
- They either need to access the data of a given component from another component that is actually too far away in the tree structure, or
- They need the data to survive the destruction of the component.
That was when they created the first Vuex store, learned about the modules, and started organizing in the application. 💡
The problem is that there is no single pattern to follow when creating a module. However, 👆🏼I strongly recommend that you consider how to organize them. As far as I know, most developers like to organize them by function. E.g:
- Verification code
- Blog
- inbox
- set up
In my case, I find it easier to understand when organizing them based on the data model they extract from the API. E.g:
- User number
- Team
- Message content
- small parts
- article
Which one you choose is up to you. The only thing to remember is that in the long run, a well-organized Vuex storage will make the team more productive. This will also make it easier for newcomers to surround your code base when they join your team.
3. Use Vuex Actions to make API calls and submit data
Most of my API calls (if not all) are made in my Vuex actions. You may be wondering: Why is it better to call here? 🤨
Just because most of them extract the data that I need to submit in the vuex store. In addition, they provide encapsulation and reusability that I really like. I did this for some other reasons:
- If I need to get the homepage of the article in two different places (for example, the blog and the homepage), I can call the appropriate scheduler with the correct parameters. The data will be extracted, submitted and returned, except for the dispatcher call, there is no duplicate code.
- If I need to create some logic to avoid fetching it when fetching the first page, I can do it in one place. In addition to reducing the load on the server, I have confidence that it can be used anywhere.
- I can track most of my Mixpanel events in these actions (vuex actions), which makes the analysis code base really easy to maintain. I do have some applications where all Mixpanel calls are made individually in operation. When I don’t have to know what to track, what’s not to track and when to send, 😂 this way of working will bring me much happiness.
Annotation: Mixpanel is a data tracking and analysis company that allows developers to track various user behaviors, such as the number of pages users browse, iPhone application analysis, Facebook application interaction, and Email analysis. A buried point analysis tool similar to Firebase.
4. Use mapState, mapGetters, mapMutations and mapAction to simplify the code base
When you only need to access state/getter or call action/mutation inside a component, there is usually no need to create multiple calculated properties or methods. Using mapState
, mapGetters
, mapMutations
and mapActions
can help you shorten your code, simplify the complex by grouping, and grasp the overall situation from one place in your storage module.
// NPM
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
computed: {
// Accessing root properties
...mapState("my_module", ["property"]),
// Accessing getters
...mapGetters("my_module", ["property"]),
// Accessing non-root properties
...mapState("my_module", {
property: state => state.object.nested.property
})
},
methods: {
// Accessing actions
...mapActions("my_module", ["myAction"]),
// Accessing mutations
...mapMutations("my_module", ["myMutation"])
}
};
JavaScript
Vuex official document provides all the information you need on these convenient helpers. 🤩
5. Use API Factory
I usually like to create an this.$api
can be called anywhere to get the API endpoint. In the root directory of the project, I have a api
containing all classes (see one of them below).
api
├── auth.js
├── notifications.js
└── teams.js
Each node groups all the endpoints of its category. This is how I initialized this mode with a plugin in a Nuxt application (this is very similar to the process in a standard Vue application).
// PROJECT: API
import Auth from "@/api/auth";
import Teams from "@/api/teams";
import Notifications from "@/api/notifications";
export default (context, inject) => {
if (process.client) {
const token = localStorage.getItem("token");
// Set token when defined
if (token) {
context.$axios.setToken(token, "Bearer");
}
}
// Initialize API repositories
const repositories = {
auth: Auth(context.$axios),
teams: Teams(context.$axios),
notifications: Notifications(context.$axios)
};
inject("api", repositories);
};
JavaScript
export default $axios => ({
forgotPassword(email) {
return $axios.$post("/auth/password/forgot", { email });
},
login(email, password) {
return $axios.$post("/auth/login", { email, password });
},
logout() {
return $axios.$get("/auth/logout");
},
register(payload) {
return $axios.$post("/auth/register", payload);
}
});
JavaScript
Now, I can simply call them in my component or Vuex action as follows:
export default {
methods: {
onSubmit() {
try {
this.$api.auth.login(this.email, this.password);
} catch (error) {
console.error(error);
}
}
}
};
JavaScript
6. Use $config to access your environment variables (especially useful in templates)
Your project may define some global configuration variables in some files:
config
├── development.json
└── production.json
I like this.$config
assistant, especially when I am in the template. As always, it is very easy to extend Vue objects:
// NPM
import Vue from "vue";
// PROJECT: COMMONS
import development from "@/config/development.json";
import production from "@/config/production.json";
if (process.env.NODE_ENV === "production") {
Vue.prototype.$config = Object.freeze(production);
} else {
Vue.prototype.$config = Object.freeze(development);
}
JavaScript
7. Follow a convention to write commit comments
As the project develops, you will need to periodically browse the submission history of the component. If your team does not follow the same conventions to write their submission instructions, it will be difficult to understand the behavior of each team member.
I always use and recommend the Angular commit message guideline . In every project I work on, I will follow it, and in many cases, other team members will quickly find that it is better to follow it.
Following these guidelines will result in more readable messages, making it easier to track submissions when viewing the project history. In short, this is how it works:
git commit -am "<type>(<scope>): <subject>"
# Here are some samples
git commit -am "docs(changelog): update changelog to beta.5"
git commit -am "fix(release): need to depend on latest rxjs and zone.js"
Check out their README file for more conventions.
8. Always freeze the version of the package when the project is produced
I know...all packages should follow the semantic version control rule . But the reality is that some of them are not. 😅
In order to avoid one of your dependencies waking up in the middle of the night and ruining the entire project, locking the versions of all packages will make your morning work less stressful. 😇
Its meaning is simple: avoid using the version starting ^
{
"name": "my project",
"version": "1.0.0",
"private": true,
"dependencies": {
"axios": "0.19.0",
"imagemin-mozjpeg": "8.0.0",
"imagemin-pngquant": "8.0.0",
"imagemin-svgo": "7.0.0",
"nuxt": "2.8.1",
},
"devDependencies": {
"autoprefixer": "9.6.1",
"babel-eslint": "10.0.2",
"eslint": "6.1.0",
"eslint-friendly-formatter": "4.0.1",
"eslint-loader": "2.2.1",
"eslint-plugin-vue": "5.2.3"
}
}
9. Use Vue virtual scroll bar when displaying large amounts of data
When you need to display many rows in a given page or need to iterate through large amounts of data, you may have noticed that the page is rendered very fast. To solve this problem, you can use vue-virtual-scoller .
npm install vue-virtual-scroller
It will only render the visible items in the list and reuse components and dom elements to make it as efficient as possible. It's really easy to use and very smooth! ✨
<template>
<RecycleScroller
class="scroller"
:items="list"
:item-size="32"
key-field="id"
v-slot="{ item }"
>
<div class="user">
{{ item.name }}
</div>
</RecycleScroller>
</template>
HTML
10. Track the size of third-party packages
When many people are working on the same project, if no one pays attention to them, the number of installed packages will increase rapidly, which is unbelievable. In order to prevent your application from slowing down (especially when the mobile network slows down), I used import fee package in Visual Studio Code. In this way, I can directly see how big the imported module library is from the editor, and can see what went wrong when the imported module library is too large.
For example, in a recent project, the entire lodash library was imported (approximately 24kB after compression). The problem is that only cloneDeep is used in the project. After identifying this problem in the import expense package , we solved the problem in the following ways:
npm remove lodash
npm install lodash.clonedeep
Then you can import the clonedeep function where needed:
import cloneDeep from "lodash.clonedeep";
JavaScript
For further optimization, you can also use the Webpack Bundle Analyzer software package to visualize the size of the Webpack output file through an interactive scalable tree diagram.
Do you have any other best practices when dealing with large Vue code bases? Please let me know in the comments below, or contact me on @RifkiNada . 🤠
About the author
Nada Rifki
Nada is a JavaScript developer who likes to use UI components to create interfaces with great UX. She specializes in Vue.js and likes to share anything that can help her front-end web developers. Nada is also involved in digital marketing, dance and Chinese.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。