Vue列表渲染

列表循环 v-for

将一个数组对应为一组元素

v-for指令根据数组选项列表进行渲染,使用时需使用item in items形式的特殊语法。items是源数据数组且item是数组元素迭代的别名。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="item in items">{{item.username}}</li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                items:[
                    {id:1, username:'alice'},
                    {id:2, username:'ben'},
                    {id:3, username:'carl'},
                ]
            }
        });
    </script>
</body>
</html>

v-for块中,拥有对父作用域属性的完全访问权限。v-for支持一个可选第二个参数为当前项的索引。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="(item,index) in items">
                    {{title}}{{index+1}}
                    <span class="pull-right">{{item.username}}</span>
                </li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                title:'user',
                items:[
                    {id:1, username:'alice'},
                    {id:2, username:'ben'},
                    {id:3, username:'carl'},
                ]
            }
        });
    </script>
</body>
</html>

可使用of替换in作为分隔符,因为它最接近JS迭代器的语法

<div v-for="item of items"></div>

一个对象的 v-for

可使用v-for通过一个对象的属性来迭代

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="val in user">
                    {{val}}
                </li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                user:{
                    name:'junchow',
                    gender:'male',
                    age:31
                }
            }
        });
    </script>
</body>
</html>

v-for提供第二个参数为键名

<li class="list-group-item" v-for="(val,key) in user">
    <strong>{{key}}</strong>
    <span class="pull-right">{{val}}</span>
</li>

v-for提供第三个参数为索引

<li class="list-group-item" v-for="(val,key,idx) in user">
    <strong>{{idx}} {{key}}</strong>
    <span class="pull-right">{{val}}</span>
</li>

在遍历对象时,是按Object.keys()的结果遍历,不能保证其结果在不同的JS引擎下是一致的。

key

当Vue.js用v-for正在更新已渲染过的元素列表时,默认使用"就地复用"策略。若数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是简单复用此处每个元素,且确保它在特定索引下显示已被渲染过的每个元素。这个默认的模式是高效的,但只适用于不依赖子组件状态或临时DOM状态的列表渲染输出。

为了给Vue一个提示,以便能跟踪每个节点的身份,从而重用和重排现有元素,需为每项提供一个唯一的key属性。理想的key值是每项都有且仅有唯一的id。这个特殊的属性的工作方式类似于一个属性,需使用v-bind来绑定动态值。

<li class="list-group-item" v-for="item in items" :key="item.id">

</li>

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

因为它是Vue识别节点的一个通用机制,key并不与v-for特别关联,key具有其他用途。

数组更新检测

变异方法

Vue包含一组观察数组的变异方法,它们会触发视图更新。

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
app.items = app.items.push({message:'foo'});

替换数组

  • 变异方法(mutation method)顾名思义,会改变方法调用的原始数组。
  • 非变异方法(non-mutating method)不会改变原始数组,总是返回一个新数组。

当使用非变异方法时,可用新数组替换旧数组。

  • filter()
  • concat()
  • slice()
app.items = app.items.filter(function(item){
    return item.message.match(/foo/);
});

非变异方法并不会导致Vue丢弃现有DOM并重新渲染整个列表,Vue为了使得DOM元素得到最大范围的重用而实现了一些智能的、启发式的方法,所以用一个含有相同元素的数组去替代原来的数组是非常高效的。

注意事项

由于JS自身限制Vue不能检测以下变动的数组:

  • 当利用索引直接设置一个项时 vm.items[index]=newVal
  • 当修改数组的长度时 vm.items.length=newLen
    为解决第一类问题,以下两种方式都可以实现和vm.items[index]=newVal相同的效果,同样也触发状态更新。
// Vue.set
Vue.set(app.items, index, newVal)

// Array.prototype.splice
app.items.splice(index, 1, newVal);

为了解决第二类问题可使用splice

app.items.splice(newLen)

对象更改检测注意事项

由于JS限制Vue无法检测对象属性的添加或删除

var vm = new Vue({
    data:{
        message:'hello'
    }
});
//vm.message是响应式的
vm.username = 'alice';//非响应式

对于已经创建的实例,Vue无法动态添加根级别的响应式属性。但可使用Vue.set(obj,key,val)方式向嵌套对象添加响应式属性。

var vm  = new Vue({
    data:{
        profile:{
            name:'alice'
        }
    }
});

//添加新属性到嵌套的profile对象
Vue.set(vm.profile, 'age', 20);

