# 列表渲染
# 介绍
如果要渲染一组数据,那么在 Vue 中可以使用 v-for 指令。
# 渲染数组
v-for指令是用来遍历渲染一组数据的。以<child> in <parent>
的形式,child
为数组的每一项子元素,也就是要被遍历的每一项;parent
为数组本身。如item in list
,那么,list
就是一个数组,而item
就是list
数组里面的每一个子元素。item
= list[0]、list[1]、list[2]...list[n]
。下面看个例子:
<template>
<div>
<ul>
<li v-for="item in list" :key="item.age">
{{item.name}} --- {{item.age}}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
name: 'tim',
age: 10
},
{
name: 'sam',
age: 15
},
{
name: 'colin',
age: 20
}
]
}
}
}
</script>
<style></style>
- tim --- 10
- sam --- 15
- colin --- 20
同时,v-for
还提供了index
属性,即当前遍历项的索引:
<ul>
<li v-for="(item, index) in list" :key="index">
{{item.name}} --- {{item.age}}
</li>
</ul>
index
跟item
一起,放在一个括号里面,用,
分隔。同时,我们可以把key
属性换为index
。 那么这个key
是干什么用的呢?官网文档里面指出,key
是在 Vue 的 dom 算法的时候,用来对比新旧vnodes
的。因为 vue 采用虚拟 dom,当页面发生改变时,不会把所有的 html 元素全都改变,只会改变 发生改动的部分。
同时还能够看到在 key 的前面有一个:
,这个是v-bind:
指令的简写形式。稍后会介绍v-bind
。
继续上面的例子,假设我们有一组数据,存放在data
里面的list
中,在页面上,我们通过ul
的形式展现出来,因为我们要得到的是一个展现用户名字和年龄的列表,所以v-for
指令要添加在<li>
标签上,这样我们能够得到一个<ul>
和一堆<li>
,这也是我们需要的页面布局,如果我们把v-for
指令放在<ul>
上面呢?我们来看看:
<ul v-for="item in list" :key="item.age">
<li>{{item.name}} --- {{item.age}}</li>
</ul>
- tim --- 10
- sam --- 15
- colin --- 20
比较明显的是每个<li>
之间的缝隙变大了,而在看页面结构能看出我们最终渲染出来的是一堆<ul>
标签和一个<li>
,这说明v-for
放在哪里,那么就遍历哪里。而我们需要的不过是一个展现列表而已,所以,第二种方式是错误的,我们应该把v-for
指令放在<li>
标签上!
# 渲染对象
还记得上一节中的小例子吗,这里把userInfo
的数据拿过来,然后使用v-for
渲染出来:
<template>
<div class="container">
<div v-if="userInfo.isMember" class="member">
<!-- change -->
<p v-for="(value, name, index) in userInfo" :key="index">
{{name}}: {{value}}
</p>
<!-- end -->
</div>
<div v-else class="noMember">
<h1>你现在不是会员哦!</h1>
<button @click="userInfo.isMember = 1">点击申请</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userInfo: {
name: 'tim',
age: 10,
sex: 1, // 1男 2女
isMember: 0 // 0为非会员 1 为会员
}
}
}
}
</script>
<style scoped lang=""></style>
这里把渲染方式进行了修改,不是在手动写出每一项的内容,而是通过v-for
进行了渲染。在v-for
里面,我使用了value, name, index
,这三个属性分别代表:
- value 当前项的值,对于
userInfo
来说,就是tim、10、1 和 0
- name 当前项的 key,对于
userInfo
来说,就是name、age、sex 和 isMember
- index 当前项的索引,也就是
0、1、2 和 3
通过以上分析,我们应该知道,我们以name: value
的形式写出来,那么结果应该是这样的:
name: tim
age: 10
sex: 1
isMember: 1
以上就是关于v-for
的全部内容了,可能有些人还对:key
属性有些不解,没关系,我们将在下一小节讲解v-bind:
的使用。不过在此之前,我们还是要通过几个例子来加深一下印象。
# 小例子
# 展示表格数据
;[
{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
},
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}
]
现在假设我们有一组数据,然后使用table
进行展示,我们当然不可能每一项都手动写出来,所以这时候可以使用v-for
来渲染:
<template>
<div class="container">
<table>
<thead>
<tr>
<th>data</th>
<th>name</th>
<th>address</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="index">
<td>{{item.date}}</td>
<td>{{item.name}}</td>
<td>{{item.address}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
},
{
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}
]
}
}
}
</script>
<style scoped lang=""></style>
在接下来的大部分例子中,为了让大家关注业务逻辑和代码,我将会省略 css 样式。
在上面的代码中,我们使用v-for
来遍历渲染一组数据 list
,本身没有什么难点,但在实际项目中,我们需要在data
里面定义list
,然后在请求到数据后,把数据赋值给list
,之后页面就会自动完成渲染工作。
为了给大家更真实的“游戏”体验,我给大家Mock
了一组数据,并创建了一个使用get
请求访问的接口,地址为:https://easy-mock.com/mock/5e1980617f109b0caa4d2d5e/listExample
那么,使用真实的接口后,代码为:
<template>
<div class="container">
<table>
<thead>
<tr>
<th>data</th>
<th>name</th>
<th>address</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="index">
<td>{{ item.date }}</td>
<td>{{ item.name }}</td>
<td>{{ item.address }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
list: [] // 初始化好一个`list`空数组
}
},
methods: {
initData() {
axios
.get(
'https://easy-mock.com/mock/5e1980617f109b0caa4d2d5e/listExample'
)
.then(res => {
console.log(res, 'res')
this.list = res.data.data.list // 这条代码最重要 是赋值代码
})
.catch(err => {
throw err
})
}
},
mounted() {
this.initData() // 这里是调用代码 在本文的生命周期一节中会讲解
}
}
</script>
<style scoped lang=""></style>
注意
上例使用了用于 Http 请求的axios
。如果要使用的话,需要在index.html
的<head>
标签中放入如下 cdn:<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
。真实的项目里面,应该通过npm install axios -S
的方式安装axios
的 npm 包,然后在项目引用。
关于 axios 在本文的最后会进行讲解,这里不多做介绍,大家只需要关注以下几点:
- 如果要使用
list
,则需要在data
里面初始化一个list: []
。list: []
代表:初始化一个 list 变量,并赋值为一个空数组。 - 在 js 代码里面访问
data
里面的变量,比如list
,我们需要以this.xxx
的形式,在本例中是this.list
(在某些情况下可能使用 var _this = this;)。 res.data.data.list
就是我们通过接口获取到真实的数据。(可以通过 F12 控制台查看数据)- 我们通过
this.list
=res.data.data.list
把获取到的数据赋值给本地的list
,然后页面就会自动渲染。 - 如果 A 页面有 list 变量,B 页面也有 list 变量,那么会不会冲突呢?答案是不会,因为我们使用的是
data(){}
函数,上面也讲过,每个组件之间不会互相影响。
上面的代码使用的 es6 语法,那么使用 es5 语法的代码如下:
<script>
export default {
data() {
return {
list: []
}
},
methods: {
initData: function() {
console.log(this, '外部的this') // {1}
var _this = this
axios
.get(
'https://easy-mock.com/mock/5e1980617f109b0caa4d2d5e/listExample'
)
.then(function(res) {
console.log(res, 'res')
console.log(this, '内部的this') // {2}
console.log(_this, '_this')
_this.list = res.data.data.list
})
.catch(function(err) {
throw err
})
}
},
mounted() {
this.initData()
}
}
</script>
因为使用 es5 语法,出现了 this 指向问题。所以,我们需要在函数开始位置把 this 储存起来,然后在函数内部依然能获取到 vue 实例。 也就是说,在{1}
的时候,this 指向 vue 实例,而在{2}
的时候,this
指向undefined
。
至于为什么使用 es6 语法的箭头函数能够通过this
访问 vue 实例,而是用es5
传统的函数方式不能够访问,大家可以在网上自行查找一下关于:箭头函数 this 作用域 的文章。