跳转到主要内容

主页内容

Vue基础(五)条件渲染和列表渲染

由 webadmin 发布于 阅读 60 次

1、条件渲染

1.1、v-if

v-if指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回true值的时候被渲染。我们可以给v-if赋予布尔值,来实现html元素的显示和隐藏。

示例1:给挂载点内的一个元素设置隐藏。

<div id="root">
        <div>
            <div class="box" v-if="show">测试内容</div>
        </div>
</div> 
<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                show: false
            }
            
        });
</script>

示例1运行结果:

v-if判断属性show的值为true的时候,显示了蓝色区块,当show的值为false的时候,蓝色区块被隐藏了。

v-if也可以直接赋布尔值:

<div id="root">
        <div>
            <div class="box" v-if=false>测试内容1</div>
            <!-- 或者 -->
            <div class="box" v-if=true>测试内容2</div>
        </div>
</div> 
1.1.1、v-if分组渲染
<div id="root">
            <h1>同时控制多个元素的显示隐藏</h1>
            <button @click="isShow = !isShow">显示/隐藏以下内容</button>
            <template v-if="isShow">
                <h3>标题</h3>
                <p>内容1</p>
                <p>内容2</p>
                <p>内容3</p>
                <p>内容4</p>
            </template>
    </div> 

    <script type="text/javascript" src="../lib/vue.js"></script>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                isShow: true
            }
            
        });
    </script>

分组渲染,通过一个条件,渲染多个HTML元素
(1)、给每个html元素都添加v-if,不合理
(2)、把多个html元素放在一个div中,给div添加v-if,但是多了一个无意义div
(3)、把多个html元素放在template中,给template添加v-iftemplate不会渲染到页面中去。

1.1.2、v-else

v-else必须跟在v-ifv-else-if后面,不能单独存在。

示例:data中的person的值为超级管理员的时候,页面显示“超级管理员”,值为其他的时候,页面显示“普通管理员”

<div id="root">
            <!-- 
                v-else必须跟在v-if或v-else-if后面,不能单独存在。
             -->
            <h2>v-else演示</h2>
            <div v-if="person === '超级管理员' ">
                超级管理员
            </div>
            <div v-else>普通管理员</div>
    </div> 

    <script type="text/javascript" src="../lib/vue.js"></script>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                person: "超级管理员"
            }
            
        });
    </script>

运行结果:

1.1.3、v-else-if

类似于v-elsev-else-if也必须紧跟在带v-if或者v-else-if的元素之后。

示例:根据考试分数score属性判定学生学习水平。

<div id="root">
            <!-- 
                v-else-if必须跟在v-if不能单独存在。
            -->
            <h1>v-else-if演示</h1>
            <div v-if="score > 90">
                优秀
            </div>
            <div v-else-if="score < 90 && score > 80">
                良好
            </div>
            <div v-else-if="score < 80 && score > 60">
                一般
            </div>
            <div v-else>
                差
            </div>
</div> 

    <script type="text/javascript" src="../lib/vue.js"></script>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                score: 78
            }
            
        });
    </script>

运行结果:

1.2、v-show

另一个用于根据条件显示隐藏元素的指令是v-showv-showv-if使用方式大致一样。

区别在于:

  • v-if如果条件不成立,不会渲染html,而v-show,无论条件是否成立,都会渲染,只是通过display来控制显示和隐藏。
  • v-show不支持template也不支持v-else
  • v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  • v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做,一直到条件第一次变为真时,才会开始渲染条件块。
  • 相比之下,v-show就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSSdisplay样式)进行切换。
<div id="root">
        <button @click="isShow = !isShow">显示/隐藏以下内容</button> 
        <div v-show="isShow">
            <p>文本内容</p>
        </div>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                isShow: false
            }
            
});
</script>

一般来说,v-if 有更高的切换开销,而v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好。

2、列表渲染

2.1、v-for

model里有多个数据需要在view里显示时,我可以使用v-for指令来进行操作。

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in itemsitem of items形式的特殊语法,其中items是源数据数组,而item则是被迭代的数组元素的别名。

2.1.1、v-for遍历数组

示例:把一个数组渲染成一个列表。

<div id="root">
        <h4>v-for列表渲染 —— 常规遍历</h4>
        <!-- 1、常规遍历 -->
        <ul>
            <li v-for="item in products">
                {{item}}
            </li>
        </ul>
        <h4>使用:key命令,提升循环效率</h4>
        <!-- 2、使用:key命令,提升循环效率 -->
        <ul>
            <li v-for="item of products" :key="item">{{item}}</li>
        </ul>
        <h4>为标准的用法,迭代器的体现</h4>
        <!-- 3、更为标准的用法,迭代器的体现 -->
        <ul>
            <li v-for="(item, index) in products" :key="index">
                第{{index+1}}个产品是:{{item}}
            </li>
        </ul>

    </div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                //数组数据
                products:["宝马","奔驰","沃尔沃","福特","奥迪","菲亚特","丰田","本田"]
            }
            
        });
</script>

示例运行结果:

也可以用 of 替代 in 作为分隔符,因为它更接近JavaScript 迭代器的语法。

2.1.1、v-for遍历对象

v-for 来遍历一个对象的属性。

<div id="root">
        <h2>v-for遍历对象</h2>
        <ul>
            <li v-for="(value, key) in NoteBook" ::key="key">
                {{value}}
            </li>
        </ul>
</div> 
<script type="text/javascript" src="../lib/vue.js"></script>
    <script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
            	//对象数据
                NoteBook:{
                    brand: 'Apple',
                    price: 12999,
                    sum: 1
                }
            }
            
        });
</script>

