005.列表渲染

列表渲染

v-for指令

用于根据数组对象的属性值来{循环渲染}元素组件。

  • 语法

    1
    v-for="(item, [key,] index) in/of datalist"
    • 🍬解析
      1. datalist
        • 源数据。
      2. item
        • 迭代的别名 ➾ 源数据中,当前的一条数据。
      3. key
        • 迭代的键名。
        • ❗注意
          • 当datalist为数组时,没有迭代的键名。
      4. index
        • 迭代的索引值 ➾ 从0开始。

1.v-for 迭代数组

  1. 提供第一个的参数为 ➾ 迭代的别名 ➾ 源数据中,当前的一个元素。

    • 🔥案例

      1
      2
      3
      4
      5
      6
      7
      <div id="app">
      <ol>
      <li v-for="item in/of datalist">
      {{ item }}
      </li>
      </ol>
      </div>
      1
      2
      3
      4
      5
      6
      new Vue({
      el: '#app',
      data: {
      datalist: ['Runoob', 'Google', 'Google']
      }
      })

      image-20231120090025733

  2. 提供第二个的参数为 ➾ 迭代的索引值。

    • 🔥案例

      1
      2
      3
      4
      5
      6
      7
      <div id="app">
      <ol>
      <li v-for="(item, index) in/of datalist">
      {{ item }}➾ {{ index }}
      </li>
      </ol>
      </div>
      1
      2
      3
      4
      5
      6
      new Vue({
      el: '#app',
      data: {
      datalist: ['Runoob', 'Google', 'Google']
      }
      })

      image-20231120090334419

2.v-for 迭代对象

  1. 提供第一个的参数为 ➾ 迭代的别名 ➾ 源数据中,对象的当前属性的属性值。

    • 🔥案例

      1
      2
      3
      4
      5
      6
      7
      <div id="app">
      <ol>
      <li v-for="item in/of obj">
      {{ item }}
      </li>
      </ol>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      new Vue({
      el: '#app',
      data: {
      obj: {
      name: '菜鸟教程',
      url: 'http://www.runoob.com',
      slogan: '学的不仅是技术,更是梦想!'
      }
      }
      })

      image-20231120091149531

  2. 提供第二个的参数为 ➾ 源数据中,迭代的键名。

    • 🔥案例

      1
      2
      3
      4
      5
      6
      7
      <div id="app">
      <ol>
      <li v-for="(item, key) in/of obj">
      {{ key }}: {{ item }}
      </li>
      </ol>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      new Vue({
      el: '#app',
      data: {
      obj: {
      name: '菜鸟教程',
      url: 'http://www.runoob.com',
      slogan: '学的不仅是技术,更是梦想!'
      }
      }
      })

      image-20231120091749815

  3. 提供第三个的参数为 ➾ 源数据中,迭代的索引值。

    • 🔥案例

      1
      2
      3
      4
      5
      6
      7
      <div id="app">
      <ol>
      <li v-for="(item, key, index) in/of datalist">
      {{ key }}: {{ item }}➾ {{ index }}
      </li>
      </ol>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      new Vue({
      el: '#app',
      data: {
      obj: {
      name: '菜鸟教程',
      url: 'http://www.runoob.com',
      slogan: '学的不仅是技术,更是梦想!'
      }
      }
      })

      image-20231120091842257

3.v-for 迭代对象数组

1
2
3
4
5
6
7
<div id="app">
<ol>
<li v-for="(item, index) in/of datalist">
{{ item.name }}➾ {{index}}
</li>
</ol>
</div>
1
2
3
4
5
6
7
8
9
10
new Vue({
el: '#app',
data: {
datalist: [
{ name: 'Runoob' },
{ name: 'Google' },
{ name: 'Google' }
]
}
})

image-20231120092136852

4.v-for 迭代整数

1
2
3
4
5
6
7
8
<div id="app">
<ul>
<li v-for="n in/of 10">
{{ n }}
</li>
</ul>
</div>

  • 🔔提示
    • n将从1开始 ➾ 1~10
1
2
3
new Vue({
el: '#app'
})

image-20231120092403400

5.v-for key 属性

