Skip to content

Renderizado de Listas

v-for

Podemos utilizar la directiva v-for para representar una lista de elementos basada en un array. La directiva v-for requiere una sintaxis especial en forma de item in items, donde items es el array de datos fuente y item es un alias para el elemento del array sobre el que se está iterando:

js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
js
data() {
  return {
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
template
<li v-for="item in items">
  {{ item.message }}
</li>

Dentro del ámbito v-for, las expresiones de la plantilla tienen acceso a todas las propiedades del ámbito padre. Además, v-for también admite un segundo alias opcional para el índice del elemento actual:

js
const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
js
data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}
template
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
  • Parent - 0 - Foo
  • Parent - 1 - Bar
  • El ámbito de las variables de v-for es similar al siguiente JavaScript:

    js
    const parentMessage = 'Parent'
    const items = [
      /* ... */
    ]
    
    items.forEach((item, index) => {
      // tiene acceso al ámbito externo `parentMessage`
      // pero `item` e `index` sólo están disponibles aquí
      console.log(parentMessage, item.message, index)
    })

    Observa en que el valor v-for coincide con la firma de la función del callback forEach. De hecho, puedes utilizar la desestructuración en el alias del elemento v-for de forma similar a la desestructuración de los argumentos de la función:

    template
    <li v-for="{ message } in items">
      {{ message }}
    </li>
    
    <!-- con alias de índice -->
    <li v-for="({ message }, index) in items">
      {{ message }} {{ index }}
    </li>

    En el caso de los v-for anidados, el ámbito también funciona de forma similar a las funciones anidadas. Cada ámbito v-for tiene acceso a los ámbitos padre:

    template
    <li v-for="item in items">
      <span v-for="childItem in item.children">
        {{ item.message }} {{ childItem }}
      </span>
    </li>

    También puedes utilizar of como delimitador en lugar de in, para que se acerque más a la sintaxis de JavaScript para los iteradores:

    template
    <div v-for="item of items"></div>

    v-for con un Objeto

    También puedes utilizar v-for para iterar a través de las propiedades de un objeto. El orden de iteración se basará en el resultado de llamar a Object.keys() sobre el objeto:

    js
    const myObject = reactive({
      title: 'Cómo hacer listas en Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    })
    js
    data() {
      return {
        myObject: {
          title: 'Cómo hacer listas en Vue',
          author: 'Jane Doe',
          publishedAt: '2016-04-10'
        }
      }
    }
    template
    <ul>
      <li v-for="value in myObject">
        {{ value }}
      </li>
    </ul>

    También puedes proporcionar un segundo alias para el nombre de la propiedad (también conocido como key o clave):

    template
    <li v-for="(value, key) in myObject">
      {{ key }}: {{ value }}
    </li>

    Y otra para el índice:

    template
    <li v-for="(value, key, index) in myObject">
      {{ index }}. {{ key }}: {{ value }}
    </li>

    v-for con un Rango

    v-for también puede tomar un entero. En este caso se repetirá la plantilla tantas veces, basado en un rango de 1...n.

    template
    <span v-for="n in 10">{{ n }}</span>

    Observa que aquí n comienza con un valor inicial de 1 en lugar de 0.

    v-for en <template>

    De forma similar a la etiqueta v-if, también puedes utilizar una etiqueta <template> con v-for para representar un bloque de múltiples elementos. Por ejemplo:

    template
    <ul>
      <template v-for="item in items">
        <li>{{ item.msg }}</li>
        <li class="divider" role="presentation"></li>
      </template>
    </ul>

    v-for con v-if

    Nota

    No es recomendado usar v-if y v-for en el mismo elemento debido a la precedencia implícita. Consulta la guía de estilo para más detalles.

    Cuando existen en el mismo nodo, v-if tiene mayor prioridad que v-for. Esto significa que la condición v-if no tendrá acceso a las variables del ámbito de v-for:

    template
    <!--
    Esto arrojará un error porque la propiedad "todo"
    no está definida en la instancia.
    -->
    <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo.name }}
    </li>

    Esto puede arreglarse moviendo v-for a una etiqueta envolvente <template> (que también es más explícita):

    template
    <template v-for="todo in todos">
      <li v-if="!todo.isComplete">
        {{ todo.name }}
      </li>
    </template>

    Manteniendo el Estado con key

    Cuando Vue está actualizando una lista de elementos renderizados con v-for, por defecto utiliza una estrategia de "parche en el lugar". Si el orden de los elementos de datos ha cambiado, en lugar de mover los elementos del DOM para que coincidan con el orden de los elementos, Vue parchará cada elemento en su lugar y se asegurará de que refleje lo que debe ser renderizado en ese índice en particular.

    Este modo por defecto es eficiente, pero sólo es adecuado cuando la salida de la lista no depende del estado de los componentes hijos o del estado temporal del DOM (por ejemplo, los valores de entrada del formulario).

    Para darle a Vue una pista para que pueda rastrear la identidad de cada nodo, y así reutilizar y reordenar los elementos existentes, necesitas proporcionar un atributo key único para cada elemento:

    template
    <div v-for="item in items" :key="item.id">
      <!-- contenido -->
    </div>

    Cuando se usa <template v-for>, la key debe colocarse en el contenedor <template>:

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

    Nota

    key aquí es un atributo especial que se vincula con v-bind. No debe confundirse con la variable clave de la propiedad cuando se utiliza v-for con un objeto.

    Se recomienda proporcionar un atributo key con v-for siempre que sea posible, a menos que el contenido del DOM iterado sea simple (es decir, que no contenga componentes o elementos del DOM con estado), o que se confíe intencionadamente en el comportamiento por defecto para mejorar el rendimiento.

    El enlace key espera valores primitivos, es decir, cadenas y números. No utilice objetos como claves v-for. Para un uso detallado del atributo key, por favor consulta la documentación de la API key.

    v-for con un Componente

    Esta sección supone el conocimiento de Componentes. Siéntete libre de saltarla y volver más tarde.

    Puedes usar directamente v-for en un componente, como cualquier elemento normal (no olvides proporcionar una clave):

    template
    <MyComponent v-for="item in items" :key="item.id" />

    Sin embargo, esto no pasará automáticamente ningún dato al componente, porque los componentes tienen ámbitos aislados propios. Para pasar los datos iterados al componente, debemos usar también props:

    template
    <MyComponent
      v-for="(item, index) in items"
      :item="item"
      :index=" index"
      :key="item.id"
    />

    La razón por la que no se inyecta automáticamente item en el componente es porque eso hace que el componente esté estrechamente acoplado a cómo funciona v-for. Ser explícito sobre la procedencia de sus datos hace que el componente sea reutilizable en otras situaciones.

    Mira este ejemplo de una simple lista de tareas para ver cómo renderizar una lista de componentes usando v-for, pasando diferentes datos a cada instancia.

    Mira este ejemplo de una simple lista de tareas para ver cómo renderizar una lista de componentes usando v-for, pasando diferentes datos a cada instancia.

    Detección de Cambios en Arrays

    Métodos de Mutación

    Vue envuelve los métodos de mutación de un array observado para que también activen las actualizaciones de la vista. Los métodos envueltos son:

    • push()
    • pop()
    • shift()
    • unshift()
    • splice()
    • sort()
    • reverse()

    Reemplazo de un Array

    Los métodos de mutación, como su nombre indica, mutan el array original sobre el que son llamados. En comparación, también hay métodos que no mutan, como filter(), concat() y slice(), que no mutan el array original sino que siempre devuelven un nuevo array. Cuando se trabaja con métodos que no mutan, debemos sustituir el array antiguo por el nuevo:

    js
    // `item` es una ref con valor de array
    items.value = items.value.filter((item) => item.message.match(/Foo/))
    js
    this.items = this.items.filter((item) => item.message.match(/Foo/))

    Podrías pensar que esto hará que Vue tire el DOM existente y vuelva a renderizar toda la lista; afortunadamente, no es el caso. Vue implementa algunas heurísticas inteligentes para maximizar la reutilización de elementos del DOM, por lo que reemplazar un array con otro array que contenga objetos superpuestos es una operación muy eficiente.

    Visualización de Resultados Filtrados/Ordenados

    A veces queremos mostrar una versión filtrada u ordenada de un array sin mutar o reiniciar los datos originales. En este caso, puedes crear una propiedad computada que devuelva el array filtrado u ordenado.

    Por ejemplo:

    js
    const numbers = ref([1, 2, 3, 4, 5])
    
    const evenNumbers = computed(() => {
      return numbers.value.filter((n) => n % 2 === 0)
    })
    js
    data() {
      return {
        numbers: [1, 2, 3, 4, 5]
      }
    },
    computed: {
      evenNumbers() {
        return this.numbers.filter(n => n % 2 === 0)
      }
    }
    template
    <li v-for="n in evenNumbers">{{ n }}</li>

    En situaciones en las que las propiedades computadas no son factibles (por ejemplo, dentro de bucles v-for anidados), puede utilizar un método:

    js
    const sets = ref([
      [1, 2, 3, 4, 5],
      [6, 7, 8, 9, 10]
    ])
    
    function even(numbers) {
      return numbers.filter((number) => number % 2 === 0)
    }
    js
    data() {
      return {
        sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
      }
    },
    methods: {
      even(numbers) {
        return numbers.filter(number => number % 2 === 0)
      }
    }
    template
    <ul v-for="numbers in sets">
      <li v-for="n in even(numbers)">{{ n }}</li>
    </ul>

    ¡Ten cuidado con reverse() y sort() en una propiedad computada! Estos dos métodos mutarán el array original, lo que debería evitarse en los getters computados. Crea una copia del array original antes de llamar a estos métodos:

    diff
    - return numbers.reverse()
    + return [...numbers].reverse()
    Renderizado de Listas has loaded