运行结果:

2.1.2、维护状态

Vue正在更新使用v-for渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时DOM 状态(例如:表单输入值)的列表渲染输出。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯— key 属性:

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

建议尽可能在使用v-for时提供key 属性,除非遍历输出的 DOM 內容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

因为它是 Vue 识别节点的一个通用机制,key 并不仅与v-for特别关联。后面我们将在指南中看到,它还具有其它用途。

不要使用对象或数组之类的非基本类型值作为v-forkey。请用字符串或数值类型的值。

2.1.3、数组更新检测

由于Javascript的限制,Vue 不能检测以下数组的变动:

  • 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  • 当你修改数组的长度时,例如:vm.items.length = newLength

举例

<div id="root">
       
        <h4>数组更新检测</h4>
        <!-- 
            数组更新检测 
        -->
        <ul>
            <li v-for="(p, index) in products" :key="index">{{p}}</li>
        </ul>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                products:["宝马","奔驰","沃尔沃","福特","奥迪","菲亚特","丰田","本田"]
            }
            
        });
</script>

通过控制台测试:

vm.products.length = 4;vm.products[10]= "起亚";这两种方式都是非响应性的。

2.1.4、更新方法

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

使用以上方法来响应式更新数组。在控制台进行如下操作:

以上操作页面数据实时发生改变,说明以上的方法操作数组具有响应性。

除此之外还有如下方法可以更新数组:

//修改products数组中的下标为1的元素的值为“路虎”
Vue.set(vm.products, 1, "路虎"); 
2.1.5、数组替换

变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如filter()concat()slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组。

<div id="root">
        <h4>替换数组</h4>
        <ul>
            <li v-for="(item, index) in items" :key="index">{{item}}</li>
        </ul>
</div> 
<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                items: [16, 18, 29, 32, 41, 17, 12, 33, 54, 66]
            } 
        });
        //过滤出大大于18的数字
        vm.items = vm.items.filter(function(item){
            return item > 18
        })
</script>

运行结果:

这可能使人误认为Vue丢弃现有DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得DOM元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。

2.1.6、对象更新检测

还是由于JavaScript的限制,Vue不能检测对象属性的添加或删除:

<div id="root">
       
        <h4>对象更新检测</h4>
        <!-- 
            对象更新检测 
        -->
        <p>{{a}}</p>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                a:1
            } 
        });
</script>

在控制台执行:vm.a = 3,页面上显示的数据会立即变成3,可见,直接修改对象的值是响应式的。

在控制台执行:vm.b = 5,页面没有发生任何变化,可见,直接新增属性和值不是响应式的,页面不会发生变化。

运行结果:

对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set (object, propertyName,value) 方法向嵌套对象添加响应式属性。例如,对于:

<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                userProfile:{
                    name: "Jack"
                }
            } 
        });
</script>

可以添加一个新的age属性到嵌套的userProfile对象:

Vue.set(vm.userProfile, 'age', 20);

运行结果:

2.1.7、显示过滤/排序后的结果

有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际变更或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。

<div id="root">
        <h4>显示过滤/排序后的数据</h4>
        <ul>
            <li v-for="(num, index) in evenNumbers" ::key="index">{{num}}</li>
        </ul>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                numbers:[1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 23, 17, 21, 33, 18]
            },
            computed:{
                evenNumbers(){
                    return this.numbers.filter(function(n){
                        return n % 2 === 0;
                    })
                }
            }
        });
</script>

运行结果:

2.1.8、v-for里使用值范围
<div id="root">
        <h4>v-for里使用值范围</h4>
        <ul>
            <li v-for="n in 10" :key="n">{{n}}</li>
        </ul>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{}
        });
</script>

运行结果:

2.1.9、在 <template> 上使用 v-for
<div id="root">
        <h4>21_列表渲染_在template使用 v-for</h4>
        <ul>
            <template v-for="(item, index) of items">
                <li :key="index">姓名:{{item.name}},年龄{{item.age}}</li>
            </template>
        </ul>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                items:[
                    {name:'zhangsan', age:20},
                    {name:'lisi', age:17},
                    {name:'wangwu', age:21},
                    {name:'Jack', age:27}
                ]
            }
        });
</script>

运行结果:

2.1.10、v-forv-if 一同使用

当它们处于同一节点,v-for 的优先级比v-if 更高,这意味着v-if 将分别重复运行于每个 v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:

<div id="root">
        <h4>v-for与v-if一同使用</h4>
        <ul>
            <li v-for="(todo, index) in todos" :key="index" v-if="!todo.status">{{todo.name}}</li>
        </ul>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                todos:[
                    {name:'打球', status: true},
                    {name:'听音乐', status: false},
                    {name:'看电影', status: false},
                    {name:'看书', status: true},
                    {name:'理发', status: false},
                    {name:'游泳', status: false}
                ]
            }
        });
</script>

上面这个例子中,同时使用了v-for和v-if,实现了渲染列表的时候,只显示符合条件的数据。(即只显示status = false的数据)。

运行结果:

显示出了所有未完成的todo

而如果目的是有条件地跳过循环的执行,那么可以将v-if置于外层元素(或 <template>)上。如:

<div id="root">
        <h4>v-for与v-if一同使用:有条件的跳过循环</h4>
        <ul v-if="students.length">
            <li v-for="(s, index) in students" :key="index">{{s}}</li>
        </ul>
        <p v-else>暂无内容~</p>
</div> 

<script type="text/javascript" src="../lib/vue.js"></script>
<script type="text/javascript">
        const vm = new Vue({
            el: '#root',
            data:{
                students:["张三","李四"]
            }
        });
</script>

运行结果: