事件监听
在前端开发中,我们需要经常和用于交互。
-
这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
-
在Vue中如何监听事件呢?使用v-on指令
v-on介绍
- 作用:绑定事件监听器
- 缩写:@
- 预期:Function | Inline Statement | Object
- 参数:event
v-on基础
这里,我们用一个监听按钮的点击事件,来简单看看v-on的使用
下面的代码中,我们使用了v-on:click="counter++”
另外,我们可以将事件指向一个在methods中定义的函数
<div id="app">
<h2>{{number}}</h2>
<button v-on:click="number++">按钮1</button>
<button v-on:click="changeNumber">按钮2</button>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
number: ''
},
methods: {
changeNumber() {
this.number++
}
}
})
</script>
点击哪一个都可以让number增加
注:v-on也有对应的语法糖
v-on:click可以写成@click
<button @click="number++">按钮1</button>
<button @click="changeNumber">按钮2</button>
效果也是一样的
v-on参数
-
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
-
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。
- 但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
-
情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
没有传参数
<div id="app">
<h2>{{number}}</h2>
<button @click="btnClick">按钮</button>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
number: ''
},
methods: {
// 如果不传递参数,默认vue给我传一个参数,这个参数就是event
btnClick(e) {
console.log(e)
}
}
})
</script>
如果传了参数,又想要event
<div id="app">
<h2>{{number}}</h2>
<button @click="btnClick(3, $event)">按钮</button>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
number: ''
},
methods: {
// 如果传参数了,number这个值就是那个参数,如果想再要event,name就需要传过来
btnClick(number, event) {
console.log(number, event)
}
}
})
</script>
v-on修饰符
在某些情况下,我们拿到event的目的可能是进行一些事件处理。
但是不想写一个函数,想更简单点,这个vue也帮我们实现了
Vue提供了修饰符来帮助我们方便的处理一些事件:
- .stop - 调用 event.stopPropagation()。阻止冒泡
- .prevent - 调用 event.preventDefault()。阻止默认事件
- .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
- .native - 监听组件根元素的原生事件。
- .once - 只触发一次回调。
<div id="app">
<!-- 阻止冒泡 -->
<button @click.stop="stopBubble">按钮</button>
<!-- 阻止默认行为 -->
<button @click.prevent="stopDefault">按钮</button>
<!-- 阻止默认行为,如果不需要事件函数,也可以直接这样写 -->
<button @click.prevent>按钮</button>
<!-- 既阻止默认行为,又阻止冒泡 -->
<button @click.prevent.stop="stop">按钮</button>
<!-- enter键盘修饰符 -->
<input @keyup.enter="btnEnter1" type="text">
<!-- enter键盘修饰符 -->
<input type="text" @keyup.13="btnEnter2">
<!-- 点击回调只会触发一次 -->
<button @click.once="onlyOne">按钮</button>
</div>
v-on绑定多个事件
<div id="app">
<div v-on="{mousemove:isMouseMove, click:isClick}" class="box"></div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
},
methods: {
isMouseMove() {
console.log('我是isMouseMove')
},
isClick() {
console.log('我是isClick')
}
}
})
</script>
一个事件绑定多个函数
<div id="app">
<div @click="one(), two()" class="box"></div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
},
methods: {
one() {
console.log('我是one')
},
two() {
console.log('我是two')
}
}
})
</script>
条件渲染
v-if、v-else-if、v-else
- 这三个指令与JavaScript的条件语句if、else、else if类似。
- Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素或组件
<div id="app">
<div v-if="score >= 90">优秀</div>
<div v-else-if="score >= 80">良好</div>
<div v-else="score >= 60">及格</div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
score: 80
}
})
</script>
注意:
- 指令后面跟的是表达式,不需要花括号。
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。
v-if的原理:
- v-if后面的条件为false时,对应的元素以及其子元素不会渲染。
- 也就是根本没有不会有对应的标签出现在DOM中。
条件渲染案例
- 用户再登录时,可以切换使用用户账号登录还是邮箱地址登录。
<div id="app">
<span v-if="type === 'userId'">
<label for="userId">用户账号:</label>
<input type="text" placeholder="请输入账号" id="userId">
</span>
<span v-else>
<label for="email">邮箱地址:</label>
<input type="email" placeholder="请输入邮箱地址" id="email">
</span>
<button @click="changeType">切换类型</button>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
type: 'userId'
},
methods: {
changeType() {
this.type = this.type === 'userId' ? 'userEmail' : 'userId'
}
}
})
</script>
案例小问题
- 如果我们在有输入内容的情况下,切换了类型,我们会发现文字依然显示之前的输入的内容。
- 但是按道理讲,我们应该切换到另外一个input元素中了。
- 在另一个input元素中,我们并没有输入内容。
为什么呢?
- 这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。
- 在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。
解决方案:
- 如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key
- 并且我们需要保证key的不同
v-show
v-show的用法和v-if非常相似,也用于决定一个元素是否渲染:
<div id="app">
<h2 v-show="isShow">我要不要显示呢?</h2>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
isShow: true
}
})
</script>
会显示出来
如果是false就什么都不显示
v-if和v-show都可以决定一个元素是否渲染,那么开发中我们如何选择呢?
区别
- v-if当条件为false时,压根不会有对应的元素在DOM中(创建和销毁节点)。
- v-show当条件为false时,仅仅是将元素的display属性设置为none而已。
开发中如何选择呢?
- 当需要在显示与隐藏之间切片很频繁时,使用v-show
- 当只有一次切换时,通过使用v-if
其实 v-if 是重新重建一个标签,而v-show仅仅是把代码的css修改了 display:none
对性能而言,如果使用v-if 在每一次切换的时候都会重新创建一个标签或组件,然后卸载一个标签或组件,这样做对性能的要求比较高
而v-show仅仅是display:none,打开,对性能的要求相对较小,但是第一次加载组件的时候v-show是会被创建的,所以就有了上面那个结论,在开发中如何选择。
v-for循环遍历
v-for可以遍历数组,字符串,对象,number
v-for遍历数组
当我们有一组数据需要进行渲染时,我们就可以使用v-for来完成。
v-for的语法类似于JavaScript中的for循环。
格式如下:item in items的形式。(item of items 也可以)
如果在遍历的过程中不需要使用索引值
- v-for=“movie in movies”
- 依次从movies中取出movie,并且在元素的内容中,我们可以使用Mustache语法,来使用movie
如果在遍历的过程中,我们需要拿到元素在数组中的索引值呢?
- 语法格式:v-for=(item, index) in items
- 其中的index就代表了取出的item在原数组的索引值。
<div id="app">
<ul>
<!-- 如果需要需要,每一项和序号之间用逗号隔开,并且需要用括号括起来 -->
<li v-for="(item, index) in data">序号:{{index}}---->内容:{{item}}</li>
</ul>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
data: [
'乡村爱情1',
'乡村爱情2',
'乡村爱情3',
'乡村爱情4'
]
}
})
</script>
v-for遍历对象
<div id="app">
<ul>
<!-- 循环对象的话,有三个值可以使用,第一个是键值,第二个值是键名,第三个值是序号 -->
<li v-for="(item, key, index) in obj">序号:{{index}}---->键名:{{key}}; 键值----->{{item}}</li>
</ul>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
obj: {
name: 'vue',
age: '5',
initiator: '尤雨溪'
}
}
})
</script>
v-for遍历字符串
<div id="app">
<ul>
<!-- 循环字符串的话,有两个值可以使用,第一个是内容,第二个值是序号 -->
<li v-for="(item, index) in string">序号:{{index}}---->内容:{{item}}</li>
</ul>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
string:'hello world'
}
})
</script>
v-for循环数字
<div id="app">
<ul>
<!-- 循环数字的话,有两个值可以使用,第一个是内容,第二个值是序号 -->
<li v-for="(item, index) in 10">序号:{{index}}---->内容:{{item}}</li>
</ul>
</div>
组件的key属性
官方推荐我们在使用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。
如果我要往ABCDE里面插入一个F
是左边这个顺序,就是本来一个标签对应一个字母,
但是我新来一个,这个时候,就需要重新编排,比如以前的li1对应A,li2对应B
我要在B和C之间插入C,这个时候,li3对应的C就变了,编程li3对应F,li4对应C,依次往后排了
这样的效率比较低,那么加了key会怎么样呢
他就会按照右边,每一个key都对应一个li,也对应一个字母,比如给li1加一个key它的值是A,那么它类似绑定了,就算你再来一个,也不会重新创建li,并且给li加一个值,不会这样的,他会怎么样呢,如果你在c和d之间加入z,每一个li都有key,都有绑定的值,他们都不变,只是在cd之间创建一个li,再绑定上key,绑定上值。这样的效率更高。
上面的li,li1都是举例,也就是
- 有很多,li1表示第一个li标签
所以,key必须是唯一的,否则没有意义
当然如果在最后一个加,加不加key都一样
检测数组更新
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
这些方法和原生js类似。
this.letters.push('aaa')
类似这种,可以和普通的js一样使用
案例:
实现上图效果
<div id="app"> <table> <thead> <tr> <td></td> <td>书籍名称</td> <td>出版日期</td> <td>价格</td> <td>购买数量</td> <td>操作</td> </tr> </thead> <tbody> <tr v-for="(item, index) in nv" :key="item.name"> <td>{{index}}</td> <td>{{item.name}}</td> <td>{{item.time}}</td> <td>{{item.price}}</td> <td> <button @click="decrease(index)">-</button> {{item.num}} <button @click="increase(index)">+</button> </td> <td> <button @click="del(index)">移除</button> </td> </tr> </tbody> </table> <p>总价:{{allPrice}}</p> </div> <script> let app = new Vue({ el: "#app", data: { nv: [ { name: '《算法导论》', time: '2006-9', price: '¥85.00', num: 1, }, { name: '《unix编程艺术》', time: '2006-2', price: '¥59.00', num: 1, }, { name: '《编程珠玑》', time: '2008-10', price: '¥39.00', num: 1, }, { name: '《编程大全》', time: '2006-3', price: '¥128.00', num: 1, } ], }, computed: { allPrice() { return this.nv.reduce((item, curr) => { let price = curr.price.substr(1) let arr = price.split('.') return item + arr[0] * curr.num }, 0) } }, methods: { increase(index) { this.nv[index].num += 1; }, decrease(index) { this.nv[index].num -=1; if(this.nv[index].num < 0) { this.nv[index].num = 0 } }, del(index) { this.nv.splice(index, 1) } } }) </script>
v-model
表单绑定v-model
表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
Vue中使用v-model指令来实现表单元素和数据的双向绑定。
- 绑定input
<div id="app"> <input type="text" v-model="message"> <p>输入的内容是:{{message}}</p> </div> <script> let app = new Vue({ el: '#app', data: { message:'' } }) </script>
案例的解析:
-
当我们在输入框输入内容时
-
因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
-
当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。
-
所以,通过v-model实现了双向的绑定。
-
绑定textarea
<div id="app"> <textarea v-model="message"></textarea> <p>输入的内容是:{{message}}</p> </div> <script> let app = new Vue({ el: '#app', data: { message:'' } }) </script>
- 绑定 radio
<div id="app"> <label for="male"> <!-- 这里的v-model绑定的是value, 这里的value是动态绑定,所以其实这个v-model绑定的是abc,所以其实是abc的值给到了gender。 --> <input type="radio" v-model="gender" :value="abc" id="male" />男 </label> <label for="female"> <input type="radio" value="female" v-model="gender" id="female" />女 </label> <p>您的选择:{{gender}}</p> </div> <script> let app = new Vue({ el: '#app', data: { gender: '', abc: 'male' } }) </script>
v-model:checkbox
复选框分为两种情况:单个勾选框和多个勾选框
单个勾选框:
- v-model即为布尔值。
- 此时input的value并不影响v-model的值。
<div id="app"> <label for="check"> <input type="checkbox" v-model="checked" id="check">同意协议 </label> </div> <script> let app = new Vue({ el: '#app', data: { checked: false } }) </script>
多个复选框:
-
当是多个复选框时,因为可以选中多个,所以对应的data中属性是一个数组。
-
当选中某一个时,就会将input的value添加到数组中。
<div id="app"> <label ><input type="checkbox" v-model="hobbies" value="篮球">篮球</label> <label ><input type="checkbox" v-model="hobbies" value="足球">足球</label> <label ><input type="checkbox" v-model="hobbies" value="台球">台球</label> <p>您选中的爱好:{{hobbies}}</p> </div> <script> let app = new Vue({ el: '#app', data: { checked: false, //这里必须是一个数组 hobbies: [] } }) </script>
v-model:select
和checkbox一样,select也分单选和多选两种情况。
单选:只能选中一个值。
v-model绑定的是一个值。
当我们选中option中的一个时,会将它对应的value赋值到mySelect中
<div id="app"> <select v-model="select"> <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> <p>当前选择的是:{{select}}</p> </div> <script> let app = new Vue({ el: '#app', data: { select: '' } }) </script>
多选:可以选中多个值。
v-model绑定的是一个数组。
当选中多个值时,就会将选中的option对应的value添加到数组mySelects中
<div id="app"> <!-- multiple是用来多选的,ctrl+鼠标进行多选 --> <select v-model="select" multiple > <option disabled value="">请选择</option> <option>A</option> <option>B</option> <option>C</option> </select> <p>当前选择的是:{{select}}</p> </div> <script> let app = new Vue({ el: '#app', data: { select: [] } }) </script>
值绑定
值绑定就是动态的给value赋值而已
-
我们前面的value中的值,可以回头去看一下,都是在定义input的时候直接给定的。
-
但是真实开发中,这些input的值可能是从网络获取或定义在data中的。
-
所以我们可以通过v-bind:value动态的给value绑定值。
-
这不就是v-bind在input中的应用吗?
<div id="app"> <!-- 这里的v-bind是为了和下面的id进行绑定,这样点击文字就能选中了 --> <!-- 这里对likes进行了循环 --> <label v-bind:for="item" v-for="item in likes" > <!-- 这里的v-bind绑定的value是为了让v-model获取到值,绑定的是每一个item,这样我们的v-model就能用数组的方式获取到点击的值了 --> <input type="checkbox" v-bind:id="item" v-bind:value="item" v-model="myLikes">{{item}} </label> <p>当前选择了:{{myLikes}}</p> </div> <script> let app = new Vue({ el: '#app', data: { myLikes:[], likes: ['足球', '篮球', '乒乓球', '台球', '羽毛球'] } }) </script>
v-model的修饰符
lazy修饰符:
-
默认情况下,v-model默认是在input事件中同步输入框的数据的。
-
也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变。
-
lazy修饰符可以让数据在失去焦点或者回车时才会更新:
<div id="app"> <input type="text" v-model.lazy="message"> <p>输入的内容是:{{message}}</p> </div> <script> let app = new Vue({ el: '#app', data: { message: '' } }) </script>
number修饰符:
- 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
- 但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
- number修饰符可以让在输入框中输入的内容自动转成数字类型
对比一下
没有加修饰符
<div id="app"> <input type="number" v-model="message"> <p>输入的内容是:{{typeof message}}</p> </div> <script> let app = new Vue({ el: '#app', data: { message: '' } }) </script>
使用修饰符
<div id="app"> <input type="number" v-model.number="message"> <p>输入的内容是:{{typeof message}}</p> </div> <script> let app = new Vue({ el: '#app', data: { message: '' } }) </script>
trim修饰符:
- 如果输入的内容首尾有很多空格,通常我们希望将其去除
- trim修饰符可以过滤内容左右两边的空格
对比一下
<div id="app"> <input type="text" v-model="message"> <p>输入的内容是:{{message}}哟哟哟</p> </div> <script> let app = new Vue({ el: '#app', data: { message: '' } }) </script>
加上修饰符
<div id="app"> <input type="text" v-model.trim="message"> <p>输入的内容是:{{message}}哟哟哟</p> </div> <script> let app = new Vue({ el: '#app', data: { message: '' } }) </script>