指令补充
指令修饰符
<!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"><h3>@keyup.enter → 监听键盘回车事件</h3><input @keyup="fn" v-model="username" type="text"></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {username: ''},methods: {fn(e){if(e.key === 'Enter'){console.log('键盘回车的时候触发',this.username);}}}})</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>.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>姓名:<input v-model.trim="username" type="text"><br>年纪:<input v-model,number="age" type="text"><br><h3>@事件名.stop → 阻止冒泡</h3><div @click="fatherFn" class="father"><div @click.stop="sonFn" class="son">儿子</div></div><h3>@事件名.prevent → 阻止默认行为</h3><a @click.prevent href="http://www.baidu.com">阻止默认行为</a></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {username: '',age: '',},methods: {fatherFn () {alert('老父亲被点击了')},sonFn () {alert('儿子被点击了')}}})</script>
</body>
</html>
v-bind 对于样式控制的增强
操作class
<!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><div id="app"><div class="box" :class="{ pink:true,big:true }">黑马程序员</div><div class="box" :class="['pink','big']">黑马程序员</div></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {}})</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="activeIndex = index"><a :class="{active:index === activeIndex}" href="#">{{ item.name }}</a></li></ul></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {activeIndex:0,list: [{ id: 1, name: '京东秒杀' },{ id: 2, name: '每日特价' },{ id: 3, name: '品类秒杀' }]}})</script>
</body>
</html>
操作style
<!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"><div class="box" :style="{width:'400px',height:'400px',backgroundColor:'green'}"></div></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {}})</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: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="./vue.js"></script><script>const app = new Vue({el: '#app',data: {percent:0}})</script>
</body>
</html>
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 v-model="gender" type="radio" name="gender" value="1">男<input v-model="gender" type="radio" name="gender" value="0">女<br><br><!-- 前置理解:1. option 需要设置 value 值,提交给后台2. select 的 value 值,关联了选中的 option 的 value 值结合 Vue 使用 → v-model-->所在城市:<select v-model="cityId"><option value="101">北京</option><option value="102">上海</option><option value="103">成都</option><option value="104">南京</option></select><br><br>自我描述:<textarea v-model="desc"></textarea> <button>立即注册</button></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {username:'',isSingle:true,gender:'1',cityId:'102',desc:''}})</script>
</body>
</html>
computed 计算属性
<!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>礼物总数:{{ totalCount }} 个</p></div><script src="./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() {// 基于现有数据,编写求值逻辑// 计算属性函数内部,可以直接通过this访问到app// 对this.list 数组里面num进行求和 -> reduce let total = this.list.reduce((sum, item) => sum + item.num, 0)return total}}})</script>
</body></html>
computed 计算属性 vs methods 方法
计算属性完整写法
<!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="firstName"><br>名:<input type="text" v-model="lastName"><br><span>{{ fullName }}</span><button @click="changeName">修改姓名</button></div><script src="./vue.js"></script><script>const app = new Vue({el: '#app',data: {firstName:'林',lastName:'杨'},methods: {changeName(){this.fullName='余周周'}},computed: {// 简写->获取// fullName(){// return this.firstName+this.lastName// }// 完整写法 -> 获取 + 设置fullName:{get(){return this.firstName+this.lastName},// 当fullName计算属性,被修改赋值时,执行set,修改的值,传递给set方法的形参set(value){this.firstName = value.slice(0,1)this.lastName = value.slice(1)}}}})</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>.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;}</style>
</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>0"><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 @click.prevent="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">平均分:{{ avg }}</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" /></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="../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: {totalScore() {return this.list.reduce((sum, item) => sum + item.score, 0)},avg(){if(this.list.length ===0){return 0}return (this.totalScore/this.list.length).toFixed(2)}},methods: {del(id) {this.list = this.list.filter(item => item.id !== id)},add() {if (!this.subject) {alert('请输入科目')return}if (typeof this.score !== 'number') {alert('请输入正确的成绩')return}this.list.unshift({id: +new Date(),subject: this.subject,score: this.score})this.subject = ''this.score = ''}}})</script>
</body></html>
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" /><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><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">{{ 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: {// words: ''obj: {words: ''},result: '', // 翻译结果// timer: null // 延时器id},// 具体讲解:(1) watch语法 (2) 具体业务实现watch: {// 该方法会在数据变化时调用执行// newValue新值, oldValue老值(一般不用)// words (newValue) {// console.log('变化了', newValue)// }'obj.words' (newValue) {// console.log('变化了', 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}})this.result = res.data.dataconsole.log(res.data.data)}, 300)}}})</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>* {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">{{ 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: '小黑',lang: 'italy'},result: '', // 翻译结果},watch: {obj: {deep: true, // 深度监视immediate: true, // 立刻执行,一进入页面handler就立刻执行一次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.dataconsole.log(res.data.data)}, 300)}}// '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// }// })// this.result = res.data.data// console.log(res.data.data)// }, 300)// }}})</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>购物车</title><style>.app-container {padding-bottom: 300px;width: 800px;margin: 0 auto;}@media screen and (max-width: 800px) {.app-container {width: 600px;}}.app-container .banner-box {border-radius: 20px;overflow: hidden;margin-bottom: 10px;}.app-container .banner-box img {width: 100%;}.app-container .nav-box {background: #ddedec;height: 60px;border-radius: 10px;padding-left: 20px;display: flex;align-items: center;}.app-container .nav-box .my-nav {display: inline-block;background: #5fca71;border-radius: 5px;width: 90px;height: 35px;color: white;text-align: center;line-height: 35px;margin-right: 10px;}.breadcrumb {font-size: 16px;color: gray;}.table {width: 100%;text-align: left;border-radius: 2px 2px 0 0;border-collapse: separate;border-spacing: 0;}.th {color: rgba(0, 0, 0, 0.85);font-weight: 500;text-align: left;background: #fafafa;border-bottom: 1px solid #f0f0f0;transition: background 0.3s ease;}.th.num-th {flex: 1.5;}.th {text-align: center;}.th:nth-child(4),.th:nth-child(5),.th:nth-child(6),.th:nth-child(7) {text-align: center;}.th.th-pic {flex: 1.3;}.th:nth-child(6) {flex: 1.3;}.th,.td {position: relative;padding: 16px 16px;overflow-wrap: break-word;flex: 1;}.pick-td {font-size: 14px;}.main,.empty {border: 1px solid #f0f0f0;margin-top: 10px;}.tr {display: flex;cursor: pointer;border-bottom: 1px solid #ebeef5;}.tr.active {background-color: #f5f7fa;}.td {display: flex;justify-content: center;align-items: center;}.table img {width: 100px;height: 100px;}button {outline: 0;box-shadow: none;color: #fff;background: #d9363e;border-color: #d9363e;color: #fff;background: #d9363e;border-color: #d9363e;line-height: 1.5715;position: relative;display: inline-block;font-weight: 400;white-space: nowrap;text-align: center;background-image: none;border: 1px solid transparent;box-shadow: 0 2px 0 rgb(0 0 0 / 2%);cursor: pointer;transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;touch-action: manipulation;height: 32px;padding: 4px 15px;font-size: 14px;border-radius: 2px;}button.pay {background-color: #3f85ed;margin-left: 20px;}.bottom {height: 60px;display: flex;align-items: center;justify-content: space-between;padding-right: 20px;border: 1px solid #f0f0f0;border-top: none;padding-left: 20px;}.right-box {display: flex;align-items: center;}.check-all {cursor: pointer;}.price {color: hotpink;font-size: 30px;font-weight: 700;}.price-box {display: flex;align-items: center;}.empty {padding: 20px;text-align: center;font-size: 30px;color: #909399;}.my-input-number {display: flex;}.my-input-number button {height: 40px;color: #333;border: 1px solid #dcdfe6;background-color: #f5f7fa;}.my-input-number button:disabled {cursor: not-allowed !important;}.my-input-number .my-input__inner {height: 40px;width: 50px;padding: 0;border: none;border-top: 1px solid #dcdfe6;border-bottom: 1px solid #dcdfe6;}.my-input-number {position: relative;display: inline-block;width: 140px;line-height: 38px;}.my-input-number span {-moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;}.my-input-number .my-input {display: block;position: relative;font-size: 14px;width: 100%;}.my-input-number .my-input__inner {-webkit-appearance: none;background-color: #fff;background-image: none;border-radius: 4px;border: 1px solid #dcdfe6;box-sizing: border-box;color: #606266;display: inline-block;font-size: inherit;height: 40px;line-height: 40px;outline: none;padding: 0 15px;transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);width: 100%;padding-left: 50px;padding-right: 50px;text-align: center;}.my-input-number .my-input-number__decrease,.my-input-number .my-input-number__increase {position: absolute;z-index: 1;top: 1px;width: 40px;height: auto;text-align: center;background: #f5f7fa;color: #606266;cursor: pointer;font-size: 13px;}.my-input-number .my-input-number__decrease {left: 1px;border-radius: 4px 0 0 4px;border-right: 1px solid #dcdfe6;}.my-input-number .my-input-number__increase {right: 1px;border-radius: 0 4px 4px 0;border-left: 1px solid #dcdfe6;}.my-input-number .my-input-number__decrease.is-disabled,.my-input-number .my-input-number__increase.is-disabled {color: #c0c4cc;cursor: not-allowed;}</style>
</head><body><div class="app-container" id="app"><!-- 顶部banner --><div class="banner-box"><img src="http://autumnfish.cn/static/fruit.jpg" alt="" /></div><!-- 面包屑 --><div class="breadcrumb"><span>🏠</span>/<span>购物车</span></div><!-- 购物车主体 --><div class="main" v-if="fruitList.length>0"><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 v-for="(item,index) in fruitList" :key="item.id" class="tr" :class="{active:item.isChecked}"><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 :disabled="item.num<=1" class="decrease" @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">{{totalPrice}}</span></span><!-- 结算按钮 --><button class="pay">结算( {{ totalCount}} )</button></div></div></div><!-- 空车 --><div class="empty" v-else>🛒空空如也</div></div><script src="../vue.js"></script><script>const defaultArr=[{id: 1,icon: 'http://autumnfish.cn/static/火龙果.png',isChecked: true,num: 2,price: 6,},{id: 2,icon: 'http://autumnfish.cn/static/荔枝.png',isChecked: false,num: 7,price: 20,},{id: 3,icon: 'http://autumnfish.cn/static/榴莲.png',isChecked: false,num: 3,price: 40,},{id: 4,icon: 'http://autumnfish.cn/static/鸭梨.png',isChecked: true,num: 10,price: 3,},{id: 5,icon: 'http://autumnfish.cn/static/樱桃.png',isChecked: false,num: 20,price: 34,},]const app = new Vue({el: '#app',data: {// 水果列表fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr,},computed:{// 默认计算属性:只能获取不能设置,要设置需要写完整写法// isAll(){// // 必须所有小选框都选中,全选按钮才选中 ->every// return this.fruitList.every(item=>item.isChecked)// }isAll:{get(){return this.fruitList.every(item=>item.isChecked)},set(value){// 基于拿到的布尔值,要让所有小选框同步状态this.fruitList.forEach(item => item.isChecked=value);}},// 统计选中总数totalCount(){return this.fruitList.reduce((sum,item)=>{if(item.isChecked){return sum+item.num}else{return sum}},0)},// 统计选中总价totalPrice(){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){// 1. 根据 id 找到数组中的对应项 -> findconst fruit = this.fruitList.find(item => item.id===id)// 2. 操作num数量fruit.num--},add(id){// 1. 根据 id 找到数组中的对应项 -> findconst fruit = this.fruitList.find(item => item.id===id)// 2. 操作num数量fruit.num++}},watch:{fruitList:{deep:true,handler(newValue){// 需要将变化后的 newValue 存入本地(转JSON)localStorage.setItem('list',JSON.stringify(newValue))}}}})</script>
</body></html>