Vue2.0学习--基础--6--列表渲染

(day6-2)

 

一、用 v-for 把一个数组对应为一组元素

我们可以使用 v-for 指令基于一个数组来渲染一个列表

v-for 指令需要 “item in items” 的特殊语法,其中items指源数据数组,item指被迭代的数组元素的别名

简单例子前面第一天已经有描述,这里就不赘述了。

#在 v-for 中的第二个参数

  • v-for 还支持可选的第二个参数,表示当前项的索引;
  • v-for 块中还可以访问父作用域的property。
<ul id="example1">
    <li v-for="(item,index) in items">
        {{ parentMessage }} - {{ index }} - {{ item.message }}
    </li>
</ul>
var example1 = new Vue({
    el:"example1",
    data:{
        parentMessage:'parent',
        items:{
            {message:'Foo'},
            {message:'Bar'}
        }
    }
});

 那么对于上面HTML中的v-for,将得到的结果为:

parent-0-Foo

parent-1-Bar

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

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

二、在 v-for 中使用对象

  •  可以使用v-for 来遍历一个对象的property。
  • 可以传入第二个参数 为 property的名称(即键名)。
  • 可以传入第三个参数作为索引。
<div v-for="(value,name,index) in object">
    {{ index }}. {{ name }}: {{ value }}
</div>
data:{
    object:{
        title:'How to do lists in Vue',
        author:'Jane Doe',
        publishedAt:'2016-04-10'
    }
}

将会得到如下结果:

0.title:How to do lists in Vue
1.author:Jane Doe
2.publishedAt:2016-04-10

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

区分一下遍历对象属性时使用的 for-in 、Object.keys()、Object.getOwnpropertyNames()。
以及查找属性的方法——property in Obeject 、hasOwnproperty()。

这些都是在ES5原型语法中讲到的遍历属性的方法。

其中  for-in 、Object.keys()、Object.getOwnpropertyNames()三个方法使用来遍历属性,property in Obeject 、hasOwnproperty()使用查找属性是否存在于对象上或者对象上。
可以用这个图来简单的表示:

 (为了便于表示,上面我将实例和原型中不可枚举属性和可枚举属性分开了) 

  • for-in 中的in单独使用时(即上面的 property in object),in操作符会在通过对象能够访问给定属性时返回true,无论属性在实例中还是原型中。
  • object.hasOwnProperty(),只有属性位于实例中时,才会返回true。
  • Object.keys(),只有属性在实例中,且为可枚举时,返回一个包含所有可枚举属性的字符串数组
  • for-in,返回所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。
  • Object.getOwnPropertyNames(),所以的实例属性,无论是是否可枚举。

这里的实例表示的是传入到方法中的对象实例。

 三、维护状态

我们知道 v-for 就是基于 一个数组渲染一个列表。 数组就是Vue实例中的数组,列表就是HTML中的一个元素列表。

维护状态讲的就在 v-for 指令中渲染的列表中的每一个元素所应该具有的 唯一key值

维护状态是啥意思?

当Vue正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。

如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素,并且确保他们在每个索引位置正确渲染

这个类似于Vue 1.x的track-by="$index"。

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

 这是官网的原话,我们从这里知道的是,当Vue在更新(也可以说是改变)我们用v-for渲染的元素列表时,Vue会采取“就地更新”这个策略来完成更新,(这里我不知道“就地更新”是怎么实现的,以及它的为什么要这么样实现,这个要到后面填坑)

那么要就地更新,Vue就需要知道每个节点的身份,从而能够重用和重排序现有元素(为了能够实现“就地跟新?”),这个时候就需要 key 了。

key属性为每一个元素节点提供一个唯一的“标识”。

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

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

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

# key

预期 number | string | boolean | symbol 

key的特殊attribute主要用在Vue 的虚拟DOM算法,在新旧nodes对比辨识VNode。

  • 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型的元素的算法。(即不使用key,就会采取“就地更新”策略)
  • 使用key时,它会基于key的变化重新排序元素顺序,并且会移除key不存在的元素。

