6.条件模板与循环模板

条件渲染

v-if

在字符串模板中,如 Handlebars ,我们得像这样写一个条件块:

<!-- Handlebars 模板 -->
{{#if ok}}
  <h1>Yes</h1>
{{/if}}

在 Vue.js ,我们使用 v-if 指令实现同样的功能:

<h1 v-if="ok">Yes</h1>

也可以用 v-else 添加一个 “else” 块:

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>

template v-if

因为 v-if 是一个指令,需要将它添加到一个元素上。但是如果我们想切换多个元素呢?此时我们可以把一个 <template> 元素当做包装元素,并在上面使用 v-if,最终的渲染结果不会包含它。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-else

可以用 v-else 指令给 v-if 或 v-show 添加一个 “else” 块:

<div v-if="Math.random() > 0.5">
  Sorry
</div>
<div v-else>
  Not sorry
</div>

v-else 元素必须紧跟在 v-if 或 v-show 元素的后面——否则它不能被识别。

v-show

另一个根据条件展示元素的选项是 v-show 指令。用法大体上一样:

<h1 v-show="ok">Hello!</h1>

不同的是有 v-show 的元素会始终渲染并保持在 DOM 中。v-show 是简单的切换元素的 CSS 属性 display 。

注意 v-show 不支持 <template> 语法。

v-if vs. v-show

v-if 是真实的条件渲染,因为它会确保条件块在切换当中适当地销毁与重建条件块内的事件监听器和子组件。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。
相比之下, v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。
一般来说, v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换使用 v-show 较好,如果在运行时条件不大可能改变则使用 v-if 较好。
上述的例子如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>level05</title>
<script type="text/javascript" src="js/vue.2.1.0.js"></script>
</head>
<body>
<div id="app">
    <div v-if="flag===true">
        {{trueText}}
    </div>
    <div v-if="flag===false">
        {{falseText}}
    </div>

    <template v-if="flag===true">
        <div class="1">
            {{trueText}}
        </div>
        <div class="1">
            {{trueText}}
        </div>
    </template>
    <template v-if="flag===false">
        <div class="1">{{falseText}}</div>
        <div class="1">{{falseText}}</div>
    </template>

    <div v-if="flag===true">
        {{trueText}}
    </div>
    <div v-else>
        {{falseText}}
    </div>

    <div v-show="flag===true">show me</div>



</div>
<script type="text/javascript">

var app = new Vue({
    el:'#app'
    ,data:{
        flag:true
        ,trueText:'为true时显示我'
        ,falseText:'为false时显示我'
    }
});
//$watch是用来监控属性或者方法是否改变的监控器
app.$watch('msg', function (newVal, oldVal) {
    console.log("msg 改变了")
})

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

循环模板

v-for

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

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      {message: 'foo' },
      {message: 'Bar' }
    ]
  }
})

结果:

Foo
Bar

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

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

结果:

Parent - 0 - Foo
Parent - 1 - Bar

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

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

如同 v-if 模板,你也可以用带有 v-for 的 <template> 标签来渲染多个元素块。例如:

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>

对象迭代 v-for

你也可以用 v-for 通过一个对象的属性来迭代。

<ul id="repeat-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
new Vue({
  el: '#repeat-object',
  data: {
    object: {
      FirstName: 'John',
      LastName: 'Doe',
      Age: 30
    }
  }
})

结果:

John
Doe
30

你也可以提供第二个的参数为键名:

<div v-for="(value, key) in object">
  {{ key }} : {{ value }}
</div>

第三个参数为索引:

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }} : {{ value }}
</div>

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

整数迭代 v-for

v-for 也可以取整数。在这种情况下,它将重复多次模板。

<div>
  <span v-for="n in 10">{{ n }}</span>
</div>

结果:

1 2 3 4 5 6 7 8 9 10

组件 和 v-for

了解组件相关知识,查看 组件 。Feel free to skip it and come back later.

在自定义组件里,你可以像任何普通元素一样用 v-for 。

<my-component v-for="item in items"></my-component>

然而他不能自动传递数据到组件里,因为组件有自己独立的作用域。为了传递迭代数据到组件里,我们要用 props :

<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index">
</my-component>

不自动注入 item 到组件里的原因是,因为这使得组件会紧密耦合到 v-for 如何运作。在一些情况下,明确数据的来源可以使组件可重用。

