Here are my best practices when developing large Vue projects. These tips will help you develop more efficient, maintainable, and shareable code.

While freelancing this year, I had the opportunity to develop some large Vue applications. The Vuex Store has more than ten of these projects I’m talking about, and contains a large number of components (sometimes hundreds) and view pages. This was a useful experience for me, as I discovered many interesting patterns that make code more scalable. I also had to fix some bad practices that led to the famous spaghetti code dilemma.

So today I’m going to share with you 10 best practices that I recommend you follow if you’re dealing with a large code base.

1. Use slots to make components more powerful and easier to understand

Recently I wrote an article about vue.js Slot, which highlights how slot makes components easier to reuse and maintain, and why they should be used.

๐Ÿง But what does this have to do with the vue.js mega project? A picture is usually worth a thousand words, so I’m going to paint you a picture of the first time I regretted not using them.

One day, I’m going to create a Popup. At first glance there is nothing complicated, just a title, a description, and some buttons. So all I did was pass in all the configurations as props. I ended up defining three Props for custom components that send an event when the user clicks a button. So easy !

However, as the project grew, the team asked us to include more new content: form fields, different buttons (depending on which page it was displayed on), cards, footers, and lists. I don’t think it would be a problem if I continued iterating through this component using Prop. But my God, I was so wrong! The component quickly became very complex and difficult to understand because it contained countless child components, used too many props, and sent a bunch of events. I started to experience the scary situation where you make a change and somewhere else the page crashes! It was as if I had built a Frankenstein freak instead of a maintainable component! ๐Ÿค–

However, it would have been much better if I had relied on Slot from the start. I ended up refactoring everything to get this widget: easier to maintain, faster to understand, and more extensible!

<template>
  <div class="c-base-popup">
    <div v-if="$slot.header" class="c-base-popup__header">
      <slot name="header">
    </div>
    <div v-if="$slot.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="$slot.actions" class="c-base-popup__actions">
      <slot name="actions">
    </div>
    <div v-if="$slot.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>

Copy the code

My point is that, from experience, projects built by developers who know when to use slot do have a big impact on their future maintainability. Because fewer events are sent, the code is easier to understand and provides greater flexibility to display any component in it.

๏ธ knock on the blackboard: As a rule of thumb, you should consider using slot when you start copying prop from child components in parent components.

2. Organize Vuex Store reasonably

In general, vue.js novices start learning about Vuex because they happen to encounter these two problems:

  • Accessing data between components that are too far apart in the component tree structure
  • Data needs to persist after component destruction

This is when they create the first Vuex Store, learn the modules and start organizing them in the application.

The problem is that there is no single pattern to follow when creating modules. However, I strongly recommend that you think carefully about how you organize them. From what I’ve seen, most developers prefer to organize them by functionality. Such as:

  • Auth.
  • Blog.
  • Inbox.
  • Settings.

For my part, I find it easier to understand how to organize them according to the data model taken from the API. Such as:

  • Users
  • Teams
  • Messages
  • Widgets
  • Articles

The choice is up to you. The only thing to keep in mind is that a well-organized Vuex Store makes for a more efficient team in the long run. It will also make it easier for new people to build their ideas around your code base when they join the team.

3. Use action to initiate API calls and submit data

Most, if not all, of my API calls are done inside Vuex Actions. Why, you may ask? ๐Ÿคจ ๐Ÿคทโ™€๏ธ Simply put, most of the data they capture needs to be submitted to the store. In addition, they provide a layer of encapsulation and reusability that I love to use. Some other reasons are as follows:

  • If I need to get the first page of an article in two places (let’s say the blog page and the front page), I just need to call the appropriate Dispatcher with the right parameters. With the exception of dispatcher calls, data fetching, commit to store and return can be done without repeated code.

  • If I need to write some logic to avoid getting the first page twice, I can do it in one place. In addition to lightening the load on the server, this will give me more confidence in the code.

  • I can track most of these Mixpanel events, which makes the analysis code very easy to maintain. I do have some applications where all Mixpanel calls are done only in the action. I don’t need to know what data is being tracked, what isn’t, and when to send it. The joy of working in this way is indescribable.