有相同父元素的子元素必须有独特的key。重复的key会造成渲染错误。

key也可以用于强制替换元素(或替换组件)而不是重复使用它。(在前面的条件渲染中的“用管理key管理可复用的元素”中有描述)当在如下场景下这种方式可能很有用:

  • 完整地触发组件的生命周期钩子
  • 触发过渡
<!--这里,当text发生改变时,<span>总是会被替换而不是修改,因此会触发过渡-->
<transition>
    <span :key="text">{{ text }}</span>
</transition>

四、数组更新检测 

1、变更方法 

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

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

 如何在Vue 中使用,很简单:

//就像前面讲到的Vue实例example1
example1.items.push({ message:'Baz' });

2、替换数组

变更方法,就是会变更原数组的方法。

非变更方法——filter()、concat()、slice()等。总是会返回一个新数组

当使用这些非变更方法时,可以用新数组替换旧数组。

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

Vue 为了实现DOM元素的最大重用而实现了一些智能的启发式方法,所以Vue不会丢弃现有的DOM并重新渲染整个列表。这样,用一个含有相同元素的数组去替换原来的数组时非常高效的操作。

(什么启发式方法?具体如何实现的?)

#注意:由于JavaScript的限制,Vue不能检测数组和对象的变化深入响应式原理中会有相应的讨论。

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

 有些时候我们需要过滤或者排序操作一个数组,但是我们不想实际变更或重置原始数据

# 这种情况下,我们可以创建一个计算属性,来返回过滤或排序后的数组

<li v-for="n in evenNumbers">{{ n }}</li>
data:{
    numbers:[1,2,3,4,5,6]
},

computed:{
    evenNumbers:function(){
        return this.numbers.filter(function(number){
            return number % 2 === 0;
        });
    }
}

(这里我有一个疑问就是:在Vue实例中的methods、watch选项中不要使用箭头函数,因为Vue 中的这些时选项对象的this对象已经自动指向了Vue实例,而箭头函数的this指向的是父级作用域的上下文,这样会发生冲突,导致this值不会指向期望的Vue实例。那么在这里的filter()中的function()是否可以用箭头函数来简化呢?这就需要下去搞明白为什么Vue选项参数的this值已经自动指向了Vue实例)

在嵌套的 v-for 循环中,计算属性不适用,则可以适用一个方法(methods选项):

<ul v-for="set in sets">
    <li v-for="n in even(set)">{{ n }}</li>
</ul>
data:{
    sets:[[1,22,3,4,5],[6,7,8,9,10]]
},

methods:{
    even:function(numbers){
        return numbers.filter(function(number){
            return number % 2 ===0;
        });
    }
}

六、在 v-for 中使用值范围

 v-for 可以接受整数。这种情况下会将模板重复对应次数。

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

结果为:12345678910

 七、在 <template> 上使用 v-for

 可以利用带有 v-for 的 <template> 来循环渲染一段包含多个元素的内容。比如:

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

八、v-for 与 v-if 一起使用

注意:我们不推荐在同一元素上使用v-if和v-for。这在上一章:“条件渲染”已经提到了

 要记住的是:当它们处于同一个节点时, v-for 的优先级比 v-if 的高。

这意味着, v-if 将重复运行与每一个 v-for 循环中

# 当你想为部分项 渲染 节点时,这种优先级的机制会十分的有用。如:

<!--只渲染未完成的todo-->
<li v-for="todo in todos" v-if="!todo.isComplete">
    {{ todo }}
</li>

这个时候,我们在每次渲染这个列表的时候,都会去循环遍历todos。在每次重渲染的时候遍历整个列表,那我们为什么不把符合“!todo.isComplete”的todo实现放到一个数组中,然后就只用遍历这个符合条件数组,省掉多余的循环。

