个人微信公众号:程序猿的月光宝盒
一. 计算属性
1.1. 计算属性的本质
-
每个计算属性都包含一个getter和一个setter
-
一般情况下我们写的都是getter方法
-
在某些情况下,你也可以提供一个setter方法(不常用)
-
在需要写setter的时候,代码如下
1.2. 计算属性和methods对比
methods和computed看起来都可以实现我们的功能,
那么为什么还要多一个计算属性这个东西呢?
原因:
-
计算属性在多次使用时, 只会调用一次.
-
它是有缓存的
〇.ES6补充
0.1. let/var
事实上var的设计可以看成JavaScript语言设计上的错误. 但是这种错误多半不能修复和移除, 以为需要向后兼容.
大概十年前, Brendan Eich就决定修复这个问题, 于是他添加了一个新的关键字: let.
我们可以将let看成更完美的var
块级作用域
JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关.
针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。
而在for中使用var时,我们往往需要这样,因为在ES5中 var 只有function有作用域
但是在ES6后,用let就简单多了
0.2 const的使用
const这个关键字
在很多语言中已经存在, 比如C/C++中, 主要的作用是将某个变量修饰为常量.
在JavaScript中也是如此, 使用const修饰的标识符为常量, 不可以再次赋值.
什么时候使用?
修饰的标识符不会被再次赋值时, 就可以使用const来保证数据的安全性.
0.2.1. 人生建议
在ES6开发中,优先使用const
, 只有需要改变某一个标识符的时候才使用let
.
使用注意点
const a = 20;
a = 30; //错误:不可以被修改
const name;//错误:没有赋值
0.3 对象字面量增强写法
ES6中,对对象字面量进行了很多增强。
属性初始化简写和方法的简写:
二. 事件监听 v-on
v-on介绍
作用:绑定事件监听器
语法糖:@
预期:Function | Inline Statement | Object
参数:event
2.1. 事件监听基本使用
这里,我们用一个监听按钮的点击事件,来简单看看v-on的使用
下面的代码中,使用了v-on:click="counter++”
另外,我们可以将事件指向一个在methods中定义的函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>{{counter}}</h2>
<!--<h2 v-bind:title></h2>-->
<!--<h2 :title></h2>-->
<!--<button v-on:click="counter++">+</button>-->
<!--<button v-on:click="counter--">-</button>-->
<!--<button v-on:click="increment">+</button>-->
<!--<button v-on:click="decrement">-</button>-->
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})
</script>
</body>
</html>
2.2. 参数问题
- btnClick
- btnClick(event)
- btnClick(abc, event) -> $event 传入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--1.事件调用的方法没有参数-->
<button @click="btn1Click()">按钮1</button>
<button @click="btn1Click">按钮1</button>
<!--2.在事件定义时, 写方法时省略了小括号, 但是方法本身是需要一个参数的, 这个时候, Vue会默认将浏览器生产的event事件对象作为参数传入到方法-->
<!--<button @click="btn2Click(123)">按钮2</button>-->
<!--<button @click="btn2Click()">按钮2</button>-->
<button @click="btn2Click">按钮2</button>
<!--3.方法定义时, 我们需要event对象, 同时又需要其他参数-->
<!-- 在调用方式, 如何手动的获取到浏览器参数的event对象: $event-->
<button @click="btn3Click(abc, $event)">按钮3</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
abc: 123
},
methods: {
btn1Click() {
console.log("btn1Click");
},
btn2Click(event) {
console.log('--------', event);
},
btn3Click(abc, event) {
console.log('++++++++', abc, event);
}
}
})
// 如果函数需要参数,但是没有传入, 那么函数的形参为undefined
// function abc(name) {
// console.log(name);
// }
//
// abc()
</script>
</body>
</html>
2.3. 修饰符
-
.stop 调用 event.stopPropagation() 停止冒泡
-
.prevent 阻止默认行为
-
.enter 键修饰符,键别名
-
.once 只监听一次
-
.native 监听组件根元素的原生事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--1. .stop修饰符的使用,调用 event.stopPropagation() 就是点了之后不显示点div-->
<div @click="divClick">
aaaaaaa
<button @click.stop="btnClick">按钮</button>
</div>
<!--2. .prevent修饰符的使用 调用 event.preventDefault() 阻止默认行为,以下 是阻止表单默认提交-->
<br>
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
<!--3. .监听某个键盘的键帽,这里是监听回车按键 把回车按键去掉就都监听了-->
<input type="text" @keyup.enter="keyUp">
<!--4. .once修饰符的使用 只触发一次回调-->
<button @click.once="btn2Click">按钮2</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
console.log("btnClick");
},
divClick() {
console.log("divClick");
},
submitClick() {
console.log('submitClick');
},
keyUp() {
console.log('keyUp');
},
btn2Click() {
console.log('btn2Click');
}
}
})
</script>
</body>
</html>
三. 条件判断
3.1. v-if/v-else-if/v-else
这三个指令与JavaScript的条件语句if、else、else if类似。
Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件
简单案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
<!--计算属性 直接计算好了返回 不写在dom里-->
<h1>{{result}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 99
},
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀'
} else if (this.score >= 80) {
showMessage = '良好'
}
// ...
return showMessage
}
}
})
</script>
</body>
</html>
v-if的原理:
v-if后面的条件为false时,对应的元素以及其子元素不会渲染。
也就是根本不会有对应的标签出现在DOM中。
3.2. 登录小案例
用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模拟登录方式切换</title>
</head>
<body>
<div id="vm">
<div v-if="showTag">
<label for="userName">用户账号</label>
<input id="userName" placeholder="用户账号">
<!--<input id="userName" placeholder="用户账号" key="name">-->
</div>
<div v-else>
<label for="userMail">用户邮箱</label>
<input id="userMail" placeholder="用户邮箱">
<!--<input id="userMail" placeholder="用户邮箱" key="email">-->
</div>
<button @click="showTag = !showTag">切换登录方式</button>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el:'#vm',
data:{
showTag:true
}
})
</script>
</html>
小问题:
如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
但是按道理讲,我们应该切换到另外一个input元素中了。
在另一个input元素中,我们并没有输入内容。
为什么会出现这个问题呢?
问题解答:
这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。
解决方案:
如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
并且我们需要保证key的不同
3.3. v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--v-if: 当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中-->
<h2 v-if="isShow" id="aaa">{{message}}</h2>
<!--v-show: 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none-->
<h2 v-show="isShow" id="bbb">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isShow: true
}
})
</script>
</body>
</html>
v-show
的用法和v-if
非常相似,也用于决定一个元素是否渲染:
v-if和v-show对比
相同:
v-if
和v-show
都可以决定一个元素是否渲染
不同:
v-if
当条件为false时,压根不会有对应的元素在DOM中。
v-show
当条件为false时,仅仅是将元素的display属性设置为none而已。
开发中如何选择呢?
当需要在显示与隐藏之间切换很频繁时,使用v-show
当只有一次切换时,通过使用v-if
四. 循环遍历
4.1. 遍历数组
当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。
v-for的语法类似于JavaScript中的for循环。
格式如下:item in items的形式。
如果在遍历的过程中不需要使用索引值
v-for="movie in movies"
依次从movies中取出movie,并且在元素的内容中,我们可以使用Mustache语法,来使用movie
如果在遍历的过程中,我们需要拿到元素在数组中的索引值呢?
语法格式:v-for=(item, index) in items
其中的index就代表了取出的item在原数组的索引值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--1.在遍历的过程中,没有使用索引值(下标值)-->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<!--2.在遍历的过程中, 获取索引值-->
<ul>
<li v-for="(item, index) in names">
{{index+1}}.{{item}}
</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
names: ['why', 'kobe', 'james', 'curry']
}
})
</script>
</body>
</html>
4.2. 遍历对象中的属性
比如某个对象中存储着你的个人信息,我们希望以列表的形式显示出来。
-
value
-
value, key
-
value, key, index
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!--1.在遍历对象的过程中, 如果只是获取一个值, 那么获取到的是value--> <ul> <li v-for="item in info">{{item}}</li> </ul> <!--2.获取key和value 格式: (value, key) --> <ul> <li v-for="(value, key) in info">{{value}}-{{key}}</li> </ul> <!--3.获取key和value和index 格式: (value, key, index) --> <ul> <li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li> </ul> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { info: { name: 'kin', age: 18, height: 1.88 } } }) </script> </body> </html>
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
为什么需要这个key属性呢(了解)?
这个其实和Vue的虚拟DOM的Diff算法有关系。
这里我们借用React’s diff algorithm中的一张图来简单说明一下:
当某一层有很多相同的节点时,也就是列表节点时,我们希望插入一个新的节点
我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的。
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识
Diff算法就可以正确的识别此节点
找到正确的位置区插入新的节点。
所以一句话,key的作用主要是为了高效的更新虚拟DOM。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <ul> <li v-for="item in letters" :key="item">{{item}}</li> </ul> </div> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { letters: ['A', 'B', 'C', 'D', 'E'] } }) </script> </body> </html>
4.3. 数组哪些方法是响应式的
push():在最后面添加元素
unshift():在数组最前面添加元素
pop():删除数组中的最后一个元素
shift():删除数组中的第一个元素
splice():删除,插入,替换
sort() :排序
reverse():倒序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
letters: ['a', 'b', 'c', 'd']
},
methods: {
btnClick() {
// 1.push方法在最后面添加元素
// this.letters.push('aaa')
// this.letters.push('aaaa', 'bbbb', 'cccc')
// 2.pop(): 删除数组中的最后一个元素
// this.letters.pop();
// 3.shift(): 删除数组中的第一个元素
// this.letters.shift();
// 4.unshift(): 在数组最前面添加元素
// this.letters.unshift()
// this.letters.unshift('aaa', 'bbb', 'ccc')
// 5.splice作用: 删除元素/插入元素/替换元素
// 第一个参数,index开始位置
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
// 替换元素: 第二个参数, 表示我们要替换几个元素,
// 后面的参数:用于替换前面的元素
// 插入元素: 第二个参数, 传入0,
// 后面的参数:要插入的元素
// splice(start)
// splice(start):
this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
// this.letters.splice(1, 0, 'x', 'y', 'z')
// 6.sort() 排序
// this.letters.sort()
// 7.reverse() 倒序
// this.letters.reverse()
// 注意: 通过索引值修改数组中的元素
// this.letters[0] = 'bbbbbb';
// this.letters.splice(0, 1, 'bbbbbb')
// set(要修改的对象, 索引值, 修改后的值)
// Vue.set(this.letters, 0, 'bbbbbb')
}
}
})
// function sum(num1, num2) {
// return num1 + num2
// }
//
// function sum(num1, num2, num3) {
// return num1 + num2 + num3
// }
// function sum(...num) {
// console.log(num);
// }
//
// sum(20, 30, 40, 50, 601, 111, 122, 33)
</script>
</body>
</html>
五. 书籍购物车案例
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="vm">
<!--判断有没有长度,没有就是空的-->
<div v-if="books.length">
<table>
<thead>
<tr>
<th></th>
<th>书名</th>
<th>单价</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in books">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<!--showPrice 是过滤器-->
<td>{{item.price | showPrice}}</td>
<td>
<!--v-bind:disabled 条件true时 让按钮失效-->
<button @click="sub(index)" v-bind:disabled="item.count <= 1">-</button>
{{item.count}}
<button @click="add(index)">+</button>
</td>
<td>
<button @click="remove(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<h2>总价格:{{totalPrice | showPrice}}</h2>
</div>
<h2 v-else>购物车为空</h2>
</div>
</body>
<script src="../js/vue.js"></script>
<script src="main.js"></script>
</html>
main.js
const vm = new Vue({
el:'#vm',
data:{
books:[
{
id: 1,
name:'「人間失格」',
price: 85.42,
count:1
},
{
id: 2,
name:'「白夜行」',
price: 89.00,
count:1
},
{
id: 3,
name:'「解忧杂货铺」',
price: 65.00,
count:1
},
{
id: 4,
name:'「天龙八部」',
price: 185.00,
count:1
}
]
},
methods:{
sub(index){
this.books[index].count--
},
add(index){
this.books[index].count++
},
remove(index){
this.books.splice(index,1)
}
},
computed:{
totalPrice(){
let totalPrice = 0;
for (let book of this.books){
totalPrice += book.price * book.count
}
return totalPrice;
}
},
//过滤器
filters:{
// 自动把参数传进来
showPrice(price){
// .toFixed(2)保留两位小数
return '¥' + price.toFixed(2)
}
}
});
index.css
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th, td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}