一、指令补充
1. 指令修饰符(简化代码)
通过 "." 指明一些指令后缀,不同后缀封装了不同的处理操作
a. 按键修饰符
@keyup.enter 键盘监听回车 @keyup.enter="addList"
b. v-model 修饰符
v-model.trim 去除首尾空格
v-model.number 转数字 不会强制转换(不会把字符转成undefined)
c. 事件修饰符
@事件名.stop 阻止冒泡
@事件名.prevent 阻止默认行为
2. v-bind 对于样式操作的加强
针对 class 类名,style 行内样式 进行控制
操作 class
语法::class = "对象 / 数组"
对象
// 值为 true,有这个类,为 false,无这个类 适用于一个类名来回切换 <div class="box" :class="{类名1: true, 类名2: false}"></div>
数组
// 所有的类都会被添加上 适用于批量添加或删除类 <div class="box" :class="[类名1, 类名2]"></div>
京东秒杀 tab 导航高亮
<!-- 切换高亮,就是改下标 -->
<div id="app">
<ul>
<li v-for="(item, index) in list" :key="item.id" @click="activeIndex = index">
<a :class="{active: index === activeIndex}" 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: {
activeIndex: 0, // 记录高亮
list: [
{ id: 1, name: '京东秒杀' },
{ id: 2, name: '每日特价' },
{ id: 3, name: '品类秒杀' }
]
}
})
</script>
操作 style
使用场景:某个具体属性的动态设置
语法::style = "对象"
<div class="box" :style="{width: '200px', heigth: '200px'}"></div>
进度条
<div id="app">
<!-- 外层盒子 黑色底色 -->
<div class="progress">
<!-- 内层盒子 蓝色底色 -->
<div class="inner" :style="{width: percent + '%'}">
<span>{{ percent }}%</span>
</div>
</div>
<button @click="percent = 25">设置25%</button>
<button @click="percent = 50">设置50%</button>
<button @click="percent = 75">设置75%</button>
<button @click="percent = 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: {
percent: 30
}
})
</script>
3. v-model 应用于其他表单元素
常见的表单元素都可以用 v-model 绑定关联,快速获取和设置表单的值
它会根据控件类型自动选取正确的方法来更新元素
输入框 input:text value
文本域 textarea value
复选框 input:checkbox checked
单选框 input:radio checked 给两个单选框加上同一个name属性,会互斥
下拉菜单 select value
二、computed 计算属性
作用:依赖的数据变化,自动重新计算
1. 基础语法(只能读取访问,不能修改)
computed: {
属性名() {
// 计算逻辑
return 结果
}
}
使用:{{属性名}} 和普通属性一样使用
<!-- 目标:统计求和,求得礼物总数 -->
<p>礼物总数:{{total}} 个</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: {
total() {
return this.list.reduce((sum, item) => sum += item.num, 0)
}
}
})
</script>
2. 计算属性 vs 方法
computed 计算属性
作用:封装了一段对于数据的处理,求得一个结果
作为属性,直接使用 this.计算属性 {{ 计算属性 }}
计算属性缓存特性(提升性能):
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算,并再次缓存
methods 方法
作用:给实例提供一个方法,调用处理业务逻辑
作为方法,需要调用 this.方法名() {{ 方法名() }} @事件名 = '方法名'
3. 完整写法(能修改)
computed: {
属性名: {
get() {
// 计算逻辑
return 结果
}
set(修改的值) {
// 修改逻辑
}
}
}
改名
<div id="app">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
<p>姓名:{{fullName}}</p>
<button @click="changeName">修改姓名</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: '孙',
lastName: '悟空'
},
computed: {
fullName: {
get() {
return this.firstName + this.lastName
},
set(value) {
// console.log(value) // 曹雪芹
this.firstName = value.slice(0, 1)
this.lastName = value.slice(1)
}
}
},
methods: {
changeName() {
this.fullName = '曹雪芹'
}
}
})
</script>
4. 成绩案例
三、wantch 侦听器
作用:监视数据变化,执行一些 业务逻辑 或 异步操作
1. 基础语法
data: {
words: '苹果',
// obj: {
// words: '苹果'
// }
}
watch: {
words(newValue, oldValue) {
// 业务逻辑
},
// 'obj.words'(newValue, oldValue) {
// // 业务逻辑
// }
}
翻译框
<!-- 翻译框 -->
<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">{{result}}</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: {
obj: {
words: '',
},
result: '' // 翻译结果
},
watch: {
'obj.words'(newValue) {
// 防抖
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: {
words: newValue
}
})
// console.log(res.data.data)
this.result = res.data.data
}, 300)
}
}
})
</script>
2. 完整写法
添加额外配置项
deep: true 对复杂类型深度监视
immediate: true 初始化立刻执行一次handler方法
<!-- 翻译框 -->
<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">{{result}}</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>
const app = new Vue({
el: '#app',
data: {
obj: {
words: '',
lang: 'italy'
},
result: ''
},
watch: {
obj: {
deep: true,
handler(newValue) {
clearTimeout(this.timer)
this.timer = setTimeout(async () => {
const res = await axios({
url: 'https://applet-base-api-t.itheima.net/api/translate',
params: newValue
})
this.result = res.data.data
}, 300)
}
}
}
})
四、综合案例:水果购物车
- 渲染
- 删除
- 修改个数
- 全选反选
- 统计选中的总价和总数量
- 持久化到本地
<!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="./css/inputnumber.css" />
<link rel="stylesheet" href="./css/index.css" />
<title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
<!-- 顶部banner -->
<div class="banner-box"><img src='../images/01.jpg' alt="" /></div>
<!-- 面包屑 -->
<div class="breadcrumb">
<span>🏠</span>/<span>购物车</span>
</div>
<!-- 购物车主体 -->
<div class="main">
<div class="table">
<!-- 头部 -->
<div class="thead">
<div class="tr">
<div class="th">选中</div>
<div class="th th-pic">图片</div>
<div class="th">单价</div>
<div class="th num-th">个数</div>
<div class="th">小计</div>
<div class="th">操作</div>
</div>
</div>
<!-- 身体 -->
<div class="tbody">
<div class="tr" :class="{active: item.isChecked}" v-for="(item,index) in fruitList" :key="item.id">
<div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
<div class="td"><img :src="item.icon" alt="" /></div>
<div class="td">{{item.price}}</div>
<div class="td">
<div class="my-input-number">
<button class="decrease" :disabled="item.num <= 1" @click="sub(item.id)"> - </button>
<span class="my-input__inner">{{item.num}}</span>
<button class="increase" @click="add(item.id)"> + </button>
</div>
</div>
<div class="td">{{item.num * item.price}}</div>
<div class="td"><button @click="del(item.id)">删除</button></div>
</div>
</div>
</div>
<!-- 底部 -->
<div class="bottom">
<!-- 全选 -->
<label class="check-all">
<input type="checkbox" v-model="isAll" />
全选
</label>
<div class="right-box">
<!-- 所有商品总价 -->
<span class="price-box">总价<span class="price">¥{{toPrice}}</span></span>
<!-- 结算按钮 -->
<button class="pay">结算({{toCount}} )</button>
</div>
</div>
</div>
<!-- 空车 -->
<div class="empty">🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const defaultArr = [
{
id: 1,
icon: '../images/01.jpg',
isChecked: true,
num: 2,
price: 6,
},
{
id: 2,
icon: '../images/02.jpg',
isChecked: false,
num: 7,
price: 20,
},
{
id: 3,
icon: '../images/03.jpg',
isChecked: false,
num: 3,
price: 40,
},
{
id: 4,
icon: '../images/04.jpg',
isChecked: true,
num: 10,
price: 3,
},
{
id: 5,
icon: '../images/05.jpg',
isChecked: false,
num: 20,
price: 34,
}
]
const app = new Vue({
el: '#app',
data: {
// 水果列表
fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr
},
computed: {
isAll: {
get() {
// 所有小按钮都选中,全选按钮才选中
return this.fruitList.every(item => item.isChecked)
},
set(value) {
// console.log(value) 全选按钮的状态
this.fruitList.forEach(item => item.isChecked = value)
}
},
// 选中的总数
toCount() {
return this.fruitList.reduce((sum, item) => {
if (item.isChecked) {
// 选中了,需要累加
return sum + item.num
} else {
// 不需要累加
return sum
}
}, 0)
},
// 选中的总价
toPrice() {
return this.fruitList.reduce((sum, item) => {
if (item.isChecked) {
return sum + item.num * item.price
} else {
return sum
}
}, 0)
}
},
methods: {
del(id) {
this.fruitList = this.fruitList.filter(item => item.id !== id)
},
sub(id) {
console.log(id)
// 根据 id 找到数组中的对应项 find
const fruit = this.fruitList.find(item => item.id === id)
fruit.num--
},
add(id) {
const fruit = this.fruitList.find(item => item.id === id)
fruit.num++
}
},
watch: {
fruitList: {
deep: true,
handler(newValue) {
localStorage.setItem('list', JSON.stringify(newValue))
}
}
}
})
</script>
</body>
</html>