written by Andre Liem
07/27/2017

Tips for keeping your Vue.js code clean

TL;DR
My opinionated list of tips for keeping your VueJs code cleaner. Reviewing the usage of eslint, ordering component properties, CSS, and mixins.

I started using Vuejs in the early days, before 2.0 and even 1.0 came out. Since then, things have really exploded and I've found myself coding with VueJs on most of my projects every single day. A result of building larger projects was that my code quality started to waiver, consistency wasn't clear, and this became really apparent when I had to merge work with other developers.

This year (2017), I decided it was time to start getting serious about laying down some general guidelines and heuristics for keeping my VueJs code consistent. Here are some of my tips that I hope you find useful. I would love to hear about the ones you implement.

Start with a JS lint

If you're using a linting already great! If not, then you really should consider adding one into your project. In case you don't know what a lint is, here's the official wikipedia definition.

In computer programming, lint is a Unix utility that flags some suspicious and non-portable constructs (likely to be bugs) in C language source code; generically, lint or a linter is any tool that flags suspicious usage in software written in any computer language.

Often, a good IDE has some basic lint built into it already. I use the IDE from the JetBrains family (PHPStorm variety) and it does some of this already.

If you're ensure how to get setup, I recommend trying out eslint. Lots of VueJs boilerplate projects will already come with this setup. If you need to setup it up manually, here's how you do it.

create JS lint file

touch .eslintrc.js 

The contents of mine follow the example provided by the NuxtJs boilerplate.

module.exports = {
  root: true,
  parser: 'babel-eslint',
  env: {
    browser: true,
    node: true
  },
  extends: 'standard',
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  rules: {},
  globals: {}
}

This file is indicating that we are using the babel-eslint so we are going to code in babel JS.

In case you're adding all of this yourself, you'll also need to make sure your package.json file has the proper eslint packages. Here is a list of some of the basics to get your going:

npm install babel-eslint 
npm install eslint 
npm install eslint-loader

There are a few more packages which will help as well. Here is an example of what I have in a package.json file for a site using NuxtJs.

"babel-eslint": "^7.1.1",
"eslint": "^3.15.0",
"eslint-config-standard": "^6.2.1",
"eslint-loader": "^1.6.1",
"eslint-plugin-html": "^2.0.0",
"eslint-plugin-promise": "^3.4.1",
"eslint-plugin-standard": "^2.0.1",

I'm not going to go into great depth on what all of this does, but here are some important points so you know what's going on.

eslint-loader is used for running the lint while webpack compiles your code. So, in order to have this hooked up you'll need something like below in the modules section of your webpack configuration file:

e.g. -

{
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  exclude: /(node_modules)/
}

If you prefer relying on your IDE for warnings, you might not want to add this. I prefer having this in as it really ensures that youre code is consistent.

babel-eslint is the type of code it expects to lint. In my case I code in babel JS, which is probably the majority of Vue Js developers.

The other files like eslint-plugin-standard are for fancier things which I won't get into, partially because I don't fully understand their uses yet. For now, I stick with this configuration and generally speaking my code adheres to the rules outlined on standardjs.com