//使用vm.$set实例方法,它只是全局Vue.set的别名。
vm.$set(this.profile, 'age', 20);

有时可能需为已有对象赋予多个新属性,如使用Object.assign()_.extend()。在这种情况下,用两个对象的属性创建一个新对象。若想添加新的响应式属性:

this.profile = Object.assign({}, this.profile, {age:31, color:'green'})

显示过滤/排序结果

想要显示一个数组的过滤或排序副本,而非实际改变或重置原始数据。此情况下可创建返回过滤或排序数组的计算属性。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="num in even">
                    {{num}}
                </li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                numbers:[11,2,43,14,5,26,17,8,29]
            },
            computed:{
                even:function(){
                    return this.numbers.filter(function(number){
                        return number%2===0;
                    })
                }
            }
        });
    </script>
</body>
</html>

当计算属性不适用情况下,如在嵌套v-for循环中,可使用method方法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="item in even(numbers)">
                    {{item}}
                </li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                numbers:[11,2,43,14,5,26,17,8,29]
            },
            methods:{
                even:function(items){
                    return items.filter(function(item){
                        return item%2===0;
                    })
                }
            }
        });
    </script>
</body>
</html>

取值范围的v-for

v-for可取整,此情况下将重复多次模板。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="item in 10">
                    {{item}}
                </li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
            }
        });
    </script>
</body>
</html>

v-for on a <template>

类似于v-if也可利用带有v-for<template>渲染多个元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <template v-for="item in items">
                    <li class="list-group-item">{{item.username}}</li>
                    <li class="divider"></li>
                </template>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                items:[
                    {username:'alice'},
                    {username:'ben'},
                    {username:'carl'},
                    {username:'deny'},
                ]
            }
        });
    </script>
</body>
</html>

v-for with v-if

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group">
                <li class="list-group-item" v-for="item in items" v-if="item.show">{{item.username}}</li>
            </ul>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                items:[
                    {username:'alice', show:true},
                    {username:'ben', show:true},
                    {username:'carl', show:false},
                    {username:'deny', show:true},
                ]
            }
        });
    </script>
</body>
</html>

若需有条件地跳过循环的执行,可将v-if置于外层元素或<template>上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">title</h1>
            <ul class="list-group" v-if="items.length">
                <li class="list-group-item" v-for="item in items" v-if="item.show">{{item.username}}</li>
            </ul>
            <p class="alert alert-warning" v-else>no data...</p>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                items:[
                    {username:'alice', show:true},
                    {username:'ben', show:true},
                    {username:'carl', show:false},
                    {username:'deny', show:true},
                ]
            }
        });
    </script>
</body>
</html>

一个组件的 v-for

在自定义组件中,可像普通元素一样使用v-for

<item class="list-group-item" v-for="item in items" :key="item.id" v-if="item.show">{{item.username}}</item>

在Vue2.2.0+版本中,当在组件中使用v-for时,key是必须的。然后在任何数据都不会被自动传递到数组里,以为组件有自己独立的作用域。为了把迭代数据传递到数组里需使用props

<item class="list-group-item"
    v-for="(item,index) in items"
    v-bind:item="item"
    v-bind:index="index"
    v-bind:key="item.id"
    ></item>

不自动将item注入到组件里的原因是,这会使得组件与v-for的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <div id="app" class="container">
        <template v-if="true">
            <h1 class="page-header">TODO-LIST</h1>
            <input v-model="title" v-on:keyup.enter="create" class="form-control" placeholder="add a todo" />
            <hr>
            <ul class="list-group" v-if="items.length">
                <li class="list-group-item"
                    is="item"
                    v-for="(item,index) in items"
                    v-bind:title="item.title"
                    v-bind:index="index"
                    v-bind:key="item.id"
                    v-on:remove="items.splice(index,1)"
                    ></li>
            </ul>
            <p class="alert alert-warning" v-else>no data...</p>
        </template>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script>
        Vue.component('item',{
            template:"<li>{{title}} <a class='pull-right' v-on:click='$emit(\"remove\")'>&times;</a></li>",
            props:['title']
        })
        var vm = new Vue({
            el:'#app',
            data:{
                title:'',
                items:[
                    {id:1,title:'java'},
                    {id:2,title:'javascript'},
                    {id:3,title:'python'},
                    {id:4,title:'vue'},
                ],
                next:5
            },
            methods:{
                create:function(){
                    this.items.push({
                        id:this.next++,
                        title:this.title
                    });
                    this.title = '';
                }
            }
        });
    </script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343