4. Simplify code with mapState, mapGetters, mapMutations, and mapActions

Often you don’t need to create multiple computed properties or methods, just access state/getters inside the component or call Actions /mutations. Using mapState, mapGetters, mapMutations, and mapActions can help you simplify your code, grouping data from the Store module together, and making your code easier to understand.

// NPM
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";

exportdefault { 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"])}};Copy the code

All information about the above tools and methods is available in the official Vuex documentation. ๐Ÿคฉ

5. Use the API factory

I usually like to write a this.$API helper that I can call anywhere to get background API resources. There is an API folder at the root of my project that contains all the related classes. As shown below (only partially) :

โ”œโ”€ activities.js API โ”œโ”€ activities.js โ”œโ”€ activities.jsCopy the code

Each file groups all the API resources under its category. Here is how I initialized this pattern using a plug-in in my Nuxt application (the process is similar in my 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);
};

Copy the code
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); }});Copy the code

Now, I can simply call them in my component or Vuex action like this:

export default {
  methods: {
    onSubmit() {
      try {
        this.$api.auth.login(this.email, this.password); } catch (error) { console.error(error); }}}};Copy the code

6. Use $config to access environment variables (especially useful in templates)

Your project may have some global configuration variables defined in some files:

Config โ”œโ”€ download. json โ”œโ”€ download. TXTCopy the code

I like quick access to them through the this.$config helper, especially in templates. As usual, extending Vue objects is very easy:

// 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);
}

Copy the code

7. Commit code to a convention

As your project grows, you may need to review the history of your components on a regular basis. If your team doesn’t follow the same convention for naming their commits, then understanding each commit becomes more difficult.

I always recommend using Angular to submit information guides. I follow it on every project, and in many cases, other team members quickly discover the benefits of following it.

Following these guidelines leads to more readable information, which makes it easier to track submissions when looking at the project history. In a nutshell, here’s how it works:

git commit -am "<type>(<scope>): <subject>"

# for
git commit -am "docs(changelog): update changelog to beta.5"
git commit -am "fix(release): need to depend on latest rxjs and zone.js"

Copy the code

Take a look at their README file to learn more about it and related conventions.

8. Fixed package version after project launch

I know that all packages should follow semantic versioning rules. But the reality is that some are not.

To avoid having to wake up in the middle of the night because a dependency breaks the entire project, locking all package versions can make your morning routine less stressful.

What it means is simple: Avoid version numbers prefixed with the ^ :

{
  "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 requires ""}}Copy the code

9. Use Vue Virtual Scroller when displaying large amounts of data

When you need to display a large number of rows on a page, or loop through a large amount of data, you may have noticed that pages can get very slow very quickly. To solve this problem, you can use vue-virtual-scoller.

npm install vue-virtual-scroller

Copy the code

It will render only the items visible in the list and reuse components and DOM elements for high efficiency and good performance. It’s really easy to use and silky smooth! โœจ

<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="32"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>

Copy the code

10. Track the size of third-party packages

When many people are working on the same project, if no one pays attention to the number of installed packages, it can quickly become more and more. To avoid application slowdowns (especially on mobile networks), I use the Import Cost plug-in in VS Code. This way, I can see from my editor how big the imported module library is and check for problems when it gets too big.

For example, in a recent project, the entire Lodash library was imported (approximately 24kB of Gzipped). As a result, only the cloneDeep method was used. Using the import Cost plug-in to locate the problem, we solved it like this:

npm remove lodash
npm install lodash.clonedeep

Copy the code

The cloneDeep function can be introduced where needed:

import cloneDeep from "lodash.clonedeep";

Copy the code

๏ธ To further optimize, you can use Webpack Bundle Analyzer to visualize file sizes with interactive, scalable maps.


Do you have any other best practices to share about working with a large Vue code base? Leave a comment in the comments section.

The original

communication

Welcome to follow the wechat public number “1024 translation station”, for you to provide more technical dry goods.