1.基础用法
概念: 依赖现有的数据,计算出来的新属性。 当依赖的数据变化时,自动重新计算
语法:
① 声明在 computed 配置项中,一个计算属性对应一个函数
② 使用起来和普通属性一样使用 {{ 计算属性名 }}
演示案例:
注意:
- computed配置项和data配置项是同级的
- computed中的计算属性虽然是函数的写法,但他依然是个属性
- computed中的计算属性不能和data中的属性同名
- 使用computed中的计算属性和使用data中的属性是一样的用法
- computed中计算属性内部的this依然指向的是Vue实例
2.computed与methods的区别
小黑礼物清单案例中的【礼物总数】为什么一定要使用计算属性,而不使用方法来计算,这是因为
computed 计算属性具有 【缓存】特性 ,执行性能比methods方法要高
computed 计算属性具有 【缓存】特性, 多次从计算属性中获取礼物总数时,如果结果相同,那么第一次会将计算的结果缓存起来,从第二次开始就会从缓存中返回结果。
methods方法,每次调用都会重新计算一次后再返回结果,性能较 computed 计算属性要低一些
3.计算属性的完整写法
既然计算属性也是属性,能访问,应该也能修改了?
1上面学习的计算属性写法,其实是计算属性的简写方式,只能读取数据,不能 "修改" 【开发较常用】
2如果要 "修改" → 需要写计算属性的完整写法 【不常用,但需要了解】
计算属性完整写法举例:
4.成绩案例
.score-case {
width: 1000px;
margin: 50px auto;
display: flex;
}
.score-case .table {
flex: 4;
}
.score-case .table table {
width: 100%;
border-spacing: 0;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
}
.score-case .table table th {
background: #f5f5f5;
}
.score-case .table table tr:hover td {
background: #f5f5f5;
}
.score-case .table table td,
.score-case .table table th {
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
text-align: center;
padding: 10px;
}
.score-case .table table td.red,
.score-case .table table th.red {
color: red;
}
.score-case .table .none {
height: 100px;
line-height: 100px;
color: #999;
}
.score-case .form {
flex: 1;
padding: 20px;
}
.score-case .form .form-item {
display: flex;
margin-bottom: 20px;
align-items: center;
}
.score-case .form .form-item .label {
width: 60px;
text-align: right;
font-size: 14px;
}
.score-case .form .form-item .input {
flex: 1;
}
.score-case .form .form-item input,
.score-case .form .form-item select {
appearance: none;
outline: none;
border: 1px solid #ccc;
width: 200px;
height: 40px;
box-sizing: border-box;
padding: 10px;
color: #666;
}
.score-case .form .form-item input::placeholder {
color: #666;
}
.score-case .form .form-item .cancel,
.score-case .form .form-item .submit {
appearance: none;
outline: none;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 10px;
margin-right: 10px;
font-size: 12px;
background: #ccc;
}
.score-case .form .form-item .submit {
border-color: #069;
background: #069;
color: #fff;
}
<!--
* @Date: 2025-01-03 16:19:54
* @LastEditors: zl 1077167261@qq.com
* @LastEditTime: 2025-01-03 16:28:35
* @FilePath: \vue\2.watch与computed\6.成绩案例.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" />
<link rel="stylesheet" href="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>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{index+1}}</td>
<td>{{item.subject}}</td>
<td :class="{red:item.score<60}">{{item.score}}</td>
<td><a href="#" @click="del(index)">删除</a></td>
</tr>
</tbody>
<tbody>
<tr v-if="list.length === 0">
<td colspan="5">
<span class="none">暂无数据</span>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<span>总分:{{total}}</span>
<span style="margin-left: 50px">平均分:{{average}}</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.trim="subject"
@keyup.enter="add"
/>
</div>
</div>
<div class="form-item">
<div class="label">分数:</div>
<div class="input">
<input
type="text"
placeholder="请输入分数"
v-model.trim.number="score"
@keyup.enter="add"
/>
</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: '',
},
computed: {
total() {
return this.list.reduce((pre, cur) => pre + cur.score, 0);
},
average() {
return (this.total / this.list.length).toFixed(2);
},
},
methods: {
add() {
if (!this.subject || !this.score) {
alert('请输入科目和分数');
return;
}
this.list.push({
id: this.list.length + 1,
subject: this.subject,
score: this.score,
});
this.subject = '';
this.score = '';
},
del(index) {
this.list.splice(index, 1);
},
},
});
</script>
</body>
</html>
2.watch侦听器(监视器)
作用:监视数据变化,执行一些业务逻辑或异步操作
例如:
根据关键字,ajax异步请求获取城市数据 接口地址
表单验证逻辑
语法:
① 简单写法 → 简单类型数据,直接监视
② 完整写法 → 添加额外配置项
1.简单写法
2.完整写法
3.综合案例
购物车案例
需求说明:
1渲染功能
2删除功能
3修改个数
4全选反选
5统计 选中的 总价 和 总数量
6持久化到本地
实现思路:
1.基本渲染: v-for遍历、:class动态绑定样式
2.删除功能 : v-on 绑定事件,获取当前行的id
3.修改个数 : v-on绑定事件,获取当前行的id,进行筛选出对应的项然后增加或减少
4.全选反选
1必须所有的小选框都选中,全选按钮才选中
2如果全选按钮选中,则所有小选框都选中
3如果全选取消,则所有小选框都取消选中
声明计算属性,判断数组中的每一个checked属性的值,看是否需要全部选
5.统计 选中的 总价 和 总数量 :通过计算属性来计算选中的总价和总数量
6.持久化到本地: 在数据变化时都要更新下本地存储 watch
<!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="./img/fruit.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 v-if="fruitList.length>0" class="tbody">
<div
v-for="(item,index) in fruitList"
:key="item.id"
class="tr active"
>
<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" @click="item.num--">-</button>
<span class="my-input__inner">{{ item.num }}</span>
<button class="increase" @click="item.num++">+</button>
</div>
</div>
<div class="td">{{ item.num * item.price }}</div>
<div class="td">
<button @click="fruitList.splice(index,1)">删除</button>
</div>
</div>
</div>
<!-- 空车 -->
<div v-else class="empty">🛒空空如也</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"
>{{ totalAmount }}</span
></span
>
<!-- 结算按钮 -->
<button class="pay">结算( {{ totalCount }} )</button>
</div>
</div>
</div>
</div>
<script src="../vue.2.7.js"></script>
<script>
// 页面加载执行
const oldList = [
{
id: 1,
icon: './img/火龙果.png',
isChecked: true,
num: 2,
price: 6,
},
{
id: 2,
icon: './img/荔枝.png',
isChecked: false,
num: 7,
price: 20,
},
{
id: 3,
icon: './img/榴莲.png',
isChecked: false,
num: 3,
price: 40,
},
{
id: 4,
icon: './img/鸭梨.png',
isChecked: true,
num: 10,
price: 3,
},
{
id: 5,
icon: './img/樱桃.png',
isChecked: false,
num: 20,
price: 34,
},
];
const tmpList = JSON.parse(localStorage.getItem('list'));
// const list = tmpList.length == 0 ? oldList : tmpList;
function getList() {
if (tmpList) {
return tmpList.length == 0 ? oldList : tmpList;
} else {
return oldList;
}
}
const list = getList();
const app = new Vue({
el: '#app',
data: {
// 水果列表
fruitList: list,
},
watch: {
fruitList: {
deep: true,
immediate: true,
handler(newValue) {
// console.log(newValue)
// 将this.fruitList 保存到本地存储
localStorage.setItem('list', JSON.stringify(newValue));
},
},
},
computed: {
// 计算勾选的总价
totalAmount() {
let total = 0;
this.fruitList.forEach((item) => {
if (item.isChecked) {
total += item.num * item.price;
}
});
return total;
},
// 计算勾选的总数量
totalCount() {
let total = 0;
this.fruitList.forEach((item) => {
if (item.isChecked) {
total += item.num;
}
});
return total;
},
isAll: {
get() {
// 它的返回值为boolean,来决定全选的复选框是否勾选
// 什么时候返回true? false
// 遍历fruitList中的所有数据 isChecked全部为true,返回true,只要有一个为false,返回false
let isOK = true; // 默认全选
// 证明它 -》 只要有一个false,即为false
for (let i = 0; i < this.fruitList.length; i++) {
if (!this.fruitList[i].isChecked) {
isOK = false;
break;
}
}
return isOK;
},
set(val) {
// console.log(val);
// 遍历水果列表,将对象中的isChecked = val
this.fruitList.forEach((element) => {
element.isChecked = val;
});
},
},
},
});
</script>
</body>
</html>