初学Vue(全家桶)-第6天(vue2):列表渲染、key的作用和原理、列表过滤、列表排序

初学Vue

1、列表渲染

v-for命令和:key属性的使用

1.1 遍历数组类型

示例一:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>遍历数组</title>
</head>
<body>
    <div id="root">
        <ul>
            <li v-for="p in persons" :key="p.id">
                {
   
   {p.id}}-{
   
   {p.name}}-{
   
   {p.age}}
            </li>
        </ul>
        <!-- p in persons中的p是从数组persons中获取到的一个对象,这里相当于迭代器的用法,in再2.6版本后可以改为of -->
        <!-- 通过p.id的形式获取对象里面的值 -->
        <!-- :key="p.id" 中的:key是标识性属性,相当于给这个元素一个身份识别 -->
        <!-- 虽然:key可以省略不写,但原则上不要省略 -->
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                // 准备一个数组,存储三个对象
                persons:[
                    {
      
      id:"001",name:"张三",age:"18"},
                    {
      
      id:"002",name:"李四",age:"20"},
                    {
      
      id:"003",name:"王五",age:"21"},
                ]
            }
        });
    </script>
</body>
</html>

在这里插入图片描述

示例二:index的使用

<body>
    <div id="root">
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {
   
   {p.id}}-{
   
   {p.name}}-{
   
   {p.age}}-{
   
   {index}}
            </li>
        </ul>
        <!-- index的值由0开始自动自增,数组中由多少项就自增多少次,并且是浏览器决定的,就算不写,浏览器也会把这个值当成默认值 -->
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                // 准备一个数组,存储三个对象
                persons:[
                    {
      
      id:"001",name:"张三",age:"18"},
                    {
      
      id:"002",name:"李四",age:"20"},
                    {
      
      id:"003",name:"王五",age:"21"},
                ]
            }
        });
    </script>
</body>

在这里插入图片描述

1.2 遍历对象类型

示例三

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>遍历对象</title>
</head>
<body>
    <div id="root">
        <ul>
            <li v-for="(value,key) in car" :key="key">
                {
   
   {key}}- {
   
   {value}}
            </li>
        </ul>
        <!-- v-for会将对象中的键值对遍历出来,并且写法是先value,再写key -->
        <!-- :key="key" 引号中的key是用来做标识的 -->
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                // 准备单独一个对象,对象中有属性
                car:{
      
      
                    name:"兰博基尼",
                    prices:"1400万",
                    color:"blue"
                }
                
            }
        });

    </script>
</body>
</html>

在这里插入图片描述

1.3 遍历字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>遍历字符串</title>
</head>
<body>
    <div id="root">
        <ul>
            <li v-for="(value,index) in str" :key="index">
                {
   
   {index}}- {
   
   {value}}
                <!-- {
    
    {value}} -->
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                str:"hello"
            }
        });

    </script>
</body>
</html>

在这里插入图片描述

  • 遍历指定次数
<body>
    <div id="root">
        <ul>
            <li v-for="(v,index) in 5" :key="index">
                {
   
   {v}}- {
   
   {index}}
                <!-- v中是从1开始逐渐自增到5的五个数:1,2,3,4,5-->
                <!-- index是从0开始逐渐自增到4 -->
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
        });

    </script>
</body>

在这里插入图片描述

2、key作用与原理

key的作用和原理是面试中可能问到的问题,所以还是比较重要的,建议慢慢研究

2.1 虚拟DOM中key的作用

key是虚拟DOM对象的表示,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行了【新虚拟DOM】与【旧虚拟DOM】的差异比较。

2.2 新虚拟DOM与旧虚拟DOM的比较规则

(1)旧虚拟DOM中如果找到了和新虚拟DOM相同的key:
1、若对比下来,虚拟DOM中内容没变,直接使用之前的真实DOM,(这也是效率高的一个原因)
2、若对比下来,虚拟DOM中内容变了,则会生成新的真实DOM,随后替换掉页面中之前的真实DOM
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key,则直接创建新的真实DOM,随后渲染到页面。

