常用指令
1.v-show与v-if底层原理的区别
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="card"><div v-html="msg" id="app" class="card-body mx-auto bg-warning" style="width:150px"></div></div><div id="vif"><div v-show="sec01" class="box1" style="background-color: red; height: 50px"></div><div v-if="sec01" class="box2" style="background-color: yellow; height: 50px"></div></div><script>const app = new Vue({el: '#vif',data: {msg: '<a href="http://www.baidu.com">百度一下</a>',sec01: false}});</script>
</body>
</html>
<div id="vif"><div v-show="sec01" class="box1" style="background-color: red; height: 50px"></div><div v-if="sec01" class="box2" style="background-color: yellow; height: 50px"></div>
</div><script>const app = new Vue({el: '#vif',data: {msg: '<a href="http://www.baidu.com">百度一下</a>',sec01: false}});</script>
- v-show是css切换display
- v-if直接不渲染
- v-show适合与频繁的切换显示与隐藏的场景(比如:购物车弹窗)
- v-if并不适合,频繁的创建标签节点与销毁标签节点非常占资源
- v-if适合要么显示要么隐藏的一次性的场景(比如:提示框)
2.v-else与v-else-if
- 辅助v-if 对 需要进行【多条件】判断的场景进行处理
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><p v-if="gender === 1">性别:男</p><p v-else>性别:女</p><hr/><p v-if="score >= 90">成绩判定:A,成绩大于90,牛逼</p><p v-else-if="score >= 80">成绩判定:B,成绩大于80,可以了,不错了</p><p v-else-if="score >= 60">成绩判定:C,成绩大于60,还要继续努力喔</p><p v-else>成绩判定:D,成绩小于60,啊米诺斯</p></div></div>
</body><script>const app = new Vue({el: '#app',data: {gender: 1,score: 85}})</script>
</html>
3.v-on
- 注册事件(对应原生onClick())
- v-on:click 点击事件
- v-on:mouseenter 滑动事件
- 【v-on:】可以简写成【@】
- dblclick 双击事件
- mousemove 鼠标移动事件,慎用
- keyup.enter 键盘按压事件,在表单中使用合适
- click.once 单击事件(只执行一次)
还有很多事件:vue中v-on支持的事件总结_v-on:paste-CSDN博客
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><!-- 1.点击事件 --><button v-on:click="count01++">-</button><span>{{count01}}</span><button v-on:click="count01++">+</button><hr><!-- 2.滑动事件 --><button v-on:mouseenter="count02++">-</button><span>{{count02}}</span><button v-on:mouseenter="count02++">+</button><hr/><!-- 3.【v-on:】整体可以简写成【@】 --><button @click="count01++">-</button><span>{{count01}}</span><button @click="count01++">+</button></div></div>
</body><script>const app = new Vue({el: '#app',data: {count01: 99,count02: 99}})</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><button @mouseenter="changeColor(1)" class="btn01">-</button><span>{{count}}</span><button @mouseenter="changeColor" class="btn02">+</button></div></div>
</body><script>const app = new Vue({el: '#app',data: {count: 99},methods:{changeColor(n){if(n === 1){console.log(n);}else{console.log(2);}}}})</script>
</html>
4.v-bind
- 动态修改标签的属性
- 可以简写,【v-bind】省略不写,【:】保留
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><img v-bind:src="imgUrl" v-bind:title="txt01" draggable=true alt="图片加载失败了喔"><!-- 可以简写喔,将【v-bind】省略,【:】保留即可--><!-- <img :src="imgUrl" :title="txt01" draggable=true alt="图片加载失败了喔"> --></div></div>
</body><script>const app = new Vue({el: '#app',data:{imgUrl: '../01.png',txt01: '御姐'}})</script>
</html>
案例:波仔的学习之旅
视频链接:015-案例-波仔的学习之旅_哔哩哔哩_bilibili
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><button v-show="index > 0" @click="index--">上一页</button><div><img :src="imgUrlArr[index]" alt="图片加载失败了喔"></div><button v-show="index < imgUrlArr.length - 1" @click="index++">下一页</button></div></div><script>const app = new Vue({el: '#app',data:{imgUrlArr: ['./img/11-00.gif', './img/11-01.gif', './img/11-02.gif', './img/11-03.gif', './img/11-04.png', './img/11-05.png'],index: 0}})</script>
</body></html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><button v-show="flag">上一页</button><img :src="imgUrlArr[index]" alt="图片加载失败了喔" style="display:block"><button @click="switchImg">下一页</button></div></div><script>const app = new Vue({el: '#app',data:{imgUrlArr: ['./img/11-00.gif', './img/11-01.gif', './img/11-02.gif', './img/11-03.gif', './img/11-04.png', './img/11-05.png'],index: 0,flag: false},methods:{switchImg(){if(this.index >= this.imgUrlArr.length - 1){this.index = 0;this.flag = false;}else{this.index++;this.flag = true;}}}})</script>
</body></html>
5.v-for
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><ul><li v-for="(item, index) in list">{{item}} - {{index}}</li></ul></div></div><script>const app = new Vue({el: '#app',data:{list: ['西瓜', '苹果', '鸭梨', '榴莲']}})</script>
</body></html>
案例:小黑的书架
视频链接:017-案例-小黑的书架_哔哩哔哩_bilibili
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app"><ul><li v-for="(item, index) in bookList"><span>{{item.name}}</span><span>{{item.author}}</span><button @click="delBookList(item.id)">删除</button></li></ul></div></div><script>const app = new Vue({el: '#app',data:{bookList:[{id: 1, name: '《西游记》', author: '吴承恩'},{id: 2, name: '《水浒传》', author: '施耐庵'},{id: 3, name: '《三国演义》', author: '罗贯中'},{id: 4, name: '《红楼梦》', author: '曹雪芹'}]},methods:{delBookList(id){this.bookList = this.bookList.filter((item) => {return item.id != id})}}})</script>
</body></html>
加不加:key的问题
详细:【Vue】v-for中:key中item.id与Index使用的区别-CSDN博客
- v-for 加上 :key才算完整
- 有:key(列表项每个li的唯一标识),对每个li能够进行精准操作,并且优先复用代码
- 没有:key,在删除一个li后,默认会原地修改代码,选择前面的li,多余的li删除
调试,给第一个li设置背景颜色,然后删除li
结果,第一个li并没有删除,而是删除了最后的li
6.v-model
(啊啊啊啊啊啊啊,v-model写得我好爽,越发爱vue了)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script>
</head>
<body><div class="container"><div id="app">账户:<input type="text" v-model="username"> <br><br>密码:<input type="password" v-model="password"> <br><br><button @click="login">登录</button><button @click="reset">重置</button></div></div><script>const app = new Vue({el: '#app',data:{username: '',password: ''},methods:{login(){console.log(this.username,this.password);},reset(){this.username = '';this.password = '';console.log(this.username,this.password);}}})</script>
</body></html>
7.综合案例:小黑记事本(10/14大指令学完)
视频链接:020-综合案例-小黑记事本-渲染和删除_哔哩哔哩_bilibili
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>创建一个Vue实例</title><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/mhDoLbDldZc3qpsJHpLogda//BVZbgYuw6kof4u2FrCedxOtgRZDTHgHUhOCVim" crossorigin="anonymous"></script><link rel="stylesheet" href="./css/小黑记事本.css">
</head>
<body><div class="container"><!-- 主体区域 --><section id="app"><!-- 输入框 --><header class="header"><h1>小黑记事本</h1><input v-model="thingStr" placeholder="请输入任务" class="new-todo" /><button @click="addThing" class="add">添加任务</button></header><!-- 列表区域 --><section class="main"><ul class="todo-list"><li v-for="(item,index) in thingList" :key="item.id" class="todo"><div class="view"><!-- 细节1:使用数组下标来进行渲染,而不使用item.id --><span class="index">{{index + 1}}.</span> <label>{{item.thing}}</label><!-- 细节2:使用item.id做形参,而不使用数组下标 --><button @click="del(item.id)" class="destroy"></button></div></li></ul></section><!-- 统计和清空 --><footer class="footer"><!-- 统计 --><span class="todo-count">合 计:<strong> {{thingList.length}} </strong></span><!-- 清空 --><button @click="clearAllth" class="clear-completed">清空任务</button></footer></section></div><script>const app = new Vue({el: '#app',data:{thingStr: '',thingList: []},methods:{addThing(){// 为空处理if(this.thingStr.trim() === ''){alert('您还没有输入喔');return;}// 在thingList的前面添加一个对象元素,没有后端数据,可以用时间戳做唯一标识符idthis.thingList.unshift({id: +new Date(), thing: this.thingStr});// 清空输入框this.thingStr = '';},// 删除一条任务del(id){this.thingList = this.thingList.filter(item => item.id !== id);},// 清空所有任务clearAllth(){this.thingList = [];}}})</script>
</body></html>
html,
body {margin: 0;padding: 0;
}
body {background: #fff;
}
button {margin: 0;padding: 0;border: 0;background: none;font-size: 100%;vertical-align: baseline;font-family: inherit;font-weight: inherit;color: inherit;-webkit-appearance: none;appearance: none;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
}body {font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;line-height: 1.4em;background: #f5f5f5;color: #4d4d4d;min-width: 230px;max-width: 550px;margin: 0 auto;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;font-weight: 300;
}:focus {outline: 0;
}.hidden {display: none;
}#app {background: #fff;margin: 180px 0 40px 0;padding: 15px;position: relative;box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
#app .header input {border: 2px solid rgba(175, 47, 47, 0.8);border-radius: 10px;
}
#app .add {position: absolute;right: 15px;top: 15px;height: 68px;width: 140px;text-align: center;background-color: rgba(175, 47, 47, 0.8);color: #fff;cursor: pointer;font-size: 18px;border-radius: 0 10px 10px 0;
}#app input::-webkit-input-placeholder {font-style: italic;font-weight: 300;color: #e6e6e6;
}#app input::-moz-placeholder {font-style: italic;font-weight: 300;color: #e6e6e6;
}#app input::input-placeholder {font-style: italic;font-weight: 300;color: gray;
}#app h1 {position: absolute;top: -120px;width: 100%;left: 50%;transform: translateX(-50%);font-size: 60px;font-weight: 100;text-align: center;color: rgba(175, 47, 47, 0.8);-webkit-text-rendering: optimizeLegibility;-moz-text-rendering: optimizeLegibility;text-rendering: optimizeLegibility;
}.new-todo,
.edit {position: relative;margin: 0;width: 100%;font-size: 24px;font-family: inherit;font-weight: inherit;line-height: 1.4em;border: 0;color: inherit;padding: 6px;box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);box-sizing: border-box;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;
}.new-todo {padding: 16px;border: none;background: rgba(0, 0, 0, 0.003);box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}.main {position: relative;z-index: 2;
}.todo-list {margin: 0;padding: 0;list-style: none;overflow: hidden;
}.todo-list li {position: relative;font-size: 24px;height: 60px;box-sizing: border-box;border-bottom: 1px solid #e6e6e6;
}.todo-list li:last-child {border-bottom: none;
}.todo-list .view .index {position: absolute;color: gray;left: 10px;top: 20px;font-size: 22px;
}.todo-list li .toggle {text-align: center;width: 40px;/* auto, since non-WebKit browsers doesn't support input styling */height: auto;position: absolute;top: 0;bottom: 0;margin: auto 0;border: none; /* Mobile Safari */-webkit-appearance: none;appearance: none;
}.todo-list li .toggle {opacity: 0;
}.todo-list li .toggle + label {/*Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/*/background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');background-repeat: no-repeat;background-position: center left;
}.todo-list li .toggle:checked + label {background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}.todo-list li label {word-break: break-all;padding: 15px 15px 15px 60px;display: block;line-height: 1.2;transition: color 0.4s;
}.todo-list li.completed label {color: #d9d9d9;text-decoration: line-through;
}.todo-list li .destroy {display: none;position: absolute;top: 0;right: 10px;bottom: 0;width: 40px;height: 40px;margin: auto 0;font-size: 30px;color: #cc9a9a;margin-bottom: 11px;transition: color 0.2s ease-out;
}.todo-list li .destroy:hover {color: #af5b5e;
}.todo-list li .destroy:after {content: '×';
}.todo-list li:hover .destroy {display: block;
}.todo-list li .edit {display: none;
}.todo-list li.editing:last-child {margin-bottom: -1px;
}.footer {color: #777;padding: 10px 15px;height: 20px;text-align: center;border-top: 1px solid #e6e6e6;
}.footer:before {content: '';position: absolute;right: 0;bottom: 0;left: 0;height: 50px;overflow: hidden;box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6,0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6,0 17px 2px -6px rgba(0, 0, 0, 0.2);
}.todo-count {float: left;text-align: left;
}.todo-count strong {font-weight: 300;
}.filters {margin: 0;padding: 0;list-style: none;position: absolute;right: 0;left: 0;
}.filters li {display: inline;
}.filters li a {color: inherit;margin: 3px;padding: 3px 7px;text-decoration: none;border: 1px solid transparent;border-radius: 3px;
}.filters li a:hover {border-color: rgba(175, 47, 47, 0.1);
}.filters li a.selected {border-color: rgba(175, 47, 47, 0.2);
}.clear-completed,
html .clear-completed:active {float: right;position: relative;line-height: 20px;text-decoration: none;cursor: pointer;
}.clear-completed:hover {text-decoration: underline;
}.info {margin: 50px auto 0;color: #bfbfbf;font-size: 15px;text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);text-align: center;
}.info p {line-height: 1;
}.info a {color: inherit;text-decoration: none;font-weight: 400;
}.info a:hover {text-decoration: underline;
}/*Hack to remove background from Mobile Safari.Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio: 0) {.toggle-all,.todo-list li .toggle {background: none;}.todo-list li .toggle {height: 40px;}
}@media (max-width: 430px) {.footer {height: 50px;}.filters {bottom: 10px;}
}
指令补充
1.常见指令修饰符
2.v-bind对于样式操作的增强
2.1操作class
便于批量处理多个class
案例:京东tab导航高亮切换
视频链接:025-v-bind操作class_哔哩哔哩_bilibili
<!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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {activeIndex: 2, // 记录高亮list: [{ id: 1, name: '京东秒杀' },{ id: 2, name: '每日特价' },{ id: 3, name: '品类秒杀' }]}})</script>
</body>
</html>
2.2操作style
便于处理单个样式
案例:进度条
视频链接:025-v-bind操作class_哔哩哔哩_bilibili
<!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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {percent: 30}})</script>
</body>
</html>
3.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="2">女<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="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {username: '',isSingle: false,gender: "2",cityId: '102',desc: ""}})</script>
</body>
</html>
computed计算属性
1.基础语法
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" ><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" ></script><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script></head>
<body><div class="container"><div class="giftList"><table><thead><tr><th>名字</th><th>数量</th></tr></thead><tbody><tr v-for="(item, index) in list" :key="item.id"><td>{{item.name}}</td><td>{{item.num}}</td></tr></tbody><tfoot><tr><td colspan="2"><span>礼物总数:{{totalNum}} 个</span></td></tr></tfoot></table></div></div><script>const app = new Vue({el: '.giftList',data:{list: [{id: 1, name: '篮球', num: 2},{id: 2, name: '玩具', num: 2},{id: 3, name: '铅笔', num: 5}]},computed:{totalNum(){let sum = 0;// this.list.forEach(function(item){// sum += item.num;// })// for(let i = 0; i < this.list.length; i++){// sum += this.list[i].num;// }// reduce()方法,从左往右累加// x为上一次调用reduce的结果值,y是本次调用的数组元素Item,末尾参数reduce( , 0),0表示x的初始值为0sum = this.list.reduce(function(x, y){return x + y.num;},0);return sum;}}})</script>
</body>
</html>
2.计算属性 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>{{ totalCountFn() }}</span></h3><h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3><h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</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>礼物总数:{{ 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>
3.完整写法
<!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>input {width: 30px;}</style>
</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: '备',},methods: {changeName () {this.fullName = '黄忠'}},computed: {// 简写 → 获取,没有配置设置的逻辑// fullName () {// return this.firstName + this.lastName// }// 完整写法 → 获取 + 设置fullName: {// (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)// 会将返回值作为,求值的结果get () {return this.firstName + this.lastName},// (2) 当fullName计算属性,被修改赋值时,执行set// 修改的值,传递给set方法的形参set (value) {// console.log(value.slice(0, 1)) // console.log(value.slice(1)) this.firstName = value.slice(0, 1)this.lastName = value.slice(1)}}}})</script>
</body>
</html>
案例:成绩案例
视频链接:030-成绩案例_哔哩哔哩_bilibili
这是我自己写的,功能逻辑都写出来了,唯独添加按钮点击没反应,以后再处理了
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" ><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" ></script><script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><style>.fontRed{color:red;}</style>
</head>
<body><div class="container"><div class="table-responsive text-center" id="app"><table class="table table-hover table-bordered"><thead class="table table-dark"><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 v-bind:class="{fontRed: item.score < 60}">{{item.score}}</td><td><button type="button" class="btn btn-link" @click="delOne(item.id)">删除</button></td></tr></tbody><tbody v-else><tr><td colspan="5"><span>暂无数据</span></td></tr></tbody><tfoot><tr><td colspan="4">总分:{{totalScoreSum}} 平均分:{{averageScore}}</td></tr></tfoot></table></div><div class="addInput"><label>科目:</label><input class="form-control form-control-lg" type="text" v-model.trim="subject" placeholder="请输入科目"><br /><br /><label>分数:</label><input class="form-control form-control-lg" type="text" v-model.number="score" placeholder="请输入分数"><br /><br /><button type="button" @click="addOne" class="btn btn-primary">添加</button></div></div><script>const app = new Vue({el: '#app',data:{list:[// id: 编号; subject:科目; score:成绩{id: 1, subject: '语文', score: 46},{id: 2, subject: '英语', score: 80},{id: 3, subject: '数学', score: 100},],totalScore: 0,subject: '',score: ''},computed:{// 计算总分totalScoreSum(){this.totalScore = this.list.reduce(function(x, y){return x + y.score;},0);return this.totalScore;},//平均分averageScore(){let num = this.totalScore / this.list.length; // 结果为小数// console.log(typeof num);if(this.totalScore === 0){return 0;// 防止出现NaN}else{return num.toFixed(2); //保留两位小数}}},methods:{delOne(id){// console.log('删除一个')this.list = this.list.filter(function(item){return item.id !== id;})},// 添加addOne(){console.log('您点击了一次')// 非空检验if(!this.subject){alert('请输入科目');return;};// 类型校验,防止汉字英文什么的if (typeof this.score !== 'number') {alert('请输入正确的成绩');return;};this.list.push({id: this.list.length + 1,// id: +new Date(),subject: this.subject,score: this.score});this.subject = '';this.score = '';}}})</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" /><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"><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: 62 },{ id: 7, subject: '数学', score: 89 },{ 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) {// console.log(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侦听器
1.基础语法
newValue:新值,就是obj这个属性的全部内容
oldValue:老值
注意{{result}}
<!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>
2.完整写法
对象中所有属性的变化都能响应到
默认只有在数据触发的时候才会调用handler(),加上immediate,就会立刻执行一次
<!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: {// words: ''obj: {words: '',lang: 'italy'},result: '', // 翻译结果// timer: null // 延时器id},// 具体讲解:(1) watch语法 (2) 具体业务实现watch: {obj:{deep: true,immediate: true,handler (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: newValue});this.result = res.data.data;},300)}}// 该方法会在数据变化时调用执行// 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.data// console.log(res.data.data)// }, 300)// }}})</script></body>
</html>
综合案例
1.渲染
(太卡了,写不动了,这个草稿我先提交了,我重新开一个)