Luna Tech

Tutorials For Dummies.

Vue: List Rendering

2022-03-20


0. Intro

Reference

The v-for directive requires a special syntax in the form of item in items, where items is the source data array and item is an alias for the array element being iterated on.

Note: item can be called anything.

我们也可以用 of 来代替 in,如 example 2,看自己怎么写的习惯。

Example 1

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

Example 2

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

1. v-for scope

Inside the v-for scope, template expressions have access to all parent scope properties. In addition, v-for also supports an optional second alias for the index of the current item.

The variable scoping of v-for is similar to the following JavaScript.

const parentMessage = "Parent";
const items = [
  /* ... */
];

items.forEach((item, index) => {
  // has access to outer scope `parentMessage`
  // but `item` and `index` are only available in here
  console.log(parentMessage, item.message, index);
});

因为 v-for 其实就是 forEach 的 function callback signature,所以我们可以 access item 和 index。

同理,我们还可以用 destructive arguments:

<li v-for="{ message } in items">{{ message }}</li>

<!-- with index alias -->
<li v-for="({ message }, index) in items">{{ message }} {{ index }}</li>

Example

比如 v-for 可以 access parent message,还可以 access index。

<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
data() {
  return {
    parentMessage: 'Parent',
    items: [{ message: 'Foo' }, { message: 'Bar' }]
  }
}

2. Nested v-for scope

每个 v-for 都可以 access 自己的 parent scope

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

3. v-for with an Object

除了 iterate array 之外,v-for 也可以 iterate object keys,但是顺序不能保证。

When iterating over an object, the order is based on the enumeration order of Object.keys(), which isn’t guaranteed to be consistent across JavaScript engine implementations.

Example 1

<ul>
  <li v-for="value in myObject">{{ value }}</li>
</ul>
data() {
  return {
    myObject: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
}

Example 2

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

Example 3

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

4. v-for with a range

n 从 1 开始,也就是 1…n 这个范围来 iterate。

Example

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

5. v-for on <template>

v-if 类似,假如有 multiple elements,可以用 v-for on template 来 render block。

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

6. v-if with v-for

不推荐在同一个 element 上同时使用,因为可能会有 implicit precedene, v-if 比 v-for 优先级要高。

ref: https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for

Example

<!--
This will throw an error because property "todo"
is not defined on instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">{{ todo.name }}</li>

Fix

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

7. Maintaining State with key

Default strategy - efficient but may not suitable for use case

When Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.

This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).

To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item.

Recommendation - define key whenever possible

Note:

Example 1

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

Example 2

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

8. v-for with a Component

Example

这样写不能 pass data to the component.

The reason for not automatically injecting item into the component is because that makes the component tightly coupled to how v-for works. Being explicit about where its data comes from makes the component reusable in other situations.

<my-component v-for="item in items" :key="item.id"></my-component>

Fix

我们要用 prop 来 pass data.

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

9. Array Change Detection

任何 mutation methods(改变 original array)都会 trigger view update:

还有一些 non-mutating methods,虽然不改变 original array,但是会 return a new array,我们要 replace old array with new array:

this.items = this.items.filter((item) => item.message.match(/Foo/));

When working with non-mutating methods, we should replace the old array with the new one. In Vue, replacing an array with another array containing overlapping objects is a very efficient operation.