2.3 如何选择key?

  1. 最好使用每条数据的唯一标识作为key,比如id,手机好,身份证,学号等能唯一表示身份的值
  2. 如果不存在对数据的的逆序添加、逆序删除等破坏正常顺序的操作,仅用于渲染列表用于展示,是可以使用index作为key的。
  3. 如果不在模板中添加key属性,那么浏览器就会默认将index作为可以的值

用index作为key可能引发的问题

1、若对数据进行逆序添加、逆序删除等破化顺序的操作:会产生没有必要的真实DOM更新,这时页面效果可能没有问题,但还是会引起效率变低。
2、如果结构中还包含输入类的DOM,例如input输入框,在逆序操作的过程中,因为会产生错误的DOM更新,也就会引起界面出错。

看下面几个示例:

  • 使用index作为key的唯一标识时

示例一:不包含输入框时

<body>
    <div id="root">
        <!-- 这个按钮用于在列表的最前面添加一个数据,并且只添加一次 -->
        <!-- 如果在列表最后面添加,并不会体现index的问题,这里就不演示了 -->
        <button @click.once="add">点我添加一个老刘</button>
        <ul>
            <li v-for="(p,index) in persons" :key="index">
                {
    
    {p.name}}-{
    
    {p.age}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        new Vue({
       
       
            el:"#root",
            data:{
       
       
                persons:[
                    {
       
       id:"001",name:"张三",age:18},
                    {
       
       id:"002",name:"李四",age:19},
                    {
       
       id:"003",name:"王五",age:20},
                ]
            },
            methods:{
       
       
                add(){
       
       
                    const p = {
       
       id:"003",name:"王五",age:20};
                    // 往persons数组最前面中添加数据
                    this.persons.unshift(p);
                }
            }
        });
    </script>
</body>

表面上查看页面结果正常,也不报警告和错误。
在这里插入图片描述
实际上存在很严重的效率问题,接下来通过给模板添加一个输入框然后分析虚拟DOM的变化来演示下这个问题。
将模板列表添加个输入框

<ul>
      <li v-for="(p,index) in persons" :key="index">
           {
    
    {p.name}}-{
    
    {p.age}}
           <input type="text">
      </li>
</ul>

此时我们往输入框中输入如下数据,然后点击添加
在这里插入图片描述
可以发现,输入框中的数据错位了!!原本对应张三-18的输入框中的数据应该也是张三哥-18才对,并且李四和王五应该也是如此才对。
下面具体分析下出现问题的原因:
如下图:
在这里插入图片描述
分析:在原数据中对应一个旧的初始虚拟DOM,在添加数据之后,产生一个新的虚拟DOM,浏览器显示的结果是拿这两个新旧虚拟DOM进行对比产生的,这里用到了一个虚拟DOM对比算法,具体描述如下:
(1)在添加新数据之后,新虚拟DOM会和虚拟DOM进行比较,比较的是key值相同的一行,例如key=0时,先比较文本内容,一个是 张三-18,一个是老刘-40,文本内容不同,那么文本就使用新虚拟DOM中的文本,接下来比较子元素input,发现input是相同的,相同就复用旧虚拟DOM中的input,此时旧虚拟DOM中的input输入框中有内容,那么就会同样保存下来。
在这里插入图片描述

(2)key=1和key=2比较过程也和(1)一样。下面具体说说key=3的情况
(3)在旧的虚拟DOM中,原本王五-20的key值是2,而到了新虚拟DOM中,王五-20的key值就成了3,而旧虚拟DOM中不存在key=3的内容,所以比较不了,那么就会直接创建新的真实DOM,输入框中也就不会存在内容。
(4)经过虚拟DOM比较之后就可以转换为真实DOM了


补充::key是属于Vue的,当打开页面查看模板时,会发现html代码中并不会出现:key,如下:
在这里插入图片描述


为什么用唯一值作为key就不会出错了

如下:
在这里插入图片描述
对于key值不存在时则会直接创建新的真实DOM。

3、列表过滤

watch实现

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>watch实现</title>
</head>

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <ul>
            <li v-for="(p,index) in filPersons" :key="index">
                {
   
   {p.name}}-{
   
   {p.age}}-{
   
   {p.sex}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el: "#root",
            data: {
      
      
                keyWord: "",
                // 用数组存储
                persons: [
                    {
      
       id: "001", name: "马冬梅", age: 30, sex: "女" },
                    {
      
       id: "002", name: "周冬雨", age: 28, sex: "女" },
                    {
      
       id: "003", name: "周杰伦", age: 30, sex: "男" },
                    {
      
       id: "004", name: "白敬亭", age: 26, sex: "男" },
                ],
                fillPersons: []
            },
            watch: {
      
      
                keyWord: {
      
      
                    // 如果要页面加载后就展示所有数据,那么添加下面语句
                    immediate: true,
                    // handler(newValue,oldValue)
                    handler(val) {
      
      
                        // 用一个新数组存储过滤后的数据
                        this.filPersons = this.persons.filter((p) => {
      
      
                            // indexOf()方法的返回值为-1时表示无匹配数据
                            return p.name.indexOf(val) !== -1;
                            // 注意当indexOf中传的是空字符串时,返回值是0,因为还是存储了空字符串,索引为0
                        })
                    }
                }
            }
        })
    </script>
