① v-model指令的详细用法
1.v-model指令,绑定文本框的内容,实现双向数据绑定
<input type="text" v-model="name">
2.v-model指令,绑定多行文本框的内容,实现双向数据绑定
注意:在文本区域插值 (<textarea>{{text}}</textarea>) 并不会生效,应用 v-model 来代替。
<textarea cols="80" rows="4" v-model="address"></textarea>
3.单个复选框,通过v-model绑定一个布尔值 选中=>true 不选中=> false
<input type="checkbox" v-model="isOK">
4.多个复选框,通过v-model绑定到同一个数组
<input type="checkbox" value="打篮球" v-model="hobbies">打篮球
<input type="checkbox" value="弹琴" v-model="hobbies">弹琴
<input type="checkbox" value="唱歌" v-model="hobbies">唱歌
5.多个单选框,通过v-model绑定同一个属性
<input type="radio" value="男" name="sex" v-model="sex">男
<input type="radio" value="女" name="sex" v-model="sex">女
6. 通过v-model可以给下拉框绑定一个属性
<select v-model="education">
<option value="小学">小学</option>
<option value="硕士">硕士</option>
<option value="博士">博士</option>
<option value="博士后">博士后</option>
</select>
下拉框设置multiple属性后,就可以选择多个项 ,通过v-model可以给下拉框绑定一个数组
<select v-model="foods" multiple>
<option value="螃蟹">螃蟹</option>
<option value="龙虾">龙虾</option>
<option value="鸡腿">鸡腿</option>
<option value="牛排">牛排</option>
<option value="海鲜">海鲜</option>
</select>
7.v-model指令,添加.lazy修饰符,在文本框失去焦点后在更新数据
<input type="text" v-model.lazy="msg">
8. v-model指令,添加.number修饰符,在修改文本框内容时,会将修改后的内容转为
number类型
<input type="text" v-model.number="age">
9.v-model指令,添加.trim修饰符,在修改文本框内容时,会忽略前后的空格
<input type="text" v-model.trim="city">
② 事件修饰符
v-on:指令绑定事件,可以指定一个事件方法,事件方法要在methods里面定义。指定
事件方法时,如果没有给方法传递参数,默认会传递一个事件对象参数。
<button v-on:click="sayHi">Say Hi</button>
如果我们传递了一个参数,还想再传递事件对象参数,就要通过$event关键字设置。
<button v-on:click="sayHello('你好',$event)">Say Hello</button>
如果事件处理的逻辑比较简单,可以直接在行内编写。
<button v-on:click="name+='*'">修改name</button>
1.通过.prevent事件修饰符,阻止默认行为
2.通过.stop事件修饰符,阻止事件冒泡
<div class="a" @click="a" @contextmenu.prevent="cm">
<div class="b" @click.stop="b"></div>
</div>
3.通过.once事件修饰符,让事件方法只执行一次
<button @click.once="once">只触发一次</button>
4.通过.self事件修饰符,控制事件在当前元素自身触发,不在内部元素身上触发
<div class="c" @click.self="c">
<div class="d"></div>
</div>
5.默认情况下,手机的捕获模式是,从内部往外部挨个执行。 如果外部事件添
加.capture修饰符,此时事件的捕获模式就变成了,从外部到内部挨个执行。
<div class="e" @click.capture="e">
<div class="f" @click="f"></div>
</div>
注意:修饰符可以串联。总结:使用修饰符时,顺序很重要。因此,用 v-on:click.prevent.self 会
阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
③ 按键修饰符
Vue针对键盘事件,提供了按键修饰符。一共有9个按键修饰符,分别是:
.enter 是回车键
.tab 是tab键
.delete 是删除键和退格键
.esc 是退出键
.space 是空格键
.up 是上箭头
.down 是下箭头
.left 是左箭头
.right 是右箭头
按键修饰符,也可以用按键码代替,注意:Vue3中取消了按键码
<input type="text" v-model="keywords" @keyup.13="keydown">
④ 深度响应式
Vue实例,在初始化的时候,会将对象身上的所有数据,做响应式处理,
之后再向对象中添加属性,这些属性就不再具备响应式能力了。
针对数组,只能通过以下方法,才能实现响应式:
push() pop() unshift() shift() splice() reverse() sort()
<button @click="arr.push('可乐')">在末尾添加可乐</button>
<button @click="arr.pop()">删除末尾元素</button>
<button @click="arr.unshift('菠萝')">在开头添加菠萝</button>
<button @click="arr.shift()">删除开始元素</button>
<button @click="arr.splice(1,1,'榴莲')">通过方法修改元素</button>
<button @click="arr.splice(1,2)">删除元素</button>
<button @click="arr.reverse()">数组元素反转</button>
<button @click="arr1.sort((a,b)=>a-b)">元素排序</button>
如何解决上面的问题?
方式1:通过Vue的set方法,更新指定的对象属性或数组成员;delete方法,删除指定对象的属性或数组的成员
方式2:通过Vue实例的$set方法,更新指定的对象属性或数组成员;$delete方法,删除指定对象的属性或数组的成员
methods: {
// 给对象添加工作属性的方法
addJob() {
// 通过观察可以发现,我们可以给对象添加属性,但是添加后的属性,不具备响应式能力。
// this.obj.job='前端开发工程师'
// set方法的参数分别是:指定的对象,对象的属性,属性值
// Vue.set(this.obj,'job','前端开发工程师')
this.$set(this.obj, 'job', '前端开发工程师')
},
// 删除对象身上年龄的方法
delAge() {
// delete this.obj.age
// delete方法的参数分别是:指定的对象,对象的属性
// Vue.delete(this.obj,'age')
this.$delete(this.obj, 'age')
},
// 修改数组身上的成员
updateArr() {
// this.arr[1] = '苹果'
// 这里set方法的参数分别是:指定的数组,数组的下标,对应的数据
this.$set(this.arr, 1, '苹果')
},
// 根据下标删除数组元素
delArr() {
// delete this.arr[1]
// 这里的delete方法的参数分别是:指定的数组,数组的下标
this.$delete(this.arr, 3)
}
},
⑤ 购物车案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>购物车</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
text-decoration: none;
outline: none;
}
#app {
width: 700px;
}
table {
border-collapse: collapse;
}
th,
td {
padding: 5px 10px;
border: 1px solid #ccc;
}
img {
width: 80px;
}
td .btn {
padding: 5px 14px;
border: none;
border-radius: 5px;
cursor: pointer;
}
td .btn:hover {
background-color: #ea13ad;
color: white;
}
td .count {
width: 50px;
padding: 5px;
text-align: center;
border: none;
}
td .del {
cursor: pointer;
padding: 5px 10px;
border: none;
border-radius: 5px;
}
td .del:hover {
background-color: #ea13ad;
color: white;
}
</style>
</head>
<body>
<div id="app">
<!-- 如果数组中有数据,显示购物车 -->
<table v-if="goodses.length>0">
<tr>
<th><input type="checkbox" v-model="isckAll" />全选</th>
<th>商品名称</th>
<th>商品图片</th>
<th>商品单价</th>
<th>购买数量</th>
<th>小计</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in goodses" :key="item.id">
<td><input type="checkbox" v-model="item.isck" /></td>
<td>{{item.name}}</td>
<td>
<img :src="item.img" />
</td>
<td>¥{{item.price | toFixed2}}</td>
<td>
<!-- :disabled绑定的值为true,按钮禁用 -->
<button @click="item.count--" :disabled="item.count===1" class="btn">-</button>
<input readonly type="text" :value="item.count" class="count" />
<button @click="item.count++" :disabled="item.count===10" class="btn">+</button>
</td>
<td>¥{{item.price*item.count | toFixed2}}</td>
<td>
<button @click="delGoods(index)" class="del">删除</button>
</td>
</tr>
<tr>
<td colspan="7" class="totalPrice">
<span>总计:¥{{totalPrice | toFixed2}}</span>
<!-- 过滤器可以链式调用 -->
<span style="color: red">${{totalPrice | toUS | toFixed2}}</span>
</td>
</tr>
</table>
<!-- 否则显示下面的div -->
<div class="empty" v-else>您的购物车空空如也</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
<script>
new Vue({
el: "#app",
// 数据
data: {
// 商品数组
goodses: [
{
// 商品编号
id: "1001",
// 商品名称
name: "小米手机",
// 商品图片
img: "https://img.alicdn.com/bao/uploaded/i3/2279837698/O1CN01gkdsUP26jjYlI8HCS_!!2279837698.jpg",
// 商品单价
price: 1999,
// 购买数量
count: 3,
// 是否选中
isck: false,
},
{
id: "1002",
name: "西门子冰箱",
img: "https://img.alicdn.com/bao/uploaded/i4/2347095319/O1CN01xhxce31pA9MmYjHPc_!!2347095319.jpg",
price: 3999,
count: 2,
isck: true,
},
{
id: "1003",
name: "索尼电视",
img: "https://img.alicdn.com/bao/uploaded/i1/782731205/O1CN01o18KOx1KlvvaEIosx_!!0-item_pic.jpg",
price: 4999,
count: 1,
isck: true,
},
{
id: "1004",
name: "联想电脑",
img: "https://img.alicdn.com/bao/uploaded/i2/459462135/O1CN01yN7bD91RdsIyoddVW_!!459462135.jpg",
price: 5999,
count: 4,
isck: false,
},
],
},
// 方法
methods: {
delGoods(index) {
if (confirm("是否确定删除?")) {
this.goodses.splice(index, 1);
}
},
},
// 计算属性
computed: {
// 表示是否全选
isckAll: {
// 返回结果
get() {
// 商品数组中所有的商品的状态为true时,返回true
return (
this.goodses.length > 0 && this.goodses.every((g) => g.isck)
);
},
// 修改结果
set(val) {
// 循环所有的商品,设置所有商品的状态为最新的全选状态
this.goodses.forEach((g) => {
g.isck = val;
});
},
},
// 表示总价
totalPrice() {
/* let total = 0
for(let i=0;i<this.goodses.length;i++){
if(this.goodses[i].isck){
total += this.goodses[i].count * this.goodses[i].price
}
}
return total */
let total = 0;
this.goodses.forEach((g) => {
if (g.isck) {
total += g.price * g.count;
}
});
return total;
/* return this.goodses.filter(g=>g.isck).map(g=>g.price*g.count).reduce((c,g)=>{
return c + g
},0) */
},
},
// 过滤器
filters: {
// 数字保留两位小数
toFixed2(val) {
return val.toFixed(2);
},
// 转美金的方法
toUS(val) {
return val / 6.444;
},
},
// 监听器
watch: {
// 监听totalPrice计算属性的值的变化
totalPrice(val) {
if (val >= 10000) {
alert("请理性消费!");
}
},
},
});
</script>
</body>
</html>