Vue入门到实战之第三篇【超基础】
Vue入门到实战之第三篇【超基础】
- 指令补充
- 指令修饰符
- v-bind 对于样式控制的增强
- 操作class
- 案例:京东秒杀tab导航高亮
- 操作style
- 案例:进度条效果
- v-model 应用于其他表单元素
- 案例:小黑学习网
- 计算属性
- 案例-小黑的礼物清单
- computed 计算属性 vs methods 方法
- 计算属性完整写法
- 综合案例:成绩案例
- watch 侦听器(监视器)
- 案例:实时翻译-代码实现(简单写法)
- 案例:实时翻译-代码实现(完整写法)
- 小结
- 综合案例 - 水果购物车
指令补充
指令修饰符
通过"."指明一些指令后缀,不同后缀封装了不同的处理操作 ——> 简化代码
①按键修饰符@keyup.enter -> 键盘回车监听
②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>
</head>
<body><div id="app"><h3>@keyup.enter -> 监听键盘回车事件</h3><!-- <input @keyup.enter="fn" v-model="username" type="text"> --><!-- 任意键--><input @keyup="fn" v-model="username" type="text">
</div><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><script>const app = new Vue({el: '#app',data:{username:''},methods:{fn (e) {console.log(e);//打印键盘事件,里面有个key,当key是Enter的时候,则打印“键盘回车的时候触发+input内容”if (e.key == 'Enter'){console.log('键盘回车的时候触发', this.username)}}}})
</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>.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 → 阻止默认行为(<a>标签的默认行为是一点链接就跳转)</h3><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>
v-bind 对于样式控制的增强
为了方便开发者进行样式控制,Vue扩展了v-bind的语法,可以针对class 类名 和 style 行内样式进行控制。
操作class
语法 :class="对象/数组"
①对象->键就是类名。值是布尔值。如果值为true,有这个类,否则没有这个类
例子:
<div class="box" :class="{类名1:布尔值},{类名2:布尔值}"></div>
适用场景:一个类名,来回切换②数组->数组中所有的类,都会添加到盒子上,本质就是一个class列表
例子:
<div class="box" :class="[类名1,类名2,类名3]"></div>
适用场景:批量添加或删除类
操作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: false}">陈奕璇程序媛</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: {}})</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><a class="active" href="#">京东秒杀</a></li>去除active高亮消失 --><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>
</body>
</html>
操作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"><!-- <div class="box" :style="{ width: '400px', height: '400px', background-color: 'green' }"></div>会报错,JS不支持带-的写法 --><div class="box" :style="{ width: '400px', height: '400px', 'background-color': 'green' }"></div><!--把属性名引起来就好了,或者用驼峰也可以backgroundColor--></div><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/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 + '%'}"><!--进度条本质就是设置这个的宽度--><!--引号要引起来百分比,不然JS不支持--><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: 0 }})</script>
</body>
</html>
v-model 应用于其他表单元素
常见的表单元素都可以用v-model绑定关联 -> 快速获取 或 设置表单元素的值
它会根据控件类型自动选取正确的方法来更新元素输入框 input:text -> value
文本域 textarea -> value
复选框 input:checkbox -> checked
单选框 input:radio -> checked
下拉框 select -> value
...
案例:小黑学习网
示例代码
<!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="2">女<br><br><!-- 前置理解:1. option 需要设置 value 值,提交给后台2. select 的 value 值,关联了选中的 option 的 value 值结合 Vue 使用 → v-model--><!-- 在开发者模式中检查选中的元素用$0代表this $0.value='101' -->所在城市:<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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {username: '',isSingle: true,gender: "2",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><!--是属性不是函数,所以不是totalCount()--></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 () {//基于现有的数据,编写求值逻辑//计算属性函数内部,可以直接通过 this 访问到 app 实例// console.log(this.list)//需求:对 this.list 数组里面的 num 进行求和 -> reduce((阶段性结果, 每一项) => 求和的逻辑 , 起始值)let total = this.list.reduce((sum, item) => sum + item.num , 0)return total}}})</script>
</body>
</html>
computed 计算属性 vs methods 方法
computed 计算属性:
作用:封装了一段对于数据的处理,求得一个结果。
语法:
①写在computed配置项中
②作为属性,直接使用 -> this.计算属性 {{ 计算属性 }}
缓存特性(提升性能):
计算属性会对计算出来的结果缓存,再次使用直接读取缓存,依赖项变化了,会自动重新计算 -> 并再次缓存
methods 方法:
作用:给实例提供一个方法,调用以处理业务逻辑。
语法:
①写在methods配置项中
②作为方法,需要调用 -> this.方法名() {{ 方法名() }} @事件名="方法名"
实例比较代码
<!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>{{ 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>礼物总数:{{ totalCount }} 个</p>推荐 --><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>
</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>
</head>
<body><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 src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {firstName: '刘',lastName: '备'},computed: {// // 简写 -> 获取,没有配置设置的逻辑 -> 点击"改名卡",控制台会报错,因为没有写set方法// fullName () {// return this.firstName + this.lastName// }//完整写法 -> 获取 + 设置fullName : {// (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)// 会将返回值作为,求值的结果get () {return this.firstName + this.lastName},// (2) 当fullName计算属性,被修改赋值时,执行set// 修改的值,传递给set方法的形参set (value) {//写了set方法不会报错// console.log(value)this.firstName = value.slice(0,1)//slice截取字符串,取第一个字符:0,字符串第0位;1,1个字符this.lastName = value.slice(1)//取第一个往后的所有字符}}},methods: {changeName () {this.fullName = '吕小布'}}})</script>
</body>
</html>
点击改名卡后
综合案例:成绩案例
style文件链接:用夸克网盘分享了「Vue入门到实战之第三篇【超基础】_style.zip」,点击链接即可保存。
示例代码
<!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 > 0"><tr v-for="(item , index) in list" :key="item.id"><td>{{ index + 1 }}</td><td>{{ item.subject }}</td><!-- 需求:不及格的标红,< 60 分,加上red类 --><td :class="{ red: item.score < 60 }">{{ item.score }}</td><td><a @click.prevent="del(item.id)" href="http://www.baidu.com">删除</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">平均分:{{ averageScore }}</span></td></tr></tfoot></table></div><div class="form"><div class="form-item"><div class="label">科目:</div><div class="input"><inputtype="text"placeholder="请输入科目"v-model.trim="subject"/></div></div><div class="form-item"><div class="label">分数:</div><div class="input"><!-- 通过控制台app.score进行查看 --><inputtype="text"placeholder="请输入分数"v-model.number="score"/></div></div><div class="form-item"><div class="label"></div><div class="input"><button @click="add" class="submit" >添加</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: 89 },{ id: 7, subject: '数学', score: 99 },{ id: 12, subject: '英语', score: 70 },],subject: '',score: ''},computed:{totalScore(){return this.list.reduce((sum, item) => sum + item.score, 0)},averageScore(){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})//往前面加是unshift,往后面加是push//输入完清空this.subject = ''this.score = ''}}})</script></body>
</html>
watch 侦听器(监视器)
作用:监视数据变化,执行一些 业务逻辑 或 异步操作。
语法:
①简单写法 → 简单类型数据,直接监视
②完整写完 → 添加额外配置项
(1)deep: true 对复杂类型深度监视
(2) immediate: true 初始化立刻执行一次handler方法
案例:实时翻译-代码实现(简单写法)
<!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 没有这个也可以实现我们的功能(与Vue渲染无关的可以直接在实例上绑定)},// 具体讲解:(1) watch语法 (2) 具体业务实现watch:{// 该方法会在数据变化时调用执行// newValue新值, oldValue 老值(一般不用)// words (newValue, oldValue) {// console.log('变化了', newValue , oldValue)// }//监视data下的words// words(newValue){// console.log('变化了', newValue)// }//监视data下obj的words'obj.words' (newValue){// console.log('变化了', newValue)// 防抖:延迟执行 → 干啥事先等一等,延迟一会,一段时间内没有再次触发,才执行//每次输入都会侦听,侦听的时候又会清空上一次的延时操作,只有当用户停顿超过300毫秒的时候才会执行异步函数clearTimeout(this.timer)//延时器idthis.timer = setTimeout(async () => {//async写在回调的位置上,紧挨着awaitconst res = await axios({url: 'https://applet-base-api-t.itheima.net/api/translate' ,params: {words: newValue}})// console.log(res)//得到结果,可以看控制台:data.data是翻译后的结果// console.log(res.data.data)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>// 需求:输入内容,修改语言,都实时翻译// 接口返回的应该是随机字符串,不用考虑翻译的正确性const app = new Vue({el: '#app',data: {// words: ''obj:{words: '陈奕璇',//bug:默认值给值的时候没有翻译,只有改了这个值才翻译lang: 'italy'},result: '' //翻译结果},watch:{obj: {deep:true,//深度监视immediate: true,//立刻执行(用于修改上述的bug)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>
小结
综合案例 - 水果购物车
准备代码文件链接:用夸克网盘分享了「Vue入门到实战之第三篇【超基础】_综合案例-购物车之准备代码文件.zip」,点击链接即可保存。
示例代码
<!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" 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 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 :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">{{ totalPirce }}</span></span><!-- 结算按钮 --><button class="pay">结算( {{ totalNum }} )</button></div></div></div><!-- 空车 --><div class="empty" v-else>🛒空空如也</div></div><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const defaultArr = [{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 app = new Vue({el: '#app',data: {// 水果列表fruitList: JSON.parse(localStorage.getItem('list')) || defaultArr},computed: {// 默认计算属性: 只能获取不能设置,要设置需要写完整写法// isAll () {// // 必须所有的小选框都选中,全选按钮才选中 → every// return this.fruitList.every(item => item.isChecked) // }// 完整写法 = get + setisAll :{get (){return this.fruitList.every(item => item.isChecked) },set (value){// 基于拿到的布尔值(value),要让所有的小选框 同步状态this.fruitList.forEach(item => item.isChecked = value)}},// 统计选中的总数totalNum () {return this.fruitList.reduce((sum,item) => {if (item.isChecked) {// 选中 → 需要累加return sum + item.num }else{// 不选中 → 不需要累加return sum}}, 0)},// 总计选中的总价 num * pricetotalPirce () {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)},add (id) {//1. 根据id 找到数组中的对应项 → findconst fruit = this.fruitList.find(item => item.id === id)//2. 操作 num 数量fruit.num++},sub (id) {//1. 根据id 找到数组中的对应项 → findconst fruit = this.fruitList.find(item => item.id === id)//2. 操作 num 数量fruit.num--//小于等于1的时候把➖禁止}},//持久化到本地watch:{fruitList:{deep: true,handler (newValue){//需要将变化后的 newValue 存入本地 (转JSON)localStorage.setItem('list', JSON.stringify(newValue))}}}})</script></body>
</html>