</body>

</html>

computed实现

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>computed实现</title>
</head>

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <ul>
            <li v-for="(p,index) in filPersons" :key="index">
                {
   
   {p.name}}-{
   
   {p.age}}-{
   
   {p.sex}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el: "#root",
            data: {
      
      
                keyWord: "",
                // 用数组存储
                persons: [
                    {
      
       id: "001", name: "马冬梅", age: 30, sex: "女" },
                    {
      
       id: "002", name: "周冬雨", age: 28, sex: "女" },
                    {
      
       id: "003", name: "周杰伦", age: 30, sex: "男" },
                    {
      
       id: "004", name: "白敬亭", age: 26, sex: "男" },
                ],
            },
            computed: {
      
      
                filPersons(){
      
      
                    return this.persons.filter((p)=>{
      
      
                        return p.name.indexOf(this.keyWord) !== -1;
                    })
                }
            }
        })
    </script>
</body>

</html>

4、列表排序

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="../../JS/vue.js"></script>
    <title>列表排序</title>
</head>

<body>
    <div id="root">
        <h2>人员列表</h2>
        <input type="text" placeholder="请输入名字" v-model="keyWord">
        <input type="button" value="升序" @click="sortType = 1">
        <input type="button" value="降序" @click="sortType = 2">
        <input type="button" value="原顺序" @click="sortType = 0">
        <ul>
            <!-- filPersons是计算属性 -->
            <li v-for="(p,index) in filPersons" :key="index">
                {
   
   {p.name}}-{
   
   {p.age}}-{
   
   {p.sex}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el: "#root",
            data: {
      
      
                // 输入框中输入的值
                keyWord: "",
                // 排序方式标记,0表示原顺序,1表示升序,2表示降序
                sortType: 0,
                // 用数组存储
                persons: [
                    {
      
       id: "001", name: "马冬梅", age: 30, sex: "女" },
                    {
      
       id: "002", name: "周冬雨", age: 28, sex: "女" },
                    {
      
       id: "003", name: "周杰伦", age: 30, sex: "男" },
                    {
      
       id: "004", name: "白敬亭", age: 26, sex: "男" },
                ],
            },
            computed: {
      
      
                filPersons() {
      
      
                    // 先过滤出名字
                    const arr = this.persons.filter((p) => {
      
      
                        return p.name.indexOf(this.keyWord) !== -1;
                    });

                    // 判断是否需要排序,0表示原顺序,其他为排序
                    if (this.sortType) {
      
      
                        // sort进行排序,返回的是一个新数组
                        arr.sort((a, b) => {
      
      
                            return this.sortType === 1 ? a.age - b.age : b.age - a.age;
                        })
                    };

                    return arr;
                }
            }
        })
    </script>
</body>

</html>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lalala_dxf/article/details/124901343