本文版权归 “公众号 | 前端一万小时” 所有,欢迎转载!
转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥本系列文章已在“公众号 | 前端一万小时”更新完毕,有需要的小伙伴可按需前往查看。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
涉及面试题:
1. 说出几种 Vue 当中的指令和它的用法?
2. 为什么使用 key?
3. 列举常用的指令?
4. v-for 指令的目的是什么?
5. 如何复用有 key 属性的元素?
6. 为什么不能在同一个元素上同时使用 v-if 和 v-for 指令?
7. 为什么使用 for 指令时需要 key 属性?
8. 如何在一个范围内使用 v-for 指令?
9. 如何在模板上使用 v-for 指令?
10. 什么是数组检测突变的方法?
11. 什么是数组检测非突变方法?
12. 检测数组变化有什么注意事项?
13. 检测对象变化有什么注意事项?
[编号:vue_13]
列表渲染,在“Vue 初识”阶段编写“简易 TodoList”时,我们就使用 v-for
指令将 TodoList 内容的数组渲染成列表,并实现了向列表中新增/删除内容等功能。
前面的文章我们是这样使用 v-for
的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>前端一万小时-Vue 中的列表渲染</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="app">
<div v-for="(item, index) of list"> <!-- 2️⃣-①:使用 v-for 指令对 list 数组进行渲染;
2️⃣-②:括号中可传递两个参数,第一个 item 为 list
中的每一项内容,第二个 index 为每项所对应的下标;
2️⃣-③:“item of list”中的 of 分隔符,
是更推荐用来替代 in 的分隔符; -->
{{item}} --> {{index}} <!-- 3️⃣将内容和对应的下标渲染在页面上。 -->
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
list: [ // 1️⃣data 中的 list 数组有五个值;
'Hello',
'qdywxs',
'I',
'am',
'Oli'
]
}
})
</script>
</body>
</html>
而在实际的项目开发中,使用 v-for
指令其实还有很多细节点需要我们注意。
1 使用 key
值提升性能
给每一个循环项上添加一个唯一的 key
值,可提升循环显示时的性能:
<div id="app">
<div v-for="(item, index) of list"
:key="index"
> <!-- ❗️在目前我们的代码中,好像 index 是唯一的值,我们绑定它为每一个循环项的 key 值! -->
{{item}} --> {{index}}
</div>
</div>
到页面查看效果,使用 index
作为 key
值好像也可以:
❌但使用 index
作为 key
值并不推荐!
因为使用 index
作为 key
值,在需要频繁对 DOM 元素相对应的数据进行操作时,它比较耗费性能,可能会导致 Vue 无法充分复用 DOM 节点。
❓如果不使用 index
作为 key
值,应该使用什么作为 key
值呢?
答:真实情况下后端向前端返回数据时,并不是如我们代码中的 list
这样写死的数据。
后端返回的数据,一般来说都会携带一个与数据库或跟后端相关的唯一的一个数据条目“标识符”:
<div id="app">
<!-- 4️⃣此时就可以用 id 作为每一项的唯一值; -->
<div v-for="(item, index) of list" :key="item.id">
{{item.text}} --> {{index}} <!-- 5️⃣数据 list 更改后,渲染的内容变为 item.text。 -->
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
// ❗️一般来说真实项目中获取到的多条数据会是这样:
list: [{
id: '0101101001', /*
1️⃣id 可能是数据库相对应的一个唯一的字段,而这个值就是这条数据
的唯一标识;
*/
text: 'Hello' // 2️⃣后端同时返回的数据内容;
}, {
id: '0101101002', // 3️⃣它们各自的标识符 id 不会是一致的;
text: 'qdywxs'
}, {
id: '0101101003',
text: 'Oli'
}]
}
})
</script>
使用数据自带的唯一标识符作为 key
值,它既是唯一的,也不是下标。这样就能保证我们 key
值的使用是正确的,性能上也最高。
返回页面查看效果,不会有任何问题:
2 改变数组内容且页面自动响应的方法
🚀需求:改变数组内容,能够让数据变化时页面也跟着变化!
2.1 方法①:使用数组的“变异方法”
之前我们向数组中添加内容时,是通过 push
方法添加的。
比如我们向 list
数组中添加内容 {id: '007', text: 'Dante'}
。通过在控制台输入 vm.list.push({id: '007', text: 'Dante'})
,来让页面自动响应新增的内容:
通过 push
方法来让页面自动响应新增的内容,就是使用了 Vue 中数组的 “变异方法”(即会改变调用这些方法的原始数组)。
Vue 提供的变异方法共有七个(效果与 ES3 数组方法一致,🔗前置知识《JavaScript 基础——JS 数组:① ES3 数组方法》):
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
我们再使用以上变异方法中的 splice()
替换一条数组内容 {id: '007', text: 'Dante'}
进行测试。
使用 splice 数组方法,它的第一个参数为要替换的内容的下标 2
,第二个参数我们是只替换一项 1
,第三个参数则是要更改的内容 {id: '007', text: 'Dante'}
:
❓还有别的方法吗? 当我们想用别的方法时,很自然可以想到利用数组的下标来更改内容。
页面的内容有三条,最后一条内容的下标为 2
,那我们新增的一条内容下标则为 3
。
打开控制台输入 vm.list[3] = {id: '007', text: 'Dante'}
验证一下利用下标更改内容的方法:
1️⃣利用下标新增内容后;
2️⃣list
数组中数据已更新,共有四条数据,新增的内容已在数组中;
3️⃣页面显示的内容并没有根据数据更新而更改。
❌在 Vue 中,不能通过下标的形式( vm.list[index]
)来修改数组并让页面自动响应。
2.2 方法②:改变数据的“引用地址”
🔗前置知识《③ JS 对象——理解对象》
list
对应的值是一个数组,数组在 JS 中是引用类型。如果我们把 list
的“引用”改变,页面也会跟着变。
刷新页面,还是让第三项 Oli
变为 {id: '007', text: 'Dante'}
。我们把数组的内容复制一份到控制台:
通过改变数组的“引用地址”来更改数据后,当数据发生改变,页面跟着发生变化:
2.3 方法③:使用 set
方法
有两种方式使用 set
方法:
- Vue 中**全局方法:
Vue.set (需要改变的“数据”, 定位下标, 要改变的具体值)
**; - Vue 中**实例方法:
vm.$set (需要改变的“数据”, 定位下标, 要改变的具体值)
**。
(❗️注: $set
实例方法,是全局方法 Vue.set
的别名!)
依然把第三项更改为 {id: '007', text: 'Dante'}
。
1️⃣使用全局方法 Vue.set
,第一个参数即要更改的数组 vm.list
,第二个参数的下标是 2
,第三个参数即要更改的内容 {id: '007', text: 'Dante'}
:
2️⃣
vm
是一个实例,实例上有 $set
方法。使用实例方法与使用全局方法一样:
🏆改变数组内容且页面自动响应的方法有三种:
- 使用“使用数组的“变异方法”;
- 改变数据的“引用地址”;
- 使用 Vue 的
set
方法。
3 template
占位符
🚀需求:让 **list**
数据的内容在页面上的两个标签中同时循环。
如果想让 list
在两个标签中循环,可能会想到直接在新的标签再循环一次:
<div id="app">
<div v-for="(item, index) of list" :key="item.id">
{{item.text}} --> {{index}}
</div>
<p v-for="(item, index) of list"
:key="item.id"> <!-- ❗️添加第二个标签 p,在 p 标签循环一次 list! -->
{{item.text}}
</p>
</div>
可当我们这样写完后,很明显能察觉循环两次有些不妥,于是会在 div 和 p 标签外再套一层 div,只循环一次:
<div id="app">
<!-- ❗️在 div 和 p 两个标签外套一层 div,循环一次! -->
<div v-for="(item, index) of list" :key="item.id">
<div>
{{item.text}} --> {{index}}
</div>
<p>{{item.text}}</p>
</div>
</div>
保存刷新网页查看效果,乍一看好像并没有什么不妥。但实际上,在我们所想显示的 div 和 p 标签外多包裹了一层 div:
❓如何让两个标签循环一次数据且没有多余标签?
答:使用 template
标签替换最外层的 div 标签。
<div id="app">
<!-- ❗️用 template 标签替换最外层的 div 标签! -->
<template v-for="(item, index) of list" :key="item.id">
<div>
{{item.text}} --> {{index}}
</div>
<p>{{item.text}}</p>
</template>
</div>
template
标签,可以理解为“模板占位符”,它可以帮助我们包裹一些元素,但在循环过程中并不会被渲染到页面上。
4 对“对象”循环遍历
Vue 中除了可以对数组循环,还可以对“对象”进行循环:
<div id="app">
<div v-for="(item, key, index) of userInfo"> <!-- 2️⃣循环 userInfo,把它所对应的
内容渲染在页面上,可接收三个参数;
2️⃣-①:item 为 userInfo 的键值;
2️⃣-②:key 为 userInfo 的键名;
2️⃣-③:index 为内容 item 的索引。
-->
{{item}} - {{key}} - {{index}}
</div>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
userInfo: { // 1️⃣定义一个 userInfo 对象,包含 name、age、gender 三个键名和对应的键值;
name: 'Oli',
age: '18',
gender: 'male'
}
}
})
</script>
当我们想更改对象的内容,并让页面自动响应时,也很简单。
比如更改 Oli
为 Oli Zhao
,可以直接通过 vm.userInfo.name = 'Oli Zhao'
来实现:
❓如何在增、减对象的内容更改数据时,让页面也跟着变化?
4.1 方法①:修改对象的“引用”
和数组一样,对象也可以通过修改“引用地址”来改变内容并让页面跟着发生改变。
我们向对象中新增一条内容 address: 'shenzhen'
:
4.2 方法②:使用 set
方法
对象也可以使用 set
方法:
- 全局方法:
Vue.set (需要改变的“数据”, 键, 值)
; - 实例方法:
vm.$set (需要改变的“数据”, 键, 值)
。
1️⃣使用全局方法向对象中新增内容 address: 'shenzhen'
:
2️⃣使用实例方法新增内容:
🏆增减对象内容且页面能自动响应的方法有两种:
- 改变对象的“引用地址”;
- 使用 Vue 的
set
方法。
祝好,qdywxs ♥ you!