03. Vue 指令拓展
3.1 指令修饰符
可以通过 .
来指明一些指令的后缀,不同的后缀中封装了不同的操作,可以帮助我们简化代码,比如之前使用过的监听 enter
键的弹起,我们需要操作事件对象,来检测用户使用了哪个键,但是 Vue 替我们封装了直接检测回车的方式,@keyup.enter
就是在我们上面的 v-on
语句后面再加一个 .
修饰。
下面来给出具体的代码的示例
<div id="app">
<h3>@keyup.enter → 监听键盘回车事件</h3>
<input @keyup.enter="fn" v-model="username" type="text">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: ''
},
methods: {
fn(e) {
if (e.key === "Enter") {
alert('回车');
}
}
}
})
</script>
这样就是实现了搜索框监听回车的操作
其他常用的修饰符比如 v-model.trim
去除空格,也就是绑定完后获取到的内容是去除空格的,v-model.number
会尝试将获取的信息转换为数组,如果无法转化为数字的,比如我们输入 abc
就会保留原本的字符串的形式
<div id="app">
<h3>v-model修饰符 .trim .number</h3>
姓名:<input v-model.trim="username" type="text"><br>
年纪:<input v-model.number="age" type="text"><br>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
age: '',
},
})
</script>
效果:
具体修改后的属性内容可以通过前面提到的 Vue 调试工具查看
另外的还有 @EVENT.stop
阻止事件冒泡和 @EVENT.prevent
阻止默认行为,比如表单的提交或者超链接的跳转行为
<!-- 阻止事件冒泡 -->
<h3>@事件名.stop → 阻止冒泡</h3>
<div @click="fatherFn" class="father">
<div @click.stop="sonFn" class="son">儿子</div>
<!-- 阻止默认行为 -->
<h3>@事件名.prevent → 阻止默认行为</h3>
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
3.2 v-bind 指令对于样式控制的增强
我们之前要控制 DOM 元素的样式是通过 ClassList.add( )
等方式实现对 DOM 元素类的操控,那通过 Vue 我么可以怎么实现呢?
语法::class="对象/数组"
对象:通过布尔值来判断是否将这个类名加到盒子上
适用于一个类名来回切换的情况,比如 TAB 栏高亮
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值}"></div>
数组:数组中的所有类都会加到盒子上,本质是一个 list
列表
适用于批量添加或者删除类的情况
<div class="box" :class="[ ‘类名1’, ‘类名2’, ‘类名3’]"></div>
来看一个实例
<div id="app">
<div class="box" :class="{pink: true, big: true}">黑马程序员</div>
<div class="box" :class="['pink']">黑马程序员</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
}
})
</script>
3.3 v-model 作用于其他的表单元素
常见的表单元素都可以通过 v-model
去关联,来快速的获取和设定表单元素的值,这里直接给出例子
<!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>Document</title>
<style>
textarea {
display: block;
width: 240px;
height: 100px;
margin: 10px 0;
}
</style>
</head>
<body>
<div id="app">
<h3>小黑学习网</h3>
姓名:
<input type="text" v-model="username">
<br><br>
是否单身:
<input type="checkbox" v-model="isSingle">
<br><br>
<!--
前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
性别:
<input type="radio" v-model="gender" value="男">男
<input type="radio" v-model="gender" value="女">女
<br><br>
<!--
前置理解:
1. option 需要设置 value 值,提交给后台
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
所在城市:
<select v-model="location">
<option>北京</option>
<option>上海</option>
<option>成都</option>
<option>南京</option>
</select>
<br><br>
自我描述:
<textarea v-model="text"></textarea>
<button>立即注册</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
isSingle: true,
gender: '男',
location: '上海',
text: ''
}
})
</script>
</body>
</html>
需要注意的是 v-model
会为单选框加上 name
属性,将其绑定为一组
04. 计算属性
4.1 基本使用
基于现有的数据,计算出来的新的属性,依赖数据的 变化,自动 重新计算
语法:声明在 computer
中,一个计算属性对应一个函数
使用起来和普通属性相同,比如插值表达式 {{ 计算属性 }}
<div id="app">
<h3>小黑的礼物清单</h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<!-- 目标:统计求和,求得礼物总数 -->
<p>礼物总数:{{totalCount}} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 1 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
computed: {
totalCount() {
// 求和
return this.list.reduce((sum, item) => sum + item.num, 0);
}
}
})
</script>
4.2 计算属性 VS Method
computed
计算属性:封装了对一段数据的处理求得一个结果,其具有缓存特性,即对计算出来的结果进行缓存,再次使用的时候就直接读取缓存,当其依赖项变化的时候会重新计算并且读取缓存。
methods
方法:如果把计算逻辑写在 methods
中,每次调用都需要重新计算,可以想象对性能有怎样的损耗
<div id="app">
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
<table>
<tr>
<th>名字</th>
<th>数量</th>
</tr>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.num }}个</td>
</tr>
</table>
<p>礼物总数:{{ totalCountFn() }} 个</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
// 现有的数据
list: [
{ id: 1, name: '篮球', num: 3 },
{ id: 2, name: '玩具', num: 2 },
{ id: 3, name: '铅笔', num: 5 },
]
},
methods: {
totalCountFn () {
console.log('methods方法执行了')
let total = this.list.reduce((sum, item) => sum + item.num, 0)
return total
}
},
computed: {
// 计算属性:有缓存的,一旦计算出来结果,就会立刻缓存
// 下一次读取 → 直接读缓存就行 → 性能特别高
// totalCount () {
// console.log('计算属性执行了')
// let total = this.list.reduce((sum, item) => sum + item.num, 0)
// return total
// }
}
})
</script>
上面每个 10
的计算都需要再次调用函数,所以对于这种情况应该使用 computed
4.3 完整写法
既然上面一直说这个
computed
里面的内容是属性,属性是可读写的,那计算属性是否是可读写的,应该如何写呢?
如果我们希望这个计算属性的改变会影响绑定的这一段数据,就需要自己编写逻辑去实现。
这时候就需要完整的配置对象了,其中的 get()
方法是我们读的操作,set
是写的操作,通过在内部去书写方法就可以实现对读写的配置。
<div id="app">
姓:<input type="text" v-model="firstName"> +
名:<input type="text" v-model="lastName"> =
<span>{{ fullName }}</span><br><br>
<button @click="changeName">改名卡</button>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: '刘',
lastName: '备',
},
methods: {
changeName () {
this.fullName = '黄忠'
}
},
computed: {
fullName: {
// (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)
// 会将返回值作为,求值的结果
get () {
return this.firstName + this.lastName
},
// (2) 当fullName计算属性,被修改赋值时,执行set
// 修改的值,传递给set方法的形参
set (value) {
// console.log(value.slice(0, 1))
// console.log(value.slice(1))
this.firstName = value.slice(0, 1)
this.lastName = value.slice(1)
}
}
}
})
</script>
当我们进行修改的时候,等号后面的值会作为 value
传给 set
函数,上面实现了通过修改姓名来修改绑定的名和姓的操作。
05. watch 监听器
5.1 基本使用
watch 监听器的可以实现对数据变化的监听,比如网页翻译的场景,我们在左边的框中输入需要翻译的文字后没有做任何操作但是右边的翻译的内容改变了,这就是因为监听了我们的输入操作。
语法:
const app = new Vue({
data: {
message: 'Hello'
},
watch: {
// 监听 message 属性的变化
message(newValue, oldValue) {
console.log(`message 从 ${oldValue} 变为 ${newValue}`);
// 在这里执行其他操作...
}
}
});
里面接收两个属性 newValue
和 oldValue
来存储更改前和更改后的值
watch
中可以监听多个属性
const app = new Vue({
data: {
firstName: 'John',
lastName: 'Doe',
fullName: ''
},
watch: {
// 监听 firstName 和 lastName 两个属性的变化
firstName(newFirstName, oldFirstName) {
this.fullName = newFirstName + ' ' + this.lastName;
// 在这里执行其他操作...
},
lastName(newLastName, oldLastName) {
this.fullName = this.firstName + ' ' + newLastName;
// 在这里执行其他操作...
}
}
});
5.2 完整写法
可以在watch
选项中添加配置项,这些配置项包括handler
和deep
。
handler
是一个函数,用于处理属性值变化时的逻辑。它接收两个参数,新值和旧值。deep
是一个布尔值,用于表示是否深度监听对象内部值的变化,默认为false
。
这里就需要写成配置对象,而不是上面的单个函数的形式,上面的函数写在对象的 handler()
函数中
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
count: 0,
user: {
name: 'John',
age: 30
}
},
watch: {
message: {
handler(newValue, oldValue) {
console.log(`message 从 ${oldValue} 变为 ${newValue}`);
// 在这里执行其他操作...
},
deep: true // 深度监听,如果message是对象,则也监听对象内部值的变化
},
count(newValue, oldValue) {
console.log(`count 从 ${oldValue} 变为 ${newValue}`);
// 在这里执行其他操作...
},
'user.name'(newValue, oldValue) { // 监听嵌套属性
console.log(`用户姓名从 ${oldValue} 变为 ${newValue}`);
// 在这里执行其他操作...
}
},
methods: {
increment() {
this.count++;
},
changeUserName() {
this.user.name = 'Alice'; // 改变嵌套属性,触发监听
}
}
});