74. vue之es6_列表渲染_数据双向绑定_事件处理
1. es6
1.1 key的引号可以省略
// 1 对象定义--原始方法
var person = {
'name': 'avril',
'age': 19
}
// es6中key的引号可以省略
var person2 = {
name: 'avril',
age: 19
1.2 修改对象中的值
[1] 修改对象中不可变类型的值时,对象的值不会发生改变
<script>
var name = 'kylian'
var age = 19
var person = {
name, /*将变量放到对象中*/
age
}
name = 'haaland'
/*consol.log运行代码直接在前端的控制台打印*/
console.log(name)
console.log(person)
</script>
name为不可变类型修改为了haaland,但是对象中的person未发生修改

[2] 修改对象中可变类型的值时,对象的值会随之发生改变
<script>
var name = 'kylian'
var age = 19
var player = {
name:"neymar",
hobby:"soccer"
}
var person = {
name, /*将变量放到对象中*/
age,
player /*将对象放到对象中*/
}
name = 'haaland'
player.hobby='swim'
/*consol.log运行代码直接在前端的控制台打印*/
console.log(name)
console.log(person)
</script>
person对象中的player对象为可变类型,修改player的值person的值随之改变

1.3 对象中放函数
方式一
<script>
var name = 'kylian'
var age = 19
var showName = function () {
console.log(this.name)
}
var person = {
name,
age,
showName
}
showName() /*this指代window对象*/
person.showName() /*调用函数*/ this指代person对象
</script>

直接调用showName()这个函数里面的this指代window对象
通过person对象调用showName(),showName()里面的this指代person对象
方式二:对象中的函数不需要写function
<script>
var name = 'avril'
var age = 20
var person = {
name,
age,
showName() {
console.log(this.name)
}
}
person.showName()
</script>
2. 指令系统
2.1 列表渲染v-for
[1] 概念
列表渲染指令:v-for,基于数组 / 对象循环渲染一组 DOM 元素。
(1) v-for循环数组
语法:v-for="(item, index) in arr" item:数组每一项
index:索引(可选)
推荐使用 of替代 in,更贴近 JS 迭代语法:v-for="(item, index) of list"
(2) v-for循环对象
v-for="(value, key, index) in obj"
value:属性值
key:属性名
index:索引(可选)
[2] 代码
bootstrap官网--V5--进入中文文档--下载--下载bootstrap生产文件
bootstrap V5不依赖jquery
(1) v-for循环数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物车</title>
<script src="vue2/vue.js"></script>
<link rel="stylesheet" href="bootstrap/bootstrap.css">
<script src="bootstrap/bootstrap.js"></script>
</head>
<body>
<div id="d1">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 offset-md-3">
<h1 class="text-center">购物车</h1>
<hr>
<table class="table table-striped"> <!--复制tables的striped rows-->
<thead>
<tr>
<th scope="col">商品id</th>
<th scope="col">商品名</th>
<th scope="col">商品价格</th>
<th scope="col">商品数量</th>
</tr>
</thead>
<tbody>
<tr v-for="item in goods"> <!--循环表体的每一行-->
<th scope="row">{{item.id}}</th> <!--插值语法-->
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.count}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
goods:[
{id:1001, name:'作业本', price:'2', count:6},
{id:1002, name:'书包', price:'60', count:1},
{id:1003, name:'图书', price:'15', count:6},
{id:1004, name:'笔', price:'2', count:10},
]
},
})
</script>
</html>

(2) 点击按钮加载购物车数据,否则显示为空
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>购物车</title>
<script src="vue2/vue.js"></script>
<link rel="stylesheet" href="bootstrap/bootstrap.css">
<script src="bootstrap/bootstrap.js"></script>
</head>
<body>
<div id="d1">
<div class="container-fluid">
<div class="row">
<div class="col-md-6 offset-md-3">
<h1 class="text-center">购物车</h1>
<div class="d-flex justify-content-center"> <!--div与class的作用是使按钮居中-->
<button class="btn btn-success" @click="handleLoad">加载购物车</button>
<button class="btn btn-info" @click="handleClear" style="margin-left: 20px">清空购物车</button>
</div>
<hr>
<p v-if="goods.length==0">购物车是空的</p> <!--v-if的情况-->
<!--v-else的情况-->
<table class="table table-striped" v-else> <!--复制tables的striped rows-->
<thead>
<tr>
<th scope="col">商品id</th>
<th scope="col">商品名</th>
<th scope="col">商品价格</th>
<th scope="col">商品数量</th>
</tr>
</thead>
<tbody>
<tr v-for="item in goods"> <!--循环表体的每一行-->
<th scope="row">{{item.id}}</th> <!--插值语法-->
<td>{{item.name}}</td>
<td>{{item.price}}</td>
<td>{{item.count}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
goods: []
},
methods: {
handleLoad() {
this.goods = [
{id: 1001, name: '作业本', price: '2', count: 6},
{id: 1002, name: '书包', price: '60', count: 1},
{id: 1003, name: '图书', price: '15', count: 6},
{id: 1004, name: '笔', price: '2', count: 10},
]
},
handleClear() {
this.goods = []
}
}
})
</script>
</html>