下面是一个简单的 todo list 完整的例子:

<div id="todo-list-example">
  <input
    v-model="newTodoText"
    v-on:keyup.enter="addNewTodo"
    placeholder="Add a todo"
  >
  <ul>
    <li
      is="todo-item"
      v-for="(todo, index) in todos"
      v-bind:title="todo"
      v-on:remove="todos.splice(index, 1)"
    ></li>
  </ul>
</div>
Vue.component('todo-item', {
  template: '\
    <li>\
      {{ title }}\
      <button v-on:click="$emit(\'remove\')">X</button>\
    </li>\
  ',
  props: ['title']
})
new Vue({
  el: '#todo-list-example',
  data: {
    newTodoText: '',
    todos: [
      'Do the dishes',
      'Take out the trash',
      'Mow the lawn'
    ]
  },
  methods: {
    addNewTodo: function () {
      this.todos.push(this.newTodoText)
      this.newTodoText = ''
    }
  }
})

KEY

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,而不是移动 DOM 元素来匹配数据项的顺序, Vue 将简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x 的 track-by="$index" 。

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

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。理想的 key 值是每项都有唯一 id。这个特殊的属性相当于 Vue 1.x 的 track-by ,但它的工作方式类似于一个属性,所以你需要用 v-bind 来绑定动态值(在这里使用简写):

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

建议尽可能使用 v-for 来提供 key ,除非迭代 DOM 内容足够简单,或者你是故意要依赖于默认行为来获得性能提升。

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

数组更新检测

变异方法

Vue 包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:

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

你打开控制台,然后用前面例子的 items 数组调用突变方法:example1.items.push({ message: 'Baz' }) 。

重塑数组

变异方法(mutation method),顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异(non-mutating method)方法,例如: filter(), concat(), slice() 。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

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

注意事项

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

当你直接设置一个项的索引时,例如: vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如: vm.items.length = newLength
为了避免第一种情况,以下两种方式将达到像 vm.items[indexOfItem] = newValue 的效果, 同时也将触发状态更新:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)

避免第二种情况,使用 splice:

example1.items.splice(newLength)

显示过滤/排序结果

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

例如:

<li v-for="n in evenNumbers">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

或者,您也可以使用在计算属性是不可行的 method 方法 (例如,在嵌套 v-for 循环中):

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}

上述的例子如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>level06</title>
<script type="text/javascript" src="js/vue.2.1.0.js"></script>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in list">{{item.name}}</li>
    </ul>

    <ul>
        <li v-for="(item,index) in list">
            {{parentMessage}} - {{index}} - {{item.name}}
        </li>
    </ul>

    <!-- template v-for 语法 -->
    <ul>
        <template  v-for="(item,index) in list">
        <li>
            {{parentMessage}} - {{index}} - {{item.name}}
        </li>
        <li>我是附件的内容</li>
        </template>
    </ul>

    <!--遍历对象内容-->
    <ul>
        <li v-for="(value, key, index) in user">
            {{index+1}}. {{key}} : {{value}}
        </li>
    </ul>

</div>


<!--循环组件-->
<div id="todoContainer">
    <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" />
    <ul>
        <li
            is="todo-item"
            v-for="(todo, index) in todos"
            v-bind:title="todo"
            v-on:remove="todos.splice(index, 1)">
        </li>
    </ul>
</div>

<script type="text/javascript">


var app = new Vue({
    el:'#app'
    ,data:{
        parentMessage:'我app的message'
        ,list:[
            {name:'item1'}
            ,{name:'item2'}
            ,{name:'item3'}
            ,{name:'item4'}
        ]
        ,user:{
            name:'louis'
            ,age:20
            ,address:'LiaoNing ShenYang'
        }
    }
});

//TODO的列子
Vue.component('todo-item', {
  template: '\
    <li>\
      {{ title }}\
      <button v-on:click="$emit(\'remove\')">X</button>\
    </li>\
  ',
  props: ['title']
})
var todo = new Vue({
    el:'#todoContainer'
    ,data:{
        newTodoText:'',
        todos:[
            'todo item1',
            'todo item2',
            'todo item3'
        ]
    }
    ,methods:{
        addNewTodo:function(){
            this.todos.push(this.newTodoText);
            this.newTodoText = "";
        }
    }
});



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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容