//这里使用computed属性,todos是我们的依赖,
//在第一次计算中,我们将todos中符合条件的todo通过filter返回,并缓存
//之后只要todos发生变化,我们computed中的notComplete()才会重新计算并返回新值,再缓存
computed:{
    notComplete:function(){
        return this.todos.filter(function(todo){
            return !todo.isComplete;
        });
    }
}
/*
    使用这种方式:
    1、过滤后的列表只会在todos数组发生变化时才会被重新计算,过滤更加高效;
    2、使用 v-for="todo in notComplete"之后,在渲染时,只遍历 未完成的todo,渲染更高效
    3、解耦渲染层的逻辑,可维护性更强(对逻辑的更改和扩展更强)
*/

//以上就是为什么这种情况下不推荐在同一个元素下同时使用v-for和v-if

 # 如果你是要有条件的跳过循环的执行

<ul>
    <li v-for="user in users" v-if="shouldShow" :key="user.id">
        {{ user.name }}
    </li>
</ul>

 这个时候要表达的其实是,如果某个列表应该被隐藏,即 v-if 中的shouldShow的值为false,那么就不渲染。

可以看出来,这里如果某个列表的shouldShow为false,我们的v-if其实只检查一次得到这个值,就可以确定不渲染这个列表了。

但是v-for 的优先级比 v-if 高,v-if将重复运行在每一个v-for循环中。所以,虽然第一次已经确定不渲染这个列表,但是由于v-for 循环的存在,v-if还是会对列表中每个用户检查shouldShow值,然后每一次返回的都是false,这就做了多余的检测。

所以我们可以在一开始就使用这个检测,将 v-if 移动到容器元素(如:ul、ol),就只会检查shouldShow一次,且在这个值为false是不运算v-for。

所以这种情况也不推荐将v-for和v-if使用在同一个元素上。

上面的代码改为:

<ul v-if="shouldShow">
    <li v-for="user in users" :key="user.id">
        {{ user.name }}
    </li>
</ul>

 九、在组件上使用 v-for

# 在自定义组件上,可以使用v-for。

在2.2.0+的版本中,当在组件上使用v-for时,key 是必须的

<my-component v-for="todo in todos" :key="todo.id"></my-component>

(真正有点意思的来了)

为什么有意思呢?我们知道,每一个自定义的组件都是一个Vue实例,它有自己独立的作用域,所以任何数据都不会自动传递到组件中,来看一看我们第一天学的“组件化构建应用”中的例子:

Vue.component("todo-item",{
    prop:["todo"],
    template:"<li>{{ todo.text }}</li>"
});

var app7 = new Vue({
    el:"#app-7",
    data:{
        groceryList:[
            {id:0,text:"蔬菜"},
            {id:1,text:"奶酪"},
            {id:2,text:"水果"}
        ]
    }
});

 在这里如果我们使用这种v-for方式创见自定义的组件:

<div id="app-7">
    <ol>
        <todo-item v-for="item in groceryList" v-bind:key="item.id"></todo-item>
    </ol>
</div>

这里得到的结果是:啥也没有!

 (这里todo-item 是能够获取到item in froceryList 的循环次数,但得不到 item 的值)

为什么?

你看,这个自定义的组件是一个单独Vue实例,而我们这里的 groceryList 是定义在app7这个Vue实例中的,在我们定义的这个自定义组件——单独的Vue实例是不具备这个数组的。在app7 Vue实例中定义的这些数据(也就是groceryList数组)不会自动传递到组件中

怎么办呢?

我们需要一个“通道”,一个连接桥,将定义在app7实例中的groceryList 数组传递给 这个自定义组件。
这个通道就是将我们的自定义组件的"todo"属性,和 app7实例中数组的item别名相绑定

<div id="app-7">
    <ol>
        <todo-item v-for="item in groceryList" 
                   v-bind:todo="item" 
                   v-bind:key="item.id"></todo-item>
    </ol>
</div>

这样就可以输出正确的结果了。

(疑问1:按理说,app7实例全权控制了app-7元素,那么组件的作用域应该是在app7实例的之内,为什么组件的不能访问app7实例中的数据呢?
所以,要下去搞明白Vue实例中 el 选项绑定元素id,到底是如何实现的?上面这种情况为什么会这样?以及v-bind、v-for、v-if、v-on、v-model等的实现原理)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值