Saltar al contenido

Reglas de Prioridad A: Esenciales

Nota

Esta Guía de Estilo de Vue.js está desactualizada y necesita ser revisada. Si tienes alguna pregunta o sugerencia, por favor abre una incidencia.

Estas reglas ayudan a prevenir errores, así que apréndelas y cúmplelas a toda costa. Pueden existir excepciones, pero deberían ser muy raras y solo ser hechas por aquellos con conocimientos expertos tanto de JavaScript como de Vue.

Usa nombres de componente de varias palabras

Los nombres de los componentes de usuario siempre deben ser de varias palabras, excepto para los componentes App raíz. Esto previene conflictos con elementos HTML existentes y futuros, ya que todos los elementos HTML son de una sola palabra.

Mal

template
<!-- in pre-compiled templates -->
<Item />

<!-- in in-DOM templates -->
<item></item>

Bien

template
<!-- in pre-compiled templates -->
<TodoItem />

<!-- in in-DOM templates -->
<todo-item></todo-item>

Usa definiciones de prop detalladas

En el código comprometido, las definiciones de prop siempre deben ser lo más detalladas posible, especificando al menos el tipo (o tipos).

Explicación Detallada

Las definiciones de prop detalladas tienen dos ventajas:

  • Documentan la API del componente, de modo que es fácil ver cómo se debe utilizar el componente.
  • En desarrollo, Vue te advertirá si a un componente se le proporcionan props con formato incorrecto, ayudándote a detectar posibles fuentes de error.

Mal

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

Bien

js
props: {
  status: String
}
js
// Even better!
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}

Mal

js
// This is only OK when prototyping
const props = defineProps(['status'])

Bien

js
const props = defineProps({
  status: String
})
js
// Even better!

const props = defineProps({
  status: {
    type: String,
    required: true,

    validator: (value) => {
      return ['syncing', 'synced', 'version-conflict', 'error'].includes(
        value
      )
    }
  }
})

Usa v-for con key

La key con v-for es siempre requerida en los componentes, para mantener el estado interno del componente en el subárbol. Sin embargo, incluso para los elementos, es una buena práctica mantener un comportamiento predecible, como la constancia de objetos en las animaciones.

Explicación Detallada

Supongamos que tienes una lista de tareas (todos):

js
data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Learn to use v-for'
      },
      {
        id: 2,
        text: 'Learn to use key'
      }
    ]
  }
}
js
const todos = ref([
  {
    id: 1,
    text: 'Learn to use v-for'
  },
  {
    id: 2,
    text: 'Learn to use key'
  }
])

Luego las ordenas alfabéticamente. Al actualizar el DOM, Vue optimizará la renderización para realizar las mutaciones del DOM más económicas posibles. Eso podría significar eliminar el primer elemento de la tarea, y luego agregarlo de nuevo al final de la lista.

El problema es que hay casos en los que es importante no eliminar elementos que permanecerán en el DOM. Por ejemplo, es posible que desees usar <transition-group> para animar la clasificación de la lista, o mantener el foco si el elemento renderizado es un <input>. En estos casos, añadir una key única para cada elemento (por ejemplo, :key="todo.id") le dirá a Vue cómo comportarse de manera más predecible.

Según nuestra experiencia, es mejor siempre añadir una key única, para que tú y tu equipo simplemente nunca tengan que preocuparse por estos casos extremos. Luego, en los escenarios raros y críticos para el rendimiento donde la constancia de objetos no es necesaria, puedes hacer una excepción consciente.

Mal

template
<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>

Bien

template
<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

Evita v-if con v-for

Nunca uses v-if en el mismo elemento que v-for.

Hay dos casos comunes en los que esto puede ser tentador:

  • Para filtrar ítems en una lista (por ejemplo, v-for="user in users" v-if="user.isActive"). En estos casos, reemplaza users con una nueva propiedad computed que devuelva tu lista filtrada (por ejemplo, activeUsers).

  • Para evitar renderizar una lista si debe estar oculta (por ejemplo, v-for="user in users" v-if="shouldShowUsers"). En estos casos, mueve el v-if a un elemento contenedor (por ejemplo, ul, ol).

Explicación Detallada

Cuando Vue procesa las directivas, v-if tiene una prioridad más alta que v-for, de modo que este template:

template
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Lanzará un error, porque la directiva v-if se evaluará primero y la variable de iteración user no existe en este momento.

Esto podría solucionarse iterando sobre una propiedad computed en su lugar, así:

js
computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
js
const activeUsers = computed(() => {
  return users.filter((user) => user.isActive)
})
template
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Alternativamente, podemos usar una etiqueta <template> con v-for para envolver el elemento <li>:

template
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

Mal

template
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

Bien

template
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
template
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>

Usa estilos con ámbito de componente

Para las aplicaciones, los estilos en un componente App de nivel superior y en los componentes de diseño pueden ser globales, pero todos los demás componentes siempre deben tener un ámbito.

Esto solo es relevante para los Single-File Components. No requiere que se use el scoped attribute. El ámbito podría ser a través de CSS modules, una estrategia basada en clases como BEM, u otra librería/convención.

Las librerías de componentes, sin embargo, deberían preferir una estrategia basada en clases en lugar de usar el scoped attribute.

Esto facilita la anulación de estilos internos, con nombres de clases legibles por humanos que no tienen una especificidad demasiado alta, pero que aún es muy poco probable que resulten en un conflicto.

Explicación Detallada

Si estás desarrollando un proyecto grande, trabajando con otros desarrolladores, o a veces incluyes HTML/CSS de terceros (por ejemplo, de Auth0), un ámbito consistente asegurará que tus estilos solo se apliquen a los componentes para los que están destinados.

Más allá del scoped attribute, usar nombres de clase únicos puede ayudar a asegurar que el CSS de terceros no se aplique a tu propio HTML. Por ejemplo, muchos proyectos usan los nombres de clase button, btn o icon, por lo que incluso si no se utiliza una estrategia como BEM, añadir un prefijo específico de la aplicación y/o del componente (por ejemplo, ButtonClose-icon) puede proporcionar cierta protección.

Mal

template
<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>

Bien

template
<template>
  <button class="button button-close">×</button>
</template>

<!-- Using the `scoped` attribute -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
template
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- Using CSS modules -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
template
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- Using the BEM convention -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
Reglas de Prioridad A: Esenciales