DEV Community

paschal
paschal

Posted on

Write better Vue JS code

Introduction

Architecture might not matter at the start of a project, but the ease at which components can be added or removed without breaking stuff shows how well the codebase was structured. Let's look at ways to make our Vue JS code better.

Use State, Map Getters and Actions.

Using state and the maps (i.e mapGetters, mapActions, mapState, etc) provided by Vuex makes code very reusable. Hard coding state to the data() object in your SFC because its "faster" will raise difficulties if some of those values are needed in the future.

<!-- first.vue -->
<template>
  <h3>{{firstname}}{{lastname}}</h3>
</template>

<script>
  export default {
    data() {
      return {
        firstname: "",
        lastname: ""
      };
    },
    methods: {
      async getFullName() {
        const { firstname, lastname } = await fetchNameFromApi();
        this.firstname = firstname;
        this.lastname = lastname;
      }
    },
    created() {
      this.getFullName();
    }
  };
</script>

Project Manager: We need the firstname and lastname to show up on two more pages.

With that request, you'll keep copying, pasting, importing and exporting from different files.

Better Still,

const state = {
  firstname: "",
  lastname: ""
};

const actions = {
  async getFullName({ commit, dispatch }, data) {
    getFullNameFromApi().then(res => {
      commit(mutate.FULL_NAME, res.body);
    });
  }
};

const mutations = {
  //Set default mutation types in another file
  [mutate.UPDATE_FULL_NAME](state, data) {
    state.firstname = data.firstName;
    state.lastname = data.lastName;
  }
};

const getters = {
  firstName: state => state.firstname,
  lastName: state => state.lastname
};

const FullName = {
  state,
  actions,
  mutations,
  getters
};

export default FullName;

Then on our first.vue component,

<template>
  <h3>{{firstName}}{{lastName}}</h3>
</template>

<script>
  import {mapGetters, mapActions} from 'vuex';

  export default {
   methods:{
   ...mapActions(['getFullName']);
   },
   created(){
   this.getFullName();
   },
   computed:{
   ...mapGetters(['firstName', 'lastName']);
   }
  }
</script>

Now, if we need to include a new component that needs the first and last names of our user, we can easily map the getters and the actions.

This also helps us avoid things like:

const firstname = this.$store.state.fullName.firstName;
const lastname = this.$store.state.fullName.lastName;

We can simply use getters

computed:{
 ...mapGetters(['firstName','lastName'])
}

Finally, this helps us abstract business logic from the SFC and makes testing easier. Allow the Store to handle all the logic, and the SFC should just handle stuff tightly coupled to it, like the state of alert buttons/snack bars, etc.

Filters over Mixins.

Mixins lead to implicit dependencies, namespace clashes, etc. You can find more about that here. Some Mixins can be converted to Filters.

//dateMixin.js
export default {
  methods: {
    formatDate(date) {
      return date.split("T")[0];
    }
  }
};

In our SFC, we have:

<template>
  <h3>{{formatDate(date)}}</h3>
</template>

<script>
  import dateMixin from "./dateMixin";

  export default {
    mixins: [dateMixin],
    data() {
      return {
        date: "2019-08-07T00:00:00"
      };
    }
  };
</script>

With filters,

//main.js
import Vue from "vue";

Vue.filter("formatDate", value => value.split("T")[0]);

In our SFC,

<template>
  <h3>{{date | formatDate}}</h3>
</template>

<script>
  export default {
    data() {
      return {
        date: "2019-08-07T00:00:00"
      };
    }
  };
</script>

Use Modules to separate the different services on your application.

Instead of having everything needed by our state in one object, we can segregate them into modules.

Instead of

const state = {
  token: "",
  amount: "",
  firstname: "",
  lastname: "",
  email: "",
  isLoggedIn: ""
};

We can divide our services into authentication, profile-management and wallet.

Our folder structure would look like

modules
 authentication
    index.js
 profile-management
    index.js
 wallet
    index.js

In the index.js file, we can have the state that matters to that service.

//modules/authentication/index.js

const state = {
 token: '',
 isLoggedIn:''
}

...

Then when we initialize our store, we can add all the modules.

export const store = new Vuex.store({
 state: {
    //something general
    isAppBusy: false
 },
 modules:{
    authentication,
    profile-management,
    wallet
 }
});

Conclusion

These are my thoughts on how to make the structure of Vue code better. If you have extra additions or subtractions, I'll like to see it in the comments πŸ˜„.

Top comments (7)

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€

I enjoyed using vuex for a shop I was building, but I am writing a portfolio now, I opted out of vuex, and yes I need a certain level of abstraction, but do you know what is better than local state management? No state, or atleast remote data. I have cut down on the need to use any of this with a headless CMS and graphql.

Collapse
 
jamesthomson profile image
James Thomson

Out of curiosity, what headless CMS are you using? Any articles you can recommend on this sort of stack? I've been very interested in giving GraphQL a go.

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€
Collapse
 
obbap profile image
paschal

Nice, that works also. The requirements of the application will justify how much a state management library is needed. Is there a repository for your shop ?

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€

I can't share the shops sorry it's unde private control. But that is the vux stuff. If you are interested in my portfolio in Vue with graphcm I can share this WIP

Collapse
 
mratiebatie profile image
Sjors van Dongen

Nice article, thank you. I think it’s even better to use mapState instead of a getter when the variable is not changed in the getter. Getters are actually provided to adjust a variable before returning it.

Collapse
 
obbap profile image
paschal

True, When there are so many getters and things get verbose, mapState works fine, but mapState actually helps you create computed getter properties under the hood. So you're still using getters. I saw that here, vuex.vuejs.org/guide/state.html