Vue.js列表渲染&关于列表元素的key&列表过滤与排序

饮水思源:https://www.bilibili.com/video/BV1Zy4y1K7SH?p=30&spm_id_from=pageDriver

一、关于key

反例

一个反例,用index作为key出bug

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title></title>
  <script src="JS/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>{{header}}</h1>
    <ul>
      <!-- 
        key相当于人类的身份证,保证每条数据的key不同就行 。
        1 key只存在于虚拟DOM中
        2 由于虚拟DOM对比算法,采用index作为key时,如果在开头
          或者中间等地方插入,或者其它一些破坏顺序的操作,可能导致,例如
          key=1对应数据实质上已经替换了(不是同一个人了),言下之意,
          1这个身份证号对应的人突然换了一个,虚拟DOM实际上根据key=1去对
          比两个不同的人来对页面进行更新(错位对比),那么不仅可能造成效率问题,
          也可能导致一些出乎意料的其它问题。结论,尽量不用index做id
      -->

      <li v-for="(article, index) in articles" :key="index">
        {{article.title}} --- index = {{index}}
        <input type="text">
      </li>
    </ul>

    <button id="btnAdd">添加一篇文章</button>
  </div>

  <script>
    const articles = [
      {
        id: 1,
        title: '这个是文章1'
      },
      {
        id: 2,
        title: '这个是文章2'
      },
      {
        id: 3,
        title: '这个是文章3'
      },
    ]

    const vm = new Vue({
      el: "#root",
      data: {
        header: "key 的原理探究 --- 一个出错的例子",
        articles: articles,
      },
    })

    let i = 4;
    document.querySelector("#btnAdd").addEventListener("click", () => {
      articles.unshift({
        id: i,
        title: `这个是文章${i++}`,
      })
    })
  </script>
</body>

正例

修改一下,一个正确的例子,用唯一的id作key:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title></title>
  <script src="JS/vue.js"></script>
</head>

<body>
  <div id="root">
    <h1>{{header}}</h1>
    <ul>
      <li v-for="(article, index) in articles" :key="article.id">
        {{article.title}} --- index = {{index}}
        <input type="text">
      </li>
    </ul>

    <button id="btnAdd">添加一篇文章</button>
  </div>

  <script>
    const articles = [
      {
        id: 1,
        title: '这个是文章1'
      },
      {
        id: 2,
        title: '这个是文章2'
      },
      {
        id: 3,
        title: '这个是文章3'
      },
    ]

    const vm = new Vue({
      el: "#root",
      data: {
        header: "key 的原理探究 --- 一个正确的例子",
        articles: articles,
      },
    })

    let i = 4;
    document.querySelector("#btnAdd").addEventListener("click", () => {
      articles.unshift({
        id: i,
        title: `这个是文章${i++}`,
      })
    })
  </script>
</body>
</html>
View Code

二、列表过滤(监视器VS.计算属性)

效果:

监视器实现

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title></title>
  <script src="JS/vue.js"></script>
</head>

<body>
  <div id="root">
    <input type="text" placeholder="请输入文章标题" v-model="keyWord">
    <ul v-show="!searchMode">
      <li v-for="(x, index) in allArticles" :key="x.id">
        #{{index}}# 标题:{{x.title}} 创建时间:{{x.create_time}}
      </li>
    </ul>
    <ul v-show="searchMode">
      <li v-for="(x, index) in filArticles" :key="x.id">
        #{{index}}# 标题:{{x.title}} 创建时间:{{x.create_time}}
      </li>      
    </ul>
  </div>

  <script>
    const articles = [
      {
        id: 1,
        title: '文章123',
        create_time: '2022-01-22',
      },
      {
        id: 2,
        title: '文章345',
        create_time: '2022-01-27',
      },
      {
        id: 3,
        title: '文章567',
        create_time: '2022-01-11',
      },
      {
        id: 4,
        title: '文章6789',
        create_time: '2022-01-05',
      },
    ]

    const vm = new Vue({
      el: "#root",
      data: {
        allArticles: articles,
        keyWord: "",
        searchMode: false,
        filArticles: [],
      },
      watch: {
        keyWord(newVal) {
          if (newVal === "") {
            this.searchMode = false;
          } else {
            this.searchMode = true;
            this.filArticles = this.allArticles.filter(x => {
              return x.title.indexOf(newVal) !== -1;
            })
          }
        },
      }
    })
  </script>
</body>
</html>

计算属性实现

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title></title>
  <script src="JS/vue.js"></script>
</head>

<body>
  <div id="root">
    <input type="text" placeholder="请输入文章标题" v-model="keyWord">
    <ul v-show="!searchMode">
      <li v-for="(x, index) in allArticles" :key="x.id">
        #{{index}}# 标题:{{x.title}} 创建时间:{{x.create_time}}
      </li>
    </ul>
    <ul v-show="searchMode">
      <li v-for="(x, index) in filArticles" :key="x.id">
        #{{index}}# 标题:{{x.title}} 创建时间:{{x.create_time}}
      </li>      
    </ul>
  </div>

  <script>
    const articles = [
      {
        id: 1,
        title: '文章123',
        create_time: '2022-01-22',
      },
      {
        id: 2,
        title: '文章345',
        create_time: '2022-01-27',
      },
      {
        id: 3,
        title: '文章567',
        create_time: '2022-01-11',
      },
      {
        id: 4,
        title: '文章6789',
        create_time: '2022-01-05',
      },
    ]

    const vm = new Vue({
      el: "#root",
      data: {
        allArticles: articles,
        keyWord: "",
      },
      computed: {
        searchMode() {
          return this.keyWord !== "";
        },
        filArticles() {
          return this.allArticles.filter(x => {
              return x.title.indexOf(this.keyWord) !== -1;
          })
        }
      }
    })
  </script>
</body>
</html>
计算属性实现.js

三、列表排序

效果:

实现:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title></title>
  <script src="JS/vue.js"></script>
</head>

<body>
  <div id="root">
    <input type="text" placeholder="请输入文章标题" v-model="keyWord">
    <button v-on:click="sortByTime">按时间排序</button>
    <button v-on:click="sortByDefault">原顺序</button>
    <ul>
      <li v-for="(article, index) in filArticles" :key="article.id">
        #{{index}}# {{article.title}} 创建时间{{article.create_time}}
      </li>
    </ul>
  </div>

  <script>
    const allArticles = [
      {
        id: 1,
        title: '章123',
        create_time: '2022-01-22',
      },
      {
        id: 2,
        title: '文章345',
        create_time: '2022-01-27',
      },
      {
        id: 3,
        title: '章567',
        create_time: '2022-01-11',
      },
      {
        id: 4,
        title: '文章6789',
        create_time: '2022-01-05',
      },
      {
        id: 5,
        title: '文章6789',
        create_time: '2022-01-01',
      },
    ]

    const vm = new Vue({
      el: "#root",
      data: {
        keyWord: "",
        sortType: 0, // 0 原顺序 1 按时间排序
      },
      computed: {
        filArticles() {
          let tmpArr = allArticles.filter(article => {
            return article.title.indexOf(this.keyWord) !== -1;
          })

          if (this.sortType) {
            tmpArr.sort((a, b) => {
              return a.create_time > b.create_time ? 1 : -1
            })
          }

          return tmpArr
        }
      },
      methods: {
        sortByTime() {
          this.sortType = 1;
        },
        sortByDefault() {
          this.sortType = 0;
        },
      },
    })
  </script>
</body>
</html>

 

posted @ 2022-01-21 20:04  xkfx  阅读(205)  评论(0编辑  收藏  举报