Here's a snippet of some of the top rules straight from their site.

  • 2 spaces – for indentation
  • Single quotes for strings – except to avoid escaping
  • No unused variables – this one catches tons of bugs!
  • No semicolons – It's fine. Really!
  • Never start a line with (, [, or `
  • Space after keywords if (condition) { ... }
  • Space after function name function name (arg) { ... }
  • Always use === instead of == – but obj == null is allowed to check null || undefined.

This is pretty good stuff. My code is much cleaner already with the first 5 rules. Some might find them too strict though, and if that's the case you will want to dig in to standardjs and figure out how to configure the rules.

When using an IDE like PHPStorm, it should detect eslint and automatically work. But sometimes you might need to actually configure it as well like below.

Settings

Once you have it setup, it's really nice how quickly you can cleanup your code. For example, check out these two images below.

Settings

Settings

The first one is straight from my IDE, highlighting the problem immediately.

The second image shows webpack complaining about some code which is not conforming to the lint rules. As I mentioned, this will not allow your code to compile. You can configure this to not fail compilation with a few options.

options: {
 failOnWarning: true,
 failOnError: true,
}

If you set them to false then you can keep your build going. This might be useful if you are pulling eslint into a legacy project with a lot of code that is not going to adhere to it.

Define an order for your Component properties

A lint is great, but it generally doesn't do high level rules or arbitrary guidelines. For example, I like to order the properties in my components in a specific fashion so I know where to find stuff.

It doesn't matter what order you have, but it will help trying to stick to a general convention so that it's easier to find things.

My goal is to follow the vue component lifecycle diagram pretty closely. So an example of a component would be something like this:

<script type="text/babel">
  export default {
    components: {
    },
    mixins: [],
    data () {
    },
    beforeCreate() {
    },
    created() {
    },
    beforeMount () {
    },
    mounted() {
    },
    computed: {
    },
    methods: {
    },
    beforeUpdate() {
    },
    updated() {
    },
    beforeDestroy() {
    }
  }
</script>

Generally speaking, most components will only consist of 3-5 of these properties. I like to put the methods near the bottom because this is generally my largest property. Even with an IDE that supports code collapsing and quick method finding, I still find it helps knowing where things should be.

If you're going to create your own guideline for component property ordering, you better put it into some general styleguide for your team. I highly recommend doing so as I remember when working with another developer who tended to order things almost exactly the opposite of myself.

Mixins as utility libraries

Mixins are great for keeping your code reusable and components lighter. I try to keep my mixins very simple and focused on one task at hand. The only time I create larger mixins is for lots of utility code which generally is needed in most components.

For example, I find the following use cases lend themselves well to larger mixins:

  • date modification (e.g. - perhaps a wrapper around momentjs)
  • Math/Financial calculations
  • URL helpers

I'm a bit more careful with mixins which attempt to reuse functionality which is specific to the component's needs.

For example, lets say you need to display a user avatar in several components. Sometimes the user won't have an avatar, so you want to display a default image. Lets say it's something as trivial as the following code which is a basic conditional helper.

userAvatarUrl (user) {
  return (user.image) ? (user.image) : '/path/to/default_avatar_image.png'
}

This code is pretty specific, a good candidate for a mixin. But lets add another feature, we want to return HTML instead.

userAvatarHtml (user) {
  const imageUrl = (user.image) ? (user.image) : '/path/to/default_avatar_image.png'
  return `<div class='thumb'><img src='${imageUrl}' /></div>`
}

This is starting to get messy. If you can guarantee that every component that needs this mixin will use the same HTML, then it might work okay. But it could be one component needs to style it differently one day. So then you add an option.

userAvatarHtml (user, cssClass) {
  const imageUrl = (user.image) ? (user.image) : '/path/to/default_avatar_image.png'
  return `<div class='thumb ${cssClass}'><img src='${imageUrl}' /></div>`
}

Things are getting complex already. In the end I would much prefer sticking to the original mixin function which does one specific thing, return the path to the avatar image. And then let each component handle the Html around it. Even if most of them do the exact same styling, I prefer not abstracting this out into a mixin.

Scope CSS within components as much as possible and use a preprocessor

I love how Vue components let you write the template, javascript and CSS styles in one file.

What I really like is the scoped feature.

<style lang="less" scoped>

The reason I like scoped is because I don't have to think about all the CSS rules and what to name things. As long as I have a good understanding of what my global base CSS files are taking care of, I can scope all the components CSS and be confident it won't overwrite or affect other components.

Also, I really believe there is very little reason to use straight up CSS. The benefits of using a tool like SASS, Less, or Stylus are great.

Avoid lots or any indentation with CSS

Following the previous benefit of scoped CSS, I try to avoid using nested CSS rules completely unless it really makes sense.

For example, it would be quite common to nest related styles together as they appear in the DOM.

<style lang="less" scoped type="text/less">
.header {
  .title {
  }
  .sub-title {
  }
}
</style>

But do you really need to even nest title and sub-title? Given how focused your component is going to be, I would argue it's cleaner to just keep it all flat.

<style lang="less" scoped type="text/less">
.header {
}
.title {
}
.sub-title {
}
</style>

When you know that this CSS will only apply to this component's CSS, the benefits of nesting rules become less clear.

I do think it's useful to nest when the styling rules are about the DOM nesting relationship itself.

For example:

<style lang="less" scoped type="text/less">
ul {
  > li:not(:first-child) {
  }

  &:hover li:not(:last-child) {
  }
}

</style>

Or using flexbox where nested children need specific ordering:

<style lang="less" scoped type="text/less">
.main-box {
  display: flex;
  .box-1 {
    order: 2;
  }
  .box-2 {
    order: 1;
  }
}
</style>

In this cases above, the benefits of using nested preprocessor rules is clear to me. Having box-1 nested is a rule that only applies when it's a child of main-box.

Whereas in the original example, I only nested the rules because that's how the HTML would be structured but in reality this isn't that important if your components are small.

I could be wrong of course, there might be some other benefits to nesting in terms of how a browser parses the CSS and DOM... drop a comment if you have any feedback here!

Wrapping Up

With the lint and these guidelines, I have found my code is getting cleaner and staying more consistent. Adding the lint has by far added the most benefits but trying to follow some general heuristics layed out by yourself or your team is always a good idea before you get too deep into the project. After all, it's often the little things which amount to big problems in the end.