当 Vue 正在更新使用v-for指令渲染的元素列表时,它默认使用“就地更新”的策略。

  • ⏳详述

    • 如果数据项的顺序被改变,

      • 在Vue中,

        • 不会移动DOM节点来匹配数据项的顺序,

        • 而是会对当前DOM节点的元素下一个DOM节点的元素进行比较

        • 若发现元素一样,就会复用原来的位置,

          • 进而将新的节点覆盖到这个位置上 ➾ “就地更新”的策略
  • 🔥案例

    • key属性的情况。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      <div id="box">
      <ul>
      <li v-for="(item, index) in lists">
      <input type="checkbox" :value="item.text" />
      {{item.text}}
      </li>
      <button @click="remove()">删除</button>
      </ul>
      </div>

      <script>
      var vm = new Vue({
      el: '#box',
      data: {
      lists: [
      { text: '张' },
      { text: '吕' },
      { text: '王' }
      ]
      },
      methods: {
      remove() {
      // shift()
      // 用于把数组的第一个元素从其中删除,并返回第一个元素的值。
      this.lists.shift()
      }
      }
      })
      </script>
      • 🎭图示①

        • 初始时,第一个节点{张前面的}的复选框是选中的,

          • 点击删除后,复选框还是选中第一个节点。

          image-20231121093441185

        • 👁️‍🗨️分析

          • 选中第一个节点{张前面的}的复选框并点击删除,
            • 在Vue中,点击删除后新的数据是{吕和王},
            • 这时会 ➾ 将第一个节点的元素第二个节点的元素进行比较
              • 发现第二个节点的元素第一个节点的元素一样但值不一样,
                • 复用原来位置的元素,而不会创建元素和删除元素,
                • 只是替换元素的内容{直接将吕赋值给张 ➾ 就地更新}。
      • 🎭图示②

        • 初始时,最后一个节点{王前面的}的复选框是选中的,

          • 点击删除后,没有复选框被选中。

          image-20231121094620506

        • 👁️‍🗨️分析

          • 点击删除后,删除的是第一个数据,但DOM中删除的是最后一个节点。
          • 由于在最后一个节点{王前面的}的复选框是选中的,但被删除掉,
            • 点击删除后,没有复选框被选中。
      • 🪄总结

        • 当选择第一个复选框时,点击删除将删除第一个节点,
        • 在Vue中,
          • 会对当前DOM节点的元素下一个DOM节点的元素进行比较 ➾ 以此类推
          • 若发现元素一样,就会复用原来的位置,
            • 进而将新的节点覆盖到这个位置上,最后导致删除最后一个节点。
✨key 属性的作用

给 Vue 一个提示,以便它能跟踪每个节点的身份,从而{重新使用排序}现有元素,触发 ➾ diff算法

  • 🔔提示

    1. 你需要为每项添加一个具有唯一值的key属性
      • ❗注意
        1. 在用v-for指令时,建议使用v-bind指令动态绑定key属性。
        2. 不要使用非基本类型值{对象、数组…}
          • 作为v-for指令的key属性的值,请用字符串数值类型的值。
        3. key属性的理想值是每项都有的唯一的id ➾ data.id
    2. diff算法
      • 是同级比较,比较当前元素上的key属性值它当前的元素名,
        • 当key属性值元素名都一样时,
          • 只移动的操作,不会重新创建元素和删除元素。
  • 🔥案例

    • key属性的情况。

      1. 用索引{index}做为key值 ➾ 不推荐

        • 如果是静态数据,用索引{index}做key值是没有问题的。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        <div id="box">
        <ul>
        <li v-for="(item, index) in lists" :key="index">
        <input type="checkbox" :value="item.text" />
        {{item.text}}
        <button @click="remove(index)">删除</button>
        </li>
        </ul>
        </div>

        <script>
        var vm = new Vue({
        el: '#box',
        data: {
        lists: [
        { text: '张' },
        { text: '吕' },
        { text: '王' }
        ]
        },
        methods: {
        remove(index) {
        // shift()
        // 用于把数组的第一个元素从其中删除,并返回第一个元素的值。
        this.lists.shift()
        console.log('index:', index)
        }
        }
        })
        </script>

        gif-231121100741

        • 👁️‍🗨️分析
          • 删除前的index是0、1、2,删除后的index是0、1
            • 这时候还会进行复用,
              • 故而不管怎么删除这个索引{index}都是有序的
              • 导致最后key属性并没有起到作用。
      2. 用数据项中的id做为key值 ➾ 推荐

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        <div id="box">
        <ul>
        <li v-for="(item, index) in lists" :key="item.id">
        <input type="checkbox" :value="item.text" />
        {{item.text}}
        </li>
        <button @click="remove(index)">删除</button>
        </ul>
        </div>

        <script>
        var vm = new Vue({
        el: '#box',
        data: {
        lists: [
        { id: 1, text: '张' },
        { id: 2, text: '吕' },
        { id: 3, text: '王' }
        ]
        },
        methods: {
        remove(index) {
        // shift()
        // 用于把数组的第一个元素从其中删除,并返回第一个元素的值。
        this.lists.shift()
        console.log('index:', index)
        }
        }
        })
        </script>
        1. 🎭图示①

          • 选中第一个节点的复选框,点击删除。

            image-20231121093441185

          • 👁️‍🗨️分析

            • 删除前的key是1、2、3,删除后的key就是2、3
              • 通过diff算法就会知道 ➾ 1是需要删除的,2、3是需要复用的,
                • 从而就会根据key属性找的对应节点做移动,
                • 最后删除没有找到key属性对应的属性值的元素。
        2. 🎭图示②

          • 选中最后一个节点的复选框,点击删除。

            image-20231121101848772

          • 👁️‍🗨️分析

            • 删除前的key是1、2、3,删除后的key就是2、3
              • 通过diff算法就会知道 ➾ 1是需要删除的,2、3是需要复用的,
                • 从而就会根据key属性找的对应节点做移动,
                • 最后删除没有找到key属性对应的属性值的元素 ➾ 
                  • 最后的复选框仍然被选中。

