文章目录
Day02 ES6语法基础和Vue指令(中)
一、let/const
let用来定义变量, const用来定义常量。
let
var的缺陷:没有块级作用域, 可以在任意地方使用定义的var变量。
ES6之前if和for没有块级作用域,变量可以任意使用。举个例子,实现点击按钮时弹出是第几个按钮时会产生一些问题, 现象是不管点击哪个按钮都会提示点击了最后一个按钮,点击效果如下:
代码
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
//ES5
var btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
alert('点击了' + i + '个按钮');
})
}
</script>
因为var变量在for中没有块级作用域,所以函数调用i变量时会调用外层定义的i,而i最终循环完毕的值是3,所以发生错误。
解决办法
- 使用函数闭包解决
ES5中函数是有作用的,可以借助函数闭包解决这个问题。因为使用的是函数自己定义的变量,所以不会发生数据共享问题
var btns = document.getElementsByTagName('button');
//ES5闭包
for (var i = 0; i < btns.length; i++) {
// 将i传进去
(function (i) {
btns[i].addEventListener('click', function () {
alert('点击了' + i + '个按钮');
})
})(i)
}
- 使用let定义变量
let定义的变量在for和if中有块级作用域
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
alert('点击了' + i + '个按钮');
})
}
效果
const
将标识符修饰为常量,其值不允许改变。
建议首选const,只有需要改变时使用let,这样符合代码的规范性。
注意:const定义的标识符必须被初始化;const定义的对象不能修改,但是对象的属性可以改变;
对象字面量增强写法
字面量:定义的对象使用的{}。
一般写法
// const obj = new Object();
cosnt obj = {
name: 'admin',
age: 23,
eat: function() {
...
},
run: function() {
...
}
};
增强写法
const name = 'admin';
const age = 23;
cosnt obj = {
name,
age,
eat () {
...
},
run () {
...
}
};
二、v-on事件监听
因为前端经常会和用户进行交互,所以会监听点击、拖拽、键盘等事件。vue中使用v-on实现事件的监听。
基本用法
前面计数器案例已经演示过了,可以使用更加简洁的写法,代码如下:
<div id="app">
<h2>{{counter}}</h2>
<!--<button v-on:click="add()">+</button>
<button v-on:click="sub()">-</button>-->
<button @click="add()">+</button>
<button @click="sub()">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
counter: 0
},
methods: {
add() {
this.counter++;
},
sub() {
this.counter--;
}
}
})
</script>
当没有参数是括号可以省略。
v-on参数传递
- 当没有参数时,有括号和没括号打印结果相同
<div id="app">
<!--<button @click="printStr()">按钮1</button>-->
<button @click="printStr">按钮1</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello, Vue!'
},
methods: {
printStr() {
console.log(this.message);
}
}
})
</script>
两个按钮点击后都会打印"Hello, Vue!"。
- 当只有一个参数时,去掉括号会打印event对象(事件对象,可以获得鼠标、键盘等事件的状态)
<div id="app">
<button @click="printStr1()">按钮1</button>
<button @click="printStr1(123)">按钮2</button>
<button @click="printStr1">按钮3</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello, Vue!'
},
methods: {
printStr1(abc) {
console.log(abc);
}
}
})
</script>
结果如下图所示
当不传参数时,abc为默认值undefined;
当传递存在的参数时,会打印参数结果;
当省略括号时,会默认传递event对象。
- 当传递多个参数时,去掉括号传递会将第一个参数作为event对象;传递event对象需要使用$event
<div id="app">
<button @click="printStr2(123, event)">按钮1</button>
<button @click="printStr2">按钮2</button>
<button @click="printStr2(123, $event)">按钮3</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello, Vue!'
},
methods: {
printStr2(abc, event) {
console.log("多个参数传递:", abc, event)
}
}
})
</script>
结果如下
- 当传递event时,会当做普通变量,由于普通变量未定义所以报错;
- 当省略括号时,会将第一个变量当做event对象打印,第二个打印默认值undefined;
- 当传递正确的参数和$event时会打印正确结果
v-on修饰符
stop修饰符
用于禁止冒泡事件发生(当div存在监听事件时,在div里面button的监听事件触发时div的事件也会出发)。
prevent修饰符
阻止默认事件提交,比如在表单中,需要使submit按钮的点击事件生效,就需要禁用submit按钮的默认提交事件。
enter修饰符
用在监听键盘按下(keyDown)或抬起事件(keyUp)中,只监听回车事件发生。也可以只用键盘代号表示键盘上的任意键。
once修饰符
事件只会触发一次。
这四个修饰符的代码示例如下:
<div id="app">
<!-- 阻止事件冒泡 -->
<div @click="divClick">
aaaaaaa
<!-- <button @click="btnClick">按钮</button> -->
<button @click.stop="btnClick">按钮1</button>
</div>
<!-- 阻止默认事件提交 -->
<form action="abc">
<input type="submit" @click.prevent="btnClick" value="提交">
</form>
<br>
<!-- 监听回车事件 -->
<input type="text" @keyDown.enter="keyDo">
<!-- button只会点击一次 -->
<button @click.once="btnClick">按钮2</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "Hello, Vue!"
},
methods: {
divClick() {
console.log("div被点击");
},
btnClick() {
console.log("btn被点击");
},
keyDo() {
console.log("键盘事件触发");
}
}
})
</script>
其他修饰符需要自己了解。
三、条件判断指令
v-if/v-else指令
类似于Java中的if/else语句
<div id="app">
<h2 v-if="isShow">
v-if为true时显示
</h2>
<h2 v-else>
v-if为false时显示
</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello, Vue!',
isShow: true
}
})
</script>
当isShow为true时显示"v-if为true时显示",当isShow为false时显示"v-if为false时显示"。
v-if-else指令
类似Java中的if-else语句,在多个条件中,当其中一个条件满足时不会执行其他判断语句
打印成绩等级案例
<div id="app">
<h2 v-if="scorce>=90">优秀</h2>
<h2 v-else-if="scorce>=70">良好</h2>
<h2 v-else-if="scorce>=60">及格</h2>
<h2 v-else>不及格</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
scorce: 85
}
})
</script>
打印结果为良好。
切换登录方式案例
目前有用户登录和邮箱登录,当点击按钮后切换邮箱登录方式。
代码
<div id="app">
<span v-if="!isAlter">
<label for="username">用户名</label>
<!-- 存在标签复用问题 -->
<!-- <input id="username" type="text" placeholder="用户名">-->
<input id="username" type="text" placeholder="用户名" key="username">
</span>
<span v-else>
<label for="email">邮箱</label>
<input id="email" type="text" placeholder="邮箱" key="password">
</span>
<button @click="isAlter=!isAlter">切换登录方式</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isAlter: false
}
})
</script>
问题
存在标签复用问题,当点击切换按钮式后,用户输入框的数据会带到邮箱输入框。
因为vue底层通过虚拟DOM(Virtual DOM)渲染浏览器。当渲染邮箱输入框时,只会从Virtual DOM中获取不同的数据进行渲染。因为value值没有改变,所以会在邮箱输入框里显示。
解决方法
使用key关键字用于区分不同输入框。
<input id="username" type="text" placeholder="用户名" key="username">
v-if和v-show的区别
v-if生效时不会在DOM中显示标签,而v-show生效时会在DOM中显示标签并增加一个行内样式display:none。当需要频繁切换时,使用v-show,性能更高;当只进行一次切换时使用v-show。
四、v-for指令
遍历数组
遍历普通数组
arr: ['marry','tom','joey','Jerry']
代码
<ul>
<li v-for="item in arr">{{item}}</li>
</ul>
<!--index是索引-->
<ul>
<li v-for="(item,index) in arr">{{index}}.{{item}}</li>
</ul>
遍历对象数组
objArr: [ {name:"張三", age:10}, {name:"張三三", age:20}, {name:"李四", age:30}]
代码
<ul>
<li v-for="item in objArr">{{item.name}}</li>
</ul>
遍历对象元素
对象数据如下
info:{ name: 'Marry', age: 35, email: '2294678909@qq.com', gender: 'female'}
获取值
<ul><li v-for="item in info">{{item}}</li></ul>
获取键和值
<ul><li v-for="(value,key) in info">{{key}}-{{value}}</li></ul>
获取索引(很少用)
<ul><li v-for="(value,key,index) in info">{{index}}{{key}}-{{value}}</li></ul>
v-for绑定key作用
场景描述
当遍历数组时需要在数组中添加元素,怎么做是最高效的方式来渲染数组列表。
代码实现
<div id="app">
<ul>
<!-- <li v-for="item in arr">{{item}}</li>-->
<li v-for="item in arr" :key="item">{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
arr: [
'a',
'b',
'c',
'd'
]
}
})
</script>
原理解析
virtual DOM是将真实的 DOM 的数据抽取出来,以对象的形式模拟树形结构,diff算法比较的也是virtual DOM
现在要在a和b之间插入f。没有绑定key时, Virtual DOM先把b改为f,c改为b,以此类推,最后在末尾添加一个d元素,效率十分低下。
当绑定了**:key=“唯一标识后”**, 可以正确的识别此节点,找到正确的位置区插入新的节点, 提高了效率。
注意index不能作为唯一标识,因为当插入f时此时对应的index为1,与b的index重复,使得key无效。
五、响应式数组方法
响应式数组函数
- pop():在末尾删除一个元素。
- shift():在首部删除一个元素。
- unshif(…items: T[]):在首部添加一个或多个元素。
- push(…items: T[]):在末尾添加一个或多个元素。
- splice():替换/删除/插入元素(主要取决于第二个参数)。
const app = new Vue({
el: "#app",
data: {
words: [
'A',
'B',
'C',
'D'
]
},
methods: {
insert() { // A B F T C D
// 在第三个位置插入F
this.words.splice(2,0,'F','T');
},
remove() { // A B
// 删除元素
this.words.splice(2, 3)
},
replace() { //A B F T D
// 替换元素
this.words.splice(2,1,'F','T');
}
}
})
第一个参数:开始操作的索引。
第二个参数:等于0时,为插入操作;大于0时,为删除或替换操作。
其他参数:要插入或替换的元素。
关于替换的理解:先删除,后插入。
- sort():排序,参数可以为函数,也可以不进行传参。
对象的属性排序:按person对象的年龄进行操作
const app = new Vue({
el: "#app",
data: {
persons: [
{name: 'admin', age: 24},
{name: 'fyee', age: 34},
{name: 'tomcat', age: 14},
{name: 'tt', age: 43},
]
},
methods: {
sortByAge() { //传入的是两个person对象
this.persons.sort((a, b) => {
//升序排序
return a.age - b.age;
})
}
}
})
- reverse():反转数组。
赋值语句问题
赋值语句无法响应式的改变基本类型的页面数据,但可以改变对象的属性。
const app = new Vue({
el: "#app",
data: {
books: [
'A',
'B',
'C',
3
]
},
methods: {
modifyA() { //页面数据不会改变
this.books[0] = 'AAA';
console.log(this.books)
},
modifyAA() { //页面数据改变
this.books.splice(0, 1, 'AAA')
console.log(this.books)
},
modifyB() { //对象属性改变
this.persons[0].name = 'cetc27';
this.persons[0].age = 88;
},
modifyBB() { //对象改变
const person = {name: 'cetc27', age: 88}
this.persons.splice(0, 1, person)
},
}
})
当通过赋值语句改变基本类型数组时,页面数据不会改变,但是数组元素已经发生变化。
当通过赋值语句改变对象数组时,页面数据会发生改变,本质上对象没有发生改变,改变的是对象的属性。
六、综合案例:购物车
需求说明
- 遍历数组中元素,显示在页面上
- 当购物车没有数据时显示当前购物车为空
- 当购买数量为1时禁用减少按钮
- 实现移除按钮,移除数组中一项
- 打印总价
步骤
1. v-for遍历数组,显示在页面上
<table>
<thead>
<tr>
<th></th>
<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>
<td>{{item.date}}</td>
<td>{{item.price | priceFilter}}</td>
<td>
<button @click="sub(index)" :disabled="item.count<=1">-</button> {{item.count}}
<button @click="add(index)">+</button>
</td>
<td><button @click="remove(index)">移除</button></td>
</tr>
</tbody>
</table>
2. 需要添加过滤器,保留小数点后两位数据
const app = new Vue({
...
filters: {
priceFilter(price) {
return '¥' + price.toFixed(2)
}
}
...
})
3. 添加点击事件, 实现数量增减和移除数组元素功能
const app = new Vue({
...
methods: {
add(index) {
this.books[index].count++;
},
sub(index) {
this.books[index].count--;
},
remove(index) {
this.books.splice(index, 1);
}
}
...
})
4. 通过计算属性显示总价
const app = new Vue({
...
computed: {
totalPrice() {
let result = 0;
for (const book of this.books) {
result += book.price * book.count;
}
return result;
}
/*totalPrice: function () {
let result = 0;
for (const book of this.books) {
result += book.price * book.count;
}
return result;
}*/
}
...
})
<h2>总价格:{{totalPrice | priceFilter}}</h2>
5. 通过v-if控制购物车是否显示
<div v-if="books.length>0">
...
</div>
<div v-else>
<h2>购物车中没有商品</h2>
</div>