takeaway

Learning to be a better Vue developer isn’t always about big concepts that take time and effort to master.

Mastering a few tricks and tricks can make our programming lives a lot easier — without a lot of repetitive work.

Over the years of developing with Vue, I’ve learned many useful tips. Some are clever, some are used almost every day, some are more advanced — but they are all useful.

Deconstruction in V-for

Did you know you can use deconstruction in V-for?

<ul>
  <li v-for="(name,age,id) in userList" :key="id"></li>
</ul>
Copy the code

Better known, you can extract indexes from v-for by using such tuples.

<ul>
<li v-for="(movie, index) in [
  'Lion King',
  'Frozen',
  'The Princess Bride'
]">
  {{ index + 1 }} - {{ movie }}
</li>
</ul>

Copy the code

When working with an object, we can use key like this:

<ul>
<li v-for="(value, key) in {
  name: 'Lion King',
  released: 2019,
  director: 'Jon Favreau',
}">
  {{ key }}: {{ value }}
</li>
</ul>
Copy the code

You can also combine the two methods to get the key and the index of the property.

<ul>
<li v-for="(value, key, index) in {
  name: 'Lion King',
  released: 2019,
  director: 'Jon Favreau',
}">
  #{{ index + 1 }}. {{ key }}: {{ value }}
</li>
</ul>
Copy the code

Restrict a prop to a list of types

Using the Validator option in the PROP definition, you can restrict a prop type to a specific set of values.

export default {
  name: 'Image',
  props: {
    src: {
      type: String,
    },
    style: {
      type: String,
      validator: s => ['square', 'rounded'].includes(s)
    }
  }
};
Copy the code

This validation function takes a prop and returns true or false if prop is valid or invalid.

I usually use this method when passing only true or false to control that some condition does not satisfy the requirement.

Button types or warning types (information, success, danger, warning) are the most common usage,,, and. Color is also a good use.

Default content and extension points

Slots in Vue can have default content, which allows us to make components that are easier to use.

<button @click="$emit('click')">
  <slot>
    my is default slot content
  </slot>
</button>
Copy the code

We can take any part of the component, encapsulate it in a slot, and outside we can overlay that part of the component with whatever we want. By default, it will still work the same way, but there are more options for doing so.

<template>
  <button class="button" @click="$emit('click')">
    <slot>
      <div class="formatting">
        {{ text }}
      </div>
    </slot>
  </button>
</template>
Copy the code

Now we can use this component in many different ways. Simple, default, or custom.

<ly-button text="Formatted text" />

<ly-button>
  <div class="different-formatting">
    Do something a little different here
  </div>
</ly-button>
Copy the code

Traverses a number array in a specified range

<template>
  <ul>
    <li v-for="n in 5">Item #{{ n }}</li>
  </ul>
</template>
Copy the code

Render result:

Item #1
Item #2
Item #3
Item #4
Item #5

Copy the code

Use quotes to enforce escape properties

In case you didn’t know this, we can easily listen directly on nested values by using quotes:

watch { '$route.query.id'() { // ... }}Copy the code

How to properly override the styles of child components

Vue has a deep selector:

<style scoped>
.my-component >>> .child-component {
  font-size: 24px;
}
</style>
Copy the code

Note: If you are using CSS preprocessors like SCSS, you may need to use /deep/ instead.

How to create a responsive variable outside of Vue (Vue2 and 3)

If you get a variable outside of Vue, it’s good to make it reactive.

This way, we can use it in computed props, Watch, and anywhere else, and it works just like any other state in Vue.

If we use the options API, all we need to do is put it in the data section of the component:

const externalVariable = getValue(); export default { data() { return { reactiveVariable: externalVariable, }; }};Copy the code

If you use Vue3’s composite API, you can use REF or Reactive directly.

import { ref } from 'vue'; // Can be done entirely outside of the Vue component const externalVariable = getValue(); const reactiveVariable = ref(externalVariable); console.log(reactiveVariable.value);Copy the code

Use Reactive instead:

import { reactive } from 'vue'; // Can be done entirely outside of the Vue component const externalVariable = getValue(); // reactive only on objects and arrays const anotherReactiveVariable = reactive(externalVariable); // Access directly console.log(anotherReactiveVariable);Copy the code

Listen for anything in your component

export default {
  computed: {
    someComputedProperty() {
      // Update the computed prop
    },
  },
  watch: {
    someComputedProperty() {
      // Do something when the computed prop is updated
    }
  }
};
Copy the code

We can listen in:

  • Calculate attribute
  • props
  • Nested Values If you use the composite API, any value can be monitored, as long as it is a REF or Reactive object.

Detects clicks outside (or inside) the element

In the development of a project component, sometimes we need to encounter scenarios where we need to detect clicks inside or outside the component, such as dripdown and Dialog components clicking inside or outside the component. My general approach is as follows

Window. addEventListener('mousedown', e => {// Get the clicked element const clickedEl = e.target; If (el. Contains (clickedEl)) {// inside "el "click} else {// inside "el" click}});Copy the code

Recursive slot

At one point, I decided to see if I could make a V-for component using only templates. In the process, I also discovered how to use slots recursively.

<template> <div> <! Render first item --> {{list[0]}} <! If we have more projects, keep going! But don't use the item we just rendered --> <v-for V-if ="list.length > 1" :list="list.slice(1)" /> </div> </template>Copy the code

If you want to do this with a scope slot, it just needs some tweaking

<template> <div> <! -- Pass the item into the slot to be rendered --> <slot v-bind:item="list[0]"> <! -- Default --> {{ list[0] }} </slot> <v-for v-if="list.length > 1" :list="list.slice(1)" > <! -- Recursively pass down scoped slot --> <template v-slot="{ item }"> <slot v-bind:item="item" /> </template> </v-for> </div> </template>Copy the code

Here’s how to use this component.

<template> <div> <! -- Regular list --> <v-for :list="list" /> <! - bold project list - > < v - for: list = "list" > < template v - slot = "{item}" > < strong > {{item}} < / strong > < / template > < / v - for > < / div >  </template>Copy the code

Multifile single-file component

This is a known feature of **SFC(single file component)**.

You can import files like regular HTML files:

<template src="./template.html"></template>
<script src="./script.js"></script>
<style scoped src="./styles.css"></style>
Copy the code

This can be handy if you need to share styles, files, or anything else.

Reusable components are not what you think they are

Reusable components don’t have to be big or complex.

I often make small and short components reusable.

Because I’m not rewriting this code around, it’s easier to update it, and I can be sure that every OverflowMenu looks and works exactly the same — because they’re the same!” .

<template> <Menu> <! <template #button V-slot ="bind"> <! <Button v-bind="bind"> <template #icon> < SVG SRC ="./ellipsis. </Button> </template> </Menu> </template>Copy the code

Here, we take a menu component, but add an Ellipsis icon on the button that triggers it. (ellipsis) icon to trigger it to open.

It doesn’t seem worth making it a reusable component because it’s only a few lines long. Can’t we just add ICONS every time we use a menu like this?

But this OverflowMenu will be used dozens of times, and now if we want to update the icon or its behavior, we can do so very easily. It’s also easier to use.

Calls a method from outside the component

We can call a method from outside a component by giving it a ref.

<template>
  <ChildComponent ref="child" />
</template>
// Somewhere in Parent.vue
this.$refs.child.methodName();
Copy the code

Explain the problem again.

Sometimes “best practices” don’t apply to what you’re doing, and you need an escape hatch like this.

Typically, we use props and Events to communicate between components. The props is delivered to the child component, and events is posted to the parent component.

<template> <ChildComponent :tell-me-what-to-do="someInstructions" @something-happened="hereIWillHelpYouWithThat" /> </template> // Child.vue export default { props: ['trigger'], watch: { shouldCallMethod(newVal) { if (newVal) { // Call the method when the trigger is set to `true` this.method(); }}}}Copy the code

This works fine, but only on the first call. If you need to trigger this operation more than once, you must clean up and reset the state. The logic goes like this

    • The parent component passes true to trigger prop
  • The Watch is triggered, and the Child component calls the method

  • The child emits an event telling the parent that the method has been fired successfully

  • The Parent component resets trigger to false, so we can start all over again

  • Instead, if we set a ref on the child component, we can call this method directly:

<! -- Parent.vue --> <template> <ChildComponent ref="child" /> </template> // Somewhere in Parent.vue this.$refs.child.method();Copy the code

Yes, we broke the rule of “props down, events up”, we broke the encapsulation, but it was clearer and easier to understand, so it was worth it

Sometimes the “best” solution turns out to be the worst solution.

Listen for arrays and objects

The trickiest part about using Watcher is that sometimes it doesn’t seem to trigger correctly.

Usually, this is because we are trying to listen on arrays or objects, but deep is not set to true

export default { name: 'ColourChange', props: { colours: { type: Array, required: true, }, }, watch: {// Use object syntax instead of just method colours: {// This will let Vue know to look inside The array for deep: true, handler() console.log('The list of colours has changed! '); }}}}Copy the code

The API for using Vue 3 would look like this:

watch( colours, () => { console.log('The list of colours has changed! '); }, { deep: true, } );Copy the code

Another use for the template tag

The template tag can be used anywhere in the template to better organize your code.

I like to use it to simplify v-if logic, and sometimes V-for.

In this example, we have several elements that all use the same V-if condition.

<template>
  <div class="card">
    <img src="imgPath" />
    <h3>
      {{ title }}
    </h3>
    <h4 v-if="expanded">
      {{ subheading }}
    </h4>
    <div
      v-if="expanded"
      class="card-content"
    >
      <slot />
    </div>
    <SocialShare v-if="expanded" />
  </div>
</template>
Copy the code

It’s a little clunky and not obvious at first, with a bunch of these elements shown and hidden together. On a larger, more complex component, this can be a worse situation

But we can optimize it.

We can group these elements using the template tag and promote the V-if to the template itself.

<template> <div class="card"> <img src="imgPath" /> <h3> {{ title }} </h3> <template v-if="expanded"> <h4> {{ subheading  }} </h4> <div class="card-content"> <slot /> </div> <SocialShare /> </template> </div> </template>Copy the code

Now it’s easier to understand, and it’s easy to see what it’s doing.

A better way to handle errors (and warnings)

We can provide a custom handler for errors and warnings in Vue.

// Vue 3
const app = createApp(App);
app.config.errorHandler = (err) => {
  alert(err);
};

// Vue 2
Vue.config.errorHandler = (err) => {
  alert(err);
};
Copy the code

Bug tracking services like Bugsnag and Rollbar can hook these handlers to log errors, but you can also use them to handle errors more elegantly for a better user experience.

For example, if an error is not handled and the application doesn’t crash directly, you can display a full error screen for the user to refresh or try something else.

In Vue3, the error handler can only handle template and Watcher errors, but Vue2’s error handler can catch almost all errors. The warning handlers in both versions are only available during development.