6.数组更新检测

1/变更方法

顾名思义,会变更调用如下方法的原始数组 ➾ 触发视图更新

  • 💫示例

    1. pop()
    2. push()
    3. sort()
    4. shift()
    5. splice()
    6. unshift()
    7. reverse()
  • 🔥案例

    gif-231122090522

2/替换数组

相比之下,也有非变更方法,它们不会变更原始数组,而是返回一个新数组,

  • 当使用非变更方法时,可以用新数组替换旧数组来 ➾ 触发视图更新

  • 💫示例

    1. map()
    2. slice()
    3. concat()
    4. filter()
  • 🔥案例

    1. 不触发视图更新

      gif-231122090640

    2. 触发视图更新

      gif-231122090750

3/不能检测如下变动的数组
1
vm.datalist[index] = newValue
  • 🔥案例

    gif-231122091129
  • 🛠️解决方法

    1. 第一种

      1
      Vue.set(vm.datalist, index, newValue)
      • 🔔提示

        • 如上方法可以指定从何处添加/删除元素。
      • 🔥案例

        gif-231122091840

    2. 第二种

      1
      vm.datalist.splice(index, howmany, item1, item2, ... , itemX)
      • 🍬解析

        1. index
          • 必选,规定从何处添加/删除元素。
        2. howmany
          • 可选,规定应该删除多少元素。
        3. item1, item2, ... , itemX
          • 可选,要添加到数组的新元素。
      • 🔥案例

        gif-231122091620

    3. Vue3

      1
      vm.datalist[index] = newValue
      • 🔥案例

        1
        2
        3
        4
        5
        6
        7
        8
        9
        var vm = Vue.createApp({
        data() {
        return {
        data: {
        datalist: ["①", "②", "③"],
        }
        }
        }
        }).mount("#box")

        gif-231122092553

🔥案例➾ 模糊查询
  1. 添加新数组originList

    • ✨作用
      • 为了使得原数组数据不改变。
    1
    2
    3
    4
    5
    6
    7
    8
    <div id="box">
    <input type="text" @input="handleInput" v-model="mytext" />
    <ul>
    <li v-for="data in datalist" :key="data">
    {{data}}
    </li>
    </ul>
    </div>
    • 🔔提示
      • input和change事件区别
        1. input只要文本框的值{value}改变就触发。
        2. change需要文本框的值{value}改变并且失去焦点才触发。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var vm = new Vue({
    el: "#box",
    data: {
    mytext: "",
    datalist: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"],
    originList: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"]
    },
    methods: {
    handleInput() {
    setTimeout(() => {
    this.datalist = this.originList.filter(item => item.includes(this.mytext))
    }, 1000)
    }
    }
    })

    gif-231123090309

    • 🔔提示

      1. filter()

        • 过滤出符合条件(条件为真)的数据。
      2. includes()

        • 若数据在数组中,返回true。反之,返回false。

        • 🔥案例

          1
          2
          3
          4
          5
          6
          7
          var arr = ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"]

          var newlist = arr.filter(item => item.includes("a"))

          console.log('newlist:', newlist)

          console.log('arr:', arr)
          image-20231123085930416
  2. 函数表达式 ➾ v-for="data in test()"

    1
    2
    3
    4
    5
    6
    7
    8
    <div id="box">
    <input type="text" v-model="mytext" />
    <ul>
    <li v-for="data in test()" :key="data">
    {{data}}
    </li>
    </ul>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var vm = new Vue({
    el: "#box",
    data: {
    mytext: "",
    datalist: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"]
    },
    methods: {
    test() {
    return this.datalist.filter(item => item.includes(this.mytext))
    }
    }
    })

    gif-231123091931

v-for 与 v-if 一同使用

当v-for 与 v-if处于同一节点,v-for 的优先级比 v-if 更高,

  • 这意味着 v-if 分别重复运行于每个 v-for 循环中。

  • ❗注意

    • 不推荐在同一元素上,使用 v-if v-for。
  • 🔥案例

    1. 当你只想渲染{部分节点}时,这种优先级的机制会十分有用。

      1
      2
      3
      4
      5
      6
      7
      <div id="box">
      <ul>
      <li v-for="todo in todos" v-if="!todo.isComplete">
      {{ todo.name }}
      </li>
      </ul>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      new Vue({
      el: '#box',
      data: {
      todos: [
      {
      name: '小糖',
      age: 18,
      isComplete: true
      },
      {
      name: '小白',
      age: 20,
      isComplete: false
      },
      {
      name: '小黑',
      age: 22,
      isComplete: false
      }
      ]
      }
      })

      image-20231124092847656

      • 🔔提示
        • 上面的代码将只渲染isComplete为false的todo
    2. 如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素template元素上。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <div id="box">
      <ul v-if="todos.length">
      <li v-for="todo in todos" >
      {{ todo.name }}
      </li>
      </ul>
      </div>

      <p v-else>No todos left!</p>