Vue2二、指令补充,computed 计算属性vs方法,watch 侦听器,
一、指令补充
1.修饰符。2.动态操作class。3.动态操作style。4.v-model 用于其他表单元素
1.修饰符
① 按键修饰符
@keyup.enter → 键盘回车监听
<body>
<div id="app">
<h3>@keyup.enter → 监听键盘回车事件</h3>
<input v-model="username" type="text" @keyup.enter="fn">
</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() {
//
// if (e.key === 'Enter') {
// console.log('按下回车了')
// }
console.log('Vue');
}
}
})
</script>
</body>
② v-model修饰符
v-model.trim
→
去除首尾空格 。
v-model.number
→
转数字
③ 事件修饰符
@事件名
.
stop
→
阻止冒泡 。@事件名
.
prevent
→
阻止默认行为
<!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>
.father {
width: 200px;
height: 200px;
background-color: pink;
margin-top: 20px;
}
.son {
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>
</head>
<body>
<div id="app">
<h3>v-model修饰符 .trim .number</h3>
<!-- v-model作用: 给 表单元素 使用, 双向数据绑定 → 可以快速 获取 或 设置 表单元素内容 -->
<!--.trim去首尾空格 -->
姓名:<input v-model.trim="username" type="text"><br>
<!-- .number转数字类型 -->
年纪:<input v-model.number="age" type="text"><br>
<h3>@事件名.stop → 阻止冒泡</h3>
<div @click="fatherFn" class="father">
<!-- .stop阻止冒泡 -->
<div @click.stop="sonFn" class="son">儿子</div>
</div>
<h3>@事件名.prevent → 阻止默认行为</h3>
<!-- .prevent阻止默认行为-超链接的跳转 -->
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
age: '',
},
methods: {
fatherFn() {
alert('老父亲被点击了')
},
sonFn(e) {
// e.stopPropagation()
alert('儿子被点击了')
}
}
})
</script>
</body>
</html>
2.动态操作class
语法:class="对象/数组"
① 对象 →键就是类名,值是布尔值。如果值为 true,有这个类,否则没有这个类
适用场景:一个类名,来回切换
<div class="box":class="{类名1:布尔值,类名2:布尔值}"></div>
② 数组→数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
适用场景:批量添加或删除类
<div class="box" :class="[ 类名1, 类名2,类名3 ]"></div>
//
:class="['pink', 'big']"
<!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>
.box {
width: 200px;
height: 200px;
border: 3px solid #000;
font-size: 30px;
margin-top: 10px;
}
.pink {
background-color: pink;
}
.big {
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<!-- v-bind 对于样式控制的增强 - 操作class -->
<!-- 语法:class="对象/数组" -->
<!-- ①对象→键就是类名,值是布尔值。如果值为true,有这个类,否则没有这个类,适用场景:一个类名,来回切换 -->
<!-- ②数组→数组中所有的类,都会添加到盒子上,本质就是一个class 列表,适用场景:批量添加或删除类-->
<div id="app">
<div class="box" :class="{pink:flag1,big:flag2}">程序员</div>
<div class="box" :class="['pink','big']">程序员</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag1: false,
flag2: true
}
})
</script>
</body>
</html>
例:tab栏高亮
<!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>
* {
margin: 0;
padding: 0;
}
ul {
display: flex;
border-bottom: 2px solid #e01222;
padding: 0 10px;
}
li {
width: 100px;
height: 50px;
line-height: 50px;
list-style: none;
text-align: center;
}
li a {
display: block;
text-decoration: none;
font-weight: bold;
color: #333333;
}
li a.active {
background-color: #e01222;
color: #fff;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id" @click="num=index">
<a :class="{active:num === index}" href="#">{{item.name}}</a></li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num: 2,
list: [{
id: 1,
name: '京东秒杀'
}, {
id: 2,
name: '每日特价'
}, {
id: 3,
name: '品类秒杀'
}]
}
})
</script>
</body>
</html>
3.动态操作style
语法:style ="样式对象"
适用场景:某个具体属性的动态设置
<div class="box":style="{CSS属性名1:CSS属性值,CSS属性名2:CSS属性值 }"></div>
<!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>
.box {
width: 200px;
height: 200px;
background-color: rgb(187, 150, 156);
}
</style>
</head>
<body>
<div id="app">
<!-- 带中横线的css属性要变小驼峰 -->
<div class="box" :style="{width:num1, height:num2+'px', backgroundColor:mycolor}"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: '300px',
mun2: 300,
mycolor: "green"
}
})
</script>
</body>
</html>
例:进度条
<!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>
.progress {
height: 25px;
width: 400px;
border-radius: 15px;
background-color: #272425;
border: 3px solid #272425;
box-sizing: border-box;
margin-bottom: 30px;
}
.inner {
width: 50%;
height: 20px;
border-radius: 10px;
text-align: right;
position: relative;
background-color: #409eff;
background-size: 20px 20px;
box-sizing: border-box;
transition: all 1s;
}
.inner span {
position: absolute;
right: -20px;
bottom: -25px;
}
</style>
</head>
<body>
<div id="app">
<div class="progress">
<div class="inner" :style="{width:num+'%' }">
<span>{{num}}%</span>
</div>
</div>
<button @click="num=25">设置25%</button>
<button @click="num=50">设置50%</button>
<button @click="num=75">设置75%</button>
<button @click="num=100">设置100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num: 10
}
})
</script>
</body>
</html>
4.v-model 用于其他表单元素
输入框 input:text | value |
文本域 textarea | checked |
复选框 input:checkbox | value |
单选框 input:radio | value |
下拉菜单 select | checked |
<!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">
<br><br> 是否单身:
<input type="checkbox" v-model="isSLingle">
<br><br>
<!--
前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
性别:
<!-- -model绑定了数据→找到对应value值的标签添加checked属性 -->
<input type="radio" name="gender" v-model="gender" value="0">男
<input type="radio" name="gender" v-model="gender" value="1">女
<br><br>
<!--
前置理解:
1. option 需要设置 value 值,提交给后台
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
所在城市:
<select v-model="selVal">
<option value="bj" >北京</option>
<option value="sh" >上海</option>
<option value="cd" >成都</option>
<option value="nj" >南京</option>
</select>
<br><br> 自我描述:
<textarea></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: '',
isSLingle: true,
gender: 0,
selVal: 'nj'
}
})
</script>
</body>
</html>
二、computed 计算属性
概念:基于现有的数据,计算出来的新属性。 依赖的数据变化,自动重新计算。
计算属性默认的简写,只能读取访问,
不能 "修改"
。
如果要
"修改"
→ 需要写计算属性的
完整写法
。
例1小黑的礼物清单
<!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>
table {
border: 1px solid #000;
text-align: center;
width: 240px;
}
th,
td {
border: 1px solid #000;
}
h3 {
position: relative;
}
</style>
</head>
<body>
<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>礼物总数:{{totalNum}} 个</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: {
totalNum() {
// return累加的结果
// return xx(前一次累加的结果,这一次要累加的结果)=>{}
return this.list.reduce((sum, item) => {
return sum += item.num
}, 0)
}
}
})
</script>
</body>
</html>
例2修改计算属性
<!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>
</head>
<body>
<div id="app">
姓:<input type="text" v-model="name1"><br> 名: <input type="text" v-model="name2"><br>
<p>姓名:{{nameFn}}</p>
<button @click='clickfn()'>修改姓名</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name1: '',
name2: '',
},
computed: {
nameFn: {
//读取-依赖别的数据计算结果
get() {
return this.name1 + this.name2
},
// 修改-如果真的修改了计算属性的结果
//set这里的形参能自动接收到修改后的结果
set(value) {
// 两个输入框的值修改-小明
//[0,1]
this.name1 = value.substring(0, 1)
this.name2 = value.substring(1)
}
}
},
methods: {
clickfn() {
// 修改计算属性得到的名字
this.nameFn = '小明'
}
}
})
</script>
</body>
</html>
1.计算属性 vs 方法
<!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>
table {
border: 1px solid #000;
text-align: center;
width: 300px;
}
th,
td {
border: 1px solid #000;
}
h3 {
position: relative;
}
span {
position: absolute;
left: 145px;
top: -4px;
width: 16px;
height: 16px;
color: white;
font-size: 12px;
text-align: center;
border-radius: 50%;
background-color: #e63f32;
}
</style>
</head>
<body>
<div id="app">
<!-- <h3>小黑的礼物清单🛒<span>{{totalCount}}</span></h3> -->
<h3>小黑的礼物清单🛒<span>{{fn()}}</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>礼物总数:{{ totalCount }} 个</p> -->
<p>礼物总数:{{fn() }} 个</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
}, ]
},
// computed: {
// totalCount() {
// console.log('我是计算属性');
// let total = this.list.reduce((sum, item) => sum + item.num, 0)
// return total
// }
// },
methods: {
fn() {
console.log('我是普通函数');
let total = this.list.reduce((sum, item) => sum + item.num, 0)
return total
}
}
})
</script>
</body>
</html>
2.例:综合案例 - 成绩案例
需求说明:
1. 渲染功能:
v-if v-else
v-for
v-bind:class
2. 删除功能:点击传参 filter过滤覆盖原数组。
.prevent 阻止默认行为
3. 添加功能:v-model
v-model修饰符(.trim .number)。
unshift 修改数组更新视图
4. 统计总分,求平均分:
计算属性
reduce求和
<!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" />
<link rel="stylesheet" href="./styles/index.css" />
<title>Document</title>
</head>
<body>
<div id="app" class="score-case">
<div class="table">
<table>
<thead>
<tr>
<th>编号</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody v-if="list.length">
<tr v-for="(item,index) in list" :key="item.id">
<td>{{index+1}}</td>
<td>{{item.subject}}</td>
<!-- v-bind 动态的设置html的标签属性 → src url title... -->
<!-- 2语法: v-bind:属性名="表达式"
3. 注意: 简写形式 :属性名="表达式" -->
<td :class="{red:item.score<60}">{{item.score}}</td>
<td><a @click="del(item.id)" href="#">删除</a></td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="5">
<span class="none">暂无数据</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<span>总分:{{ totalScore}}</span>
<span style="margin-left: 50px">平均分:{{avgScore}}</span>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="form">
<div class="form-item">
<div class="label">科目:</div>
<div class="input">
<input type="text" placeholder="请输入科目" v-model="subject" />
</div>
</div>
<div class="form-item">
<div class="label">分数:</div>
<div class="input">
<input type="text" placeholder="请输入分数" v-model.number="score" />
</div>
</div>
<div class="form-item">
<div class="label"></div>
<div class="input">
<button class="submit" @click="add()">添加</button>
</div>
</div>
</div>
</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,
subject: '语文',
score: 20
}, {
id: 7,
subject: '数学',
score: 99
}, {
id: 12,
subject: '英语',
score: 70
}, ],
subject: '',
score: ''
},
methods: {
//删除
del(ids) {
this.list = this.list.filter(item => item.id != ids)
},
//添加
add() {
if (this.subject.trim() === '' || this.score === '') {
alert('数据不能为空')
return
}
this.list.unshift({
id: +new Date(),
subject: this.subject,
score: this.score
})
this.subject = ''
this.score = ''
}
},
computed: {
// 总分
totalScore() {
return this.list.reduce((sum, item) => sum + item.score, 0)
},
//平均分
avgScore() {
return this.totalScore / this.list.length
}
}
})
</script>
</body>
</html>
三、watch 侦听器
作用:
监视数据变化
,执行一些 业务逻辑 或 异步操作。
① 简单写法
监视简单类型的变化
watch: {
数据属性名 (newValue, oldValue) {
一些业务逻辑 或 异步操作。
},
'对象.属性名' (newValue, oldValue) {
一些业务逻辑 或 异步操作。
}
}
② 完整写法
添加额外的配置项 (深度监视复杂类型,立刻执行)
watch: {// watch 完整写法
数据属性名: {
deep: true, // 深度监视(针对复杂类型)
immediate: true, // 是否立刻执行一次handler
handler (newValue) {
console.log(newValue)
}
}
}
例:翻译语言
需求:输入内容,
修改语言
,都实时翻译
<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 18px;
}
#app {
padding: 10px 20px;
}
.query {
margin: 10px 0;
}
.box {
display: flex;
}
textarea {
width: 300px;
height: 160px;
font-size: 18px;
border: 1px solid #dedede;
outline: none;
resize: none;
padding: 10px;
}
textarea:hover {
border: 1px solid #1589f5;
}
.transbox {
width: 300px;
height: 160px;
background-color: #f0f0f0;
padding: 10px;
border: none;
}
.tip-box {
width: 300px;
height: 25px;
line-height: 25px;
display: flex;
}
.tip-box span {
flex: 1;
text-align: center;
}
.query span {
font-size: 18px;
}
.input-wrap {
position: relative;
}
.input-wrap span {
position: absolute;
right: 15px;
bottom: 15px;
font-size: 12px;
}
.input-wrap i {
font-size: 20px;
font-style: normal;
}
</style>
</head>
<body>
<div id="app">
<!-- 条件选择框 -->
<div class="query">
<span>翻译成的语言:</span>
<select v-model="obj.lang">
<option value="italy">意大利</option>
<option value="english">英语</option>
<option value="german">德语</option>
</select>
</div>
<!-- 翻译框 -->
<div class="box">
<div class="input-wrap">
<textarea v-model="obj.words"></textarea>
<span><i>⌨️</i>文档翻译</span>
</div>
<div class="output-wrap">
<div class="transbox">{{txt}}</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
// 接口地址:https://applet-base-api-t.itheima.net/api/translate
// 请求方式:get
// 请求参数:
// (1)words:需要被翻译的文本(必传)
// (2)lang: 需要被翻译成的语言(可选)默认值-意大利
// -----------------------------------------------
const app = new Vue({
el: '#app',
data: {
words: '',
txt: '',
obj: {
words: '',
// timer: ''
lang: 'italy'
}
},
// 具体讲解:(1) watch语法 (2) 具体业务实现
watch: {
// words(新值,老值){
// words(newval, oldval) {
// console.log(`数据变化了`);
// console.log(newval, oldval)
// }
// -------------------------------------------------------------------
obj: {
// 复杂类型深度监听·→里面的属性变化也能监听到
deep: true,
// 打开页面,默认调用一次handler函数
// immediate: true,
// 监听到数据变化执行的代码·-·handler
handler(newval) {
// console.log(`obj变化了`, newval);
clearTimeout(this.timer) //this.timer→timer 也是挂载到了Vue实例上,只不过是普通变量,不是响应式数据
this.timer = setTimeout(async() => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: {
words: this.obj.words,
lang: this.obj.lang
}
})
//
this.txt = res.data.data
}, 500)
}
}
// ---------------------------------------------------------------------
// 属性名要加引号
// 'obj.words' (newval) {
// // console.log(`name变化了`)
// console.log(newval)
// clearTimeout(this.timer) //this.timer→timer 也是挂载到了Vue实例上,只不过是普通变量,不是响应式数据
// this.timer = setTimeout(async() => {
// const res = await axios({
// url: 'https://applet-base-api-t.itheima.net/api/translate',
// params: {
// words: this.obj.words
// }
// })
// //
// this.txt = res.data.data
// }, 500)
// }
}
})
</script>
</body>
</html>
综合案例 - 水果购物车
需求说明:
1. 渲染功能:
v-if/v-else v-for :class
2. 删除功能:
点击传参 filter
过滤覆盖原数组
3. 修改个数:
点击传参 find
找对象
4. 全选反选:计算属性
computed
完整写法
get/set
5. 统计
选中的
总价和总数量: 计算属性
computed reduce
条件求和
6. 持久化到本地:
watch
监视,
localStorage
,
JSON.stringify
,
JSON.parse