Some features of Vue exist to accommodate rare edge cases or smoother migrations from a legacy code base. When overused however, they can make your code more difficult to maintain or even become a source of bugs. These rules shine a light on potentially risky features, describing when and why they should be avoided.
scoped
Element selectors should be avoided with scoped
.
Prefer class selectors over element selectors in scoped
styles, because large numbers of element selectors are slow.
To scope styles, Vue adds a unique attribute to component elements, such as data-v-f3f3eg9
. Then selectors are modified so that only matching elements with this attribute are selected (e.g. button[data-v-f3f3eg9]
).
The problem is that large numbers of element-attribute selectors (e.g. button[data-v-f3f3eg9]
) will be considerably slower than class-attribute selectors (e.g. .btn-close[data-v-f3f3eg9]
), so class selectors should be preferred whenever possible.
<template> <button>×</button> </template> <style scoped> button { background-color: red; } </style>
<template> <button class="btn btn-close">×</button> </template> <style scoped> .btn-close { background-color: red; } </style>
Props and events should be preferred for parent-child component communication, instead of this.$parent
or mutating props.
An ideal Vue application is props down, events up. Sticking to this convention makes your components much easier to understand. However, there are edge cases where prop mutation or this.$parent
can simplify two components that are already deeply coupled.
The problem is, there are also many simple cases where these patterns may offer convenience. Beware: do not be seduced into trading simplicity (being able to understand the flow of your state) for short-term convenience (writing less code).
<script setup> defineProps({ todo: { type: Object, required: true } }) </script> <template> <input v-model="todo.text" /> </template>
<script setup> import { getCurrentInstance } from 'vue' const props = defineProps({ todo: { type: Object, required: true } }) const instance = getCurrentInstance() function removeTodo() { const parent = instance.parent if (!parent) return parent.props.todos = parent.props.todos.filter((todo) => { return todo.id !== props.todo.id }) } </script> <template> <span> {{ todo.text }} <button @click="removeTodo">×</button> </span> </template>
<script setup> defineProps({ todo: { type: Object, required: true } }) const emit = defineEmits(['input']) </script> <template> <input :value="todo.text" @input="emit('input', $event.target.value)" /> </template>
<script setup> defineProps({ todo: { type: Object, required: true } }) const emit = defineEmits(['delete']) </script> <template> <span> {{ todo.text }} <button @click="emit('delete')">×</button> </span> </template>
© 2013–present Yuxi Evan You
Licensed under the MIT License.
https://vuejs.org/style-guide/rules-use-with-caution