点击加载按钮

点击加载之后再点击清空按钮

(2) v-for循环字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p v-for="item in x">{{item}}</p>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
x: 'hello world'
},
})
</script>
</html>

(3) v-for循环数字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p v-for="item in 10">{{item}}</p>
</div>
</body>
<script>
/*循环数字也必须加上script*/
var a = new Vue({
el: '#d1',
})
</script>
</html>

(4) v-for循环对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p v-for="item in obj">{{item}}</p>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
obj:{name: 'avril', age: 19, hobby: 'sing'}
}
})
</script>
</html>
前端打印的是value值

同时循环k、v
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p v-for="(v, k) in obj">key值为:{{k}},value值为:{{v}}</p>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
obj: {name: 'avril', age: 19, hobby: 'sing'}
}
})
</script>
</html>

[3] key的作用(重点)
(1) 概念
:key必须绑定唯一标识,不要直接用 index 当 key(存在隐患)
作用:Vue 虚拟 DOM diff 算法,通过 key 识别节点,Vue diff 在更新列表时,默认就地复用 DOM
风险:数组增删、打乱顺序时,index 作为 key 会造成状态错位(输入框内容错乱等)
最佳实践:使用业务唯一 id(id、uuid)
如果没有 key,或者 key 用 index,数组顺序变化时 Vue 不会移动 DOM,只会复用已有 DOM 修改内容,导致元素内部状态错乱。
diff算法:vue中使用的是虚拟DOM,会和原生的DOM进行比较,然后进行数据的更新,提高数据的刷新速度
在v-for循环数组、对象时,建议在控件/组件/标签写1个key属性,属性值唯一
页面更新之后,会加速DOM的替换(渲染)
:key="变量"
(2) 代码
不写key或用index作为key会出现状态错乱
使用业务唯一 id 作为 key(正确写法)
<div v-for="item in list" :key="item.id">
{{ item.name }}
<input type="text">
</div>
[4] 数组的检测与更新
Vue 能够监听数组函数触发前端响应式更新
可以检测到的函数:
push:最后位置添加
pop:最后位置删除
shift:第一个位置删除
unshift:第一个位置添加
splice:切片
sort:排序
reverse:反转
不能检测到的函数:
filter:过滤
concat:追加另一个数组
slice
map
作者重写了相关函数(只重写了函数的一部分,还有另一部分没有重写)
数组或对象的值改了,但是页面没有变化:
使用Vue.set(对象,对象key,对象value)
使用Vue.set(数组,数组的下标,数组value)
解决方法:
// 方法1:通过 索引值 更新数组(数据会更新,但是页面不会发生改变)
vm.arrayList[0]
"Alan"
vm.arrayList[0]='Darker'
"Darker"
// 方法2:通过 Vue.set(对象, index/key, value) 更新数组(数据会更新,页面也会发生改变)
Vue.set(vm.arrayList, 0, 'Darker')
2.2 表单元素数据双向绑定
[1] 概念
v-model 是语法糖,用于表单元素双向数据绑定,本质是 :value + @input 的简写。
双向绑定:数据改变 → 视图更新;视图输入修改 → 同步更新数据。
v-model作用于输入型标签:
1. input 各类输入框,text、password、radio、checkbox 等
2. textarea 多行文本域
3. select 下拉选择框
[2] 代码
(1) 属性指定单向绑定数据
属性指令给标签绑定值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p>用户名:<input type="text" :value="username"></p> <!--通过属性指令获取data中的值-->
<p>密码:<input type="password"></p>
<p><input type="submit" value="登录" @click="handleSubmit"></p>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
username: 'avril', /*通过属性指令传值给input标签*/
password: ''
},
methods:{
handleSubmit(){
console.log(this.username)
}
}
})
</script>
</html>

变量改变,页面随之改变

页面发生变化,变量不会随之改变
数据是单向绑定

(2) v-model进行数据双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<p>用户名:<input type="text" v-model="username"></p> <!--v-model与data中的数据双向绑定-->
<p>密码:<input type="password"></p>
<p><input type="submit" value="登录" @click="handleSubmit"></p>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
username: 'avril', /*传值给input标签*/
password: ''
},
methods:{
handleSubmit(){
console.log(this.username)
}
}
})
</script>
</html>
变量改变,页面随之改变


页面改变,变量随之改变
数据是双向绑定

2.3 事件处理
[1] 概念
input标签的事件:
input 输入框输入内容,触发的事件 每输入一个字符就立刻触发
change 输入框发生变化,触发的事件 只有内容变更 + 失焦(文本框)才触发
blur 输入框失去焦点,触发的事件
focus 获得焦点,触发的事件
[2] 代码
(1) input事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<input type="text" v-model="aa" @input="handleInput"> <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定input事件-->
<br>
{{aa}} <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
aa: '', /*存储双向绑定的数据,供v-model和插值语法使用*/
},
methods: {
handleInput() {
console.log("检测到内容输入")
}
}
})
</script>
</html>
输入框每输入一个字母,都会触发一次input事件

(2) change事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<input type="text" v-model="aa" @change="handleChange"> <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定Change事件-->
<br>
{{aa}} <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
aa: '', /*存储双向绑定的数据,供v-model和插值语法使用*/
},
methods: {
handleChange() {
console.log("检测到内容变化")
}
}
})
</script>
</html>
只输入内容并且文本框聚焦时,不会触发change事件

当内容变化且文本框失去焦点时,会触发change事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<input type="text" v-model="aa" @change="handleChange"> <!--v-model实现输入框与data中的aa双向绑定,并且给标签绑定Change事件-->
<br>
{{aa}} <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
aa: '', /*存储双向绑定的数据,供v-model和插值语法使用*/
},
methods: {
handleChange() {
console.log("检测到内容变化")
}
}
})
</script>
</html>

(3) blur事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<input type="text" v-model="aa" @blur="handleBlur"> <!--v-model实现输入框与data中的aa双向绑定;给标签绑定blur事件-->
<br>
{{aa}} <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
aa: '', /*存储双向绑定的数据,供v-model和插值语法使用*/
},
methods: {
handleBlur() {
console.log("检测到失去焦点")
}
}
})
</script>
</html>
只要从聚焦到失焦,就会触发blur事件

(4) focus事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<input type="text" v-model="aa" @focus="handleFocus"> <!--v-model实现输入框与data中的aa双向绑定;给标签绑定focus事件-->
<br>
{{aa}} <!--插值表达式读取data中的aa,渲染展示到页面-->
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
aa: '', /*存储双向绑定的数据,供v-model和插值语法使用*/
},
methods: {
handleFocus() {
console.log("检测到聚焦")
}
}
})
</script>
</html>
只要从失焦到聚焦,就会触发focus事件

2.3 练习前补充
[1] 数组的筛选方法
匿名函数返回true,就保留这个字符串;返回false就不保留。
var a=['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
var res=a.filter(function (item){
if (item.length >=3){
return true
}else {
return false
}
})
console.log(res)
[2] indexOf判断字符串的包含
在目标字符串中查找字符串,返回首次匹配的起始索引;找不到返回 -1。
var a = 'avril'
var b = 'avril is beautiful'
if (b.indexOf(a) >= 0) { /*只要大于等于0就代表包含*/
console.log('在')
} else {
console.log('不在')
}
[3] _this
_this 不是 Vue 内置关键字,是开发者自定义变量,Vue 官方没有定义 _this。
作用:缓存当前组件实例 this,用于回调函数内部丢失 this 指向的场景,Vue2 选项式 API 使用最多,Vue3 setup 语法糖一般不用。
外层函数是 methods 的方法(普通 function):外层直接用 this,this 指向 Vue 实例。
里层函数如果是 普通 function:this 不再指向 Vue 实例,需要提前保存 var _this = this。
里层函数如果是 箭头函数:不需要 _this,直接继承外层的 this。
只区分里层函数是普通 function 还是箭头函数,无需关注是不是嵌套。
示例1 :里层普通 function → 需要 _this
methods: {
outer() {
// 外层:methods函数,this = Vue实例
console.log(this.name)
var _this = this // 在外层先存好Vue实例
function inner() {
// 里层普通function,this不是vue实例
console.log(_this.name) // 要用保存好的_this
}
inner()
}
}
示例 2:里层箭头函数 → 不需要 _this
methods: {
outer() {
console.log(this.name)
const inner = () => {
// 箭头函数不创建独立this,继承outer里的this
console.log(this.name)
}
inner()
}
}
示例 3:异步回调嵌套(最常用场景)
methods: {
fetch() {
// 外层 this → vue实例
var _this = this
setTimeout(function () {
// 第一层普通function回调,this丢失
_this.msg = '第一层'
setTimeout(function () {
// 第二层,依然使用同一个_this
_this.msg = '第二层'
}, 100)
},100)
}
}
改成箭头函数版本,直接省去 _this:
methods: {
fetch() {
setTimeout(() => {
this.msg = '第一层'
setTimeout(() => {
this.msg = '第二层'
},100)
},100)
}
}
2.4 练习
根据输入框输入的字符串来匹配包含该字符串的字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="vue2/vue.js"></script>
</head>
<body>
<div id="d1">
<!--v-model实现输入框与data中的msg双向绑定,msg为输入的值;给标签绑定focus事件,输入框聚焦再展示数据;-->
<!--绑定input事件,根据输入的数据动态筛选-->
<input type="text" v-model="msg" @focus="handleFocus" @input="handleInput">
<ul>
<li v-for="item in info_revise">{{item}}</li> <!--遍历修改后的数据-->
</ul>
</div>
</body>
<script>
var a = new Vue({
el: '#d1',
data: {
msg: '', /*存储双向绑定的数据,供v-model使用*/
/*初始值为空,聚焦再添加数据*/
info: ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf'], /*保存原始数据,使得输入字符串在退格时也能读取全部数据*/
info_revise: [], /*保存修改后的数据*/
},
methods: {
handleFocus() { /*聚焦才添加数据*/
/*如果不加if判断则每次搜索at筛选出两个字符串之后,重新聚焦会展示列表所有的字符串*/
/*只有在输入框为空时才展示所有数据,不为空则不作处理*/
if (this.info_revise.length==0){
this.info_revise = ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
}
},
handleInput() { /*根据输入的msg值动态的去info中筛选包含msg的字符串*/
var _this = this /*外层函数的里层普通函数需要_this*/
/*根据所有字符串去筛选输入的字符串*/
this.info_revise = this.info.filter(function (i) { /*i是每次遍历取得的字符串*/
if (i.indexOf(_this.msg)>=0) {
return true /*包含输入的字符串则保留*/
} else {
return false
}
})
}
}
})
</script>
</html>
输入'at',触发input事件运行
拿着输入的msg:"at",去info(包含所有值)中筛选
保留包含"at"的所有字符串,赋值给info_revise
退格时,触发input事件运行
拿着输入的msg:"...",去info(包含所有值)中筛选
保留包含"at"的所有字符串,赋值给info_revise
搜索

退格

箭头函数改写
<script>
var a = new Vue({
el: '#d1',
data: {
msg: '', /*存储双向绑定的数据,供v-model使用*/
/*初始值为空,聚焦再添加数据*/
info: ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf'], /*保存原始数据,使得输入字符串在退格时也能读取全部数据*/
info_revise: [], /*保存修改后的数据*/
},
methods: {
handleFocus() { /*聚焦才添加数据*/
/*如果不加if判断则每次搜索at筛选出两个字符串之后,重新聚焦会展示列表所有的字符串*/
/*只有在输入框为空时才展示所有数据,不为空则不作处理*/
if (this.info_revise.length==0){
this.info_revise = ['a', 'at', 'atom', 'atomic', 'be', 'beyond', 'cs', 'csrf']
}
},
handleInput() { /*根据输入的msg值动态的去info中筛选包含msg的字符串*/
var _this = this /*外层函数的里层普通函数需要_this*/
/*根据所有字符串去筛选输入的字符串*/
// this.info_revise = this.info.filter(function (i) { /*i是每次遍历取得的字符串*/
// if (i.indexOf(_this.msg)>=0) {
// return true /*包含输入的字符串则保留*/
// } else {
// return false
// }
// })
// 箭头函数一个参数有返回值:return 箭头之后的值
// 表达式>=0时return true,否则return false
this.info_revise = this.info.filter(i => i.indexOf(_this.msg)>=0)
}
}
})
</script>

浙公网安备 33010602011771号