Vue特定模板语法实战(三)—— 列表渲染

此学习教程是对官方教程的解析,

本章节主要涉及到的官方教程地址:

条件渲染

上一章 :Vue入门实战教程(三)—— 视图层:模板及指令

列表渲染方式

数组渲染

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
在 v-for 块中,我们可以访问所有父作用域的 property。v-for 还支持一个可选的第二个参数,即当前项的索引。
第一个的参数:item 被迭代的数组元素
第二个的参数:index 作为索引


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  li>
ul>

<script>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
script>
body>
html>

运行结果:
Vue特定模板语法实战(三)—— 列表渲染_第1张图片

数组更新检测

变更方法

变更方法:会变更调用了这些方法的原始数组。
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()

非变更方法:替换数组

非变更方法:不会变更原始数组,而总是返回一个新数组。
例如 filter()、concat() 和 slice()

当使用非变更方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})

对象渲染

你也可以用 v-for 来遍历一个对象的 property。
第一个的参数:value 对象的 property值
第二个的参数:name 对象的 property 名称 (也就是键名)
第三个的参数:index 作为索引


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<ul id="v-for-object">
  <li v-for="(value, name, index)  in object">
    {{ index }}. {{ name }}: {{ value }}
  li>
ul>

<script>
var example2 = new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})
script>
body>
html>

运行结果:
Vue特定模板语法实战(三)—— 列表渲染_第2张图片

整数渲染

v-for 也可以接受整数。在这种情况下,它会把模板重复对应次数。
第一个的参数:item 第几次重复,以1开始
第二个的参数:index 作为索引,以0开始


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<ul id="example">
  <li v-for="(item,index) in 10">{{index}}.{{item}}li>
ul>
<script>
var example2 = new Vue({
  el: '#example'
})
script>
body>
html>

运行结果:
Vue特定模板语法实战(三)—— 列表渲染_第3张图片

key: 跟踪每个节点的身份,从而重用和重新排序现有元素

列表数据更新后的渲染模式

默认的高效模式

如果数据项的顺序被改变,Vue 默认将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。但只适用于 不 依 赖 子 组 件 状 态 或 临 时 D O M 状 态 ( 例 如 : 表 单 输 入 值 ) 的 列 表 渲 染 输 出 。 \color{red}{不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。} DOM()

特点:

  1. 高效:因为没有key区分是否独立, 会复用相同标签的子组件, 所以只更新元素而不是重新渲染
  2. 不绑定子组件状态:同样的,因为没有key区分是否是独立的, 所以子组件状态和具体某个子组件并不绑定。这意味着数据变化视图更新时,具体某个子组件的状态不会跟着这个组件。这往往造成BUG。

使用key模式: 为每项提供一个唯一 key attribute

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素。

特点:

  1. 并不高效:每个子组件都有唯一key区分, 所以复用不高,而且有维护唯一key与具体子组件关联的开销。
  2. 绑定子组件状态:因为绑定了,所以数据变化视图更新时,具体某个子组件的状态会跟着这个组件。

选择哪种模式

基于两个模式完全相反的特点,所以官方建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

两种模式效率对比

例子(使用和不使用v-bind:key来比较):


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<ul id="example">
  <li v-for="(item, index) in items">
     {{ index }} - id[{{ item.id }}] - message[{{ item.message }}]
  li>
ul>
<script>
var max_num = 10000;
var items = [];
for(var i=0; i<max_num; i++){
	var im = {};
	im["id"] = ""+i;
	im["message"] = "Foo";
	items.push(im);
}

var beforeUpdateTime = null;
var updatedTime = null;
var vm = new Vue({
  el: '#example',
  data: {
    items: items
  },
  beforeUpdate: function () {
    beforeUpdateTime = new Date().getTime();
	console.log("数据更新前时间:"+beforeUpdateTime);
  },
  updated: function () {
	updatedTime  = new Date().getTime();
    console.log("数据更新后渲染完成时间:"+updatedTime);
	console.log("耗时:"+(updatedTime-beforeUpdateTime)+"ms");
  }
})
script>
body>
html>

不使用key:

Vue特定模板语法实战(三)—— 列表渲染_第4张图片
使用key:
Vue特定模板语法实战(三)—— 列表渲染_第5张图片
可以看到不使用key的默认模式高效的多。

两种模式子组件状态绑定的区别

例子(使用和不使用v-bind:key来比较):


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<div id="app">
    <div>
      <input type="text" v-model="name">
      <button @click="add">添加button>
    div>
    <ul>
      <li v-for="(item, i) in list">
        <input type="checkbox"> {{item.name}}
      li>
    ul>
  div>
<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        name: '',
        newId: 3,
        list: [
          { id: 1, name: 'a' },
          { id: 2, name: 'b' },
          { id: 3, name: 'c' }
        ]
      },
      methods: {
        add() {
         //注意这里是unshift
          this.list.unshift({ id: ++this.newId, name: this.name })
          this.name = ''
        }
      }
    });
script>
body>
html>

不使用key:
先选中选项b后,在列表数据头部添加选项d, 视图更新后, 选项b的选中状态变到了a上
Vue特定模板语法实战(三)—— 列表渲染_第6张图片
使用key:
先选中选项b后,在列表数据头部添加选项d, 视图更新后, 选项b仍然维持选中状态
Vue特定模板语法实战(三)—— 列表渲染_第7张图片
可以看到没有key不绑定子组件状态, 而有key绑定子组件状态。

在自定义组件上使用 v-for

两个约束:

  1. 当在组件上使用 v-for 时,key 是必须的

  2. 任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用v-bind指令和组件的props进行绑定:

例子:


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
head>

<body>
<ul id="app">
    <my-component
      v-for="(im, i) in items"
      v-bind:item="im"
      v-bind:index="i"
      v-bind:key="im.id"
    >my-component>
ul>

<script>
Vue.component('my-component', {
  template: '
  • {{ index }} - id[{{ item.id }}] - message[{{ item.message }}]
  • '
    , props: ['item', 'index'] }) var vm = new Vue({ el: '#app', data: { items: [ { id: 1, message: 'Foo1' }, { id: 2, message: 'Foo2' }, { id: 3, message: 'Foo3' } ] } });
    script> body> html>

    运行结果:
    Vue特定模板语法实战(三)—— 列表渲染_第8张图片

    v-for 与 v-if 一同使用

    处于同一节点,v-for 的优先级比 v-if 更高,即:
    先 执 行 v − f o r 循 环 , 再 执 行 v − i f 判 断 \color{red}{先执行v-for循环, 再执行v-if判断} vfor,vif

    如果想先判断再循环,将 v-if 置于v-for所在元素的外层元素 (或