Get the most out of Vue

Vue.js Tips & Tricks

Dima Vishnevetsky

About me

Dima Vishnevetsky

📐 UX / UI Designer

🖼 Media Developer Expert @ Cloudinary

👨‍🏫 #HackathonMentor

🎓 Lecturer

🗣 International Tech speaker

👨‍💻 Front End Expert

Co-founder of

Vue.js Israel community leader

Transparent Wrappers

<template>
  <input 
    :value="value"
    @input="$emit('input', $event.target.value)" 
  >
</template>

BaseInput.vue

<BaseInput @focus.native="someFunc">
<BaseInput @focus.native="someFunc">
<template>
  <label>
    {{ label }}
    <input 
      :value="value"
      @input="$emit('input', $event.target.value)" 
    >
  </label>
</template>

Problem

<BaseInput @focus="someFunc">
<template>
  <label>
    {{ label }}
    <input 
      :value="value"
      @input="$emit('input', $event.target.value)"
      @focus="$emit('focus', $event)"
    >
  </label>
</template>

Bad solution

<BaseInput @focus="someFunc">
<template>
  <label>
    {{ label }}
    <input 
      :value="value"
      v-on="listeners"
    >
  </label>
</template>

Good solution

computed: {
  listeners() {
    return {
      ...this.$listeners,
      input: event => this.$emit('input', event.target.value)
    }
  }
}

No need for the ".native" modifier anymore

<template>
  <label>
    {{ label }}
    <input 
      :value="value"
      v-on="listeners"
    >
  </label>
</template>

Another problem

<BaseInput 
  placeholder="Placeholders are EVIL 😈"
  @focus="someFunc"
>
<BaseInput 
  placeholder="Placeholders are EVIL 😈"
  @focus="someFunc"
>
<template>
  <label>
    {{ label }}
    <input 
      v-bind="$attrs"     
      :value="value"
      v-on="listeners"
    >
  </label>
</template>

Best solution

inheritAttrs: false

Re-rendering route views

<template>
  <router-view 
    :key="$route.fullPath"
  ></router-view>
</template>

Custom v-model

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    // this allows using the `value` prop for a different purpose
    value: String,
    // use `checked` as the prop which take the place of `value`
    checked: {
      type: Number,
      default: 0
    }
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

Custom prop validators

Vue.component('my-component', {
  props: {
    propA: {
      validator: function (value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

Functional components

Vue.component('my-functional-component', {
  functional: true,
  // Props are optional
  props: {
    // ...
  },
  // To compensate for the lack of an instance,
  // we are now provided a 2nd context argument.
  render: function (createElement, context) {
    return createElement('p', 'Functional component')
  }
})
<template functional>
  <p>{{ props.someProp }}</p>
</template>

<script>
  export default {
    props: {
      someProp: String
    }
  }
</script>
const EmptyList = { /* ... */ }
const TableList = { /* ... */ }
const OrderedList = { /* ... */ }
const UnorderedList = { /* ... */ }

Vue.component('smart-list', {
  functional: true,
  props: {
    items: {
      type: Array,
      required: true
    },
    isOrdered: Boolean
  },
  render: function (createElement, context) {
    function appropriateListComponent () {
      const items = context.props.items
      if (items.length === 0)           return EmptyList
      if (typeof items[0] === 'object') return TableList
      if (context.props.isOrdered)      return OrderedList
      return UnorderedList
    }
    return createElement(
      appropriateListComponent(),
      context.data,
      context.children
    )
  }
})

Example

Don't use v-for with v-if

<template>
  <div>
    <p 
      v-for="item of items" 
      v-if="item < 5" 
      :key="item"
    >
      {{ item }}
    </p>
  </div>
</template>

<script>
export default {
  data () {
    return {
      items: [...Array(1000).keys()]
    }
  }
};
</script>

Bad example

<template>
  <div>
    <p 
      v-for="item of filteredItems" 
      :key="item"
    >
      {{ item }}
    </p>

  </div>
</template>

<script>
export default {
  name: "App",
  data () {
    return {
      items: [...Array(1000).keys()]
    }
  },
  computed: {
    filteredItems: function () {
      return this.items.filter(function (item) {
        return item < 5
      })
    }
  }
};
</script>

Good example

Best practices

Detailed prop definitions

// This is only OK when prototyping
props: ['status']

Bad

props: {
  status: {
    type: String,
    required: true,
    validator: function (value) { /* ... */ }
  }
}

Best

Component files

Vue.component('TodoList', {
  // ...
})

Vue.component('TodoItem', {
  // ...
})

Bad

components/
|- TodoList.vue
|- TodoItem.vue

Best

Base components

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

Bad

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

Best

Single instance components

components/
|- Heading.vue
|- MySidebar.vue

Bad

components/
|- TheHeading.vue
|- TheSidebar.vue

Best

Tightly coupled component names

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue



components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue

Bad

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue

Best

Full-word component names

components/
|- SdSettings.vue
|- UProfOpts.vue

Bad

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

Best

Simple expressions in templates

{{
  fullName.split(' ').map(function (word) {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

Bad

<!-- In a template -->
{{ normalizedFullName }}

// The complex expression has been moved to a computed property
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}

Best

Thank you

Dima Vishnevetsky

@dimshik100

www.dimshik.com

Vue tips & tricks 🪄🎩

By Dima Vishnevetsky

Vue tips & tricks 🪄🎩

  • 720