一. v-on指令
1. 基础用法
v-on是事件监听的指令, 下面来看简单用法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <h2>{{counter}}</h2> <button v-on:click="add"> + </button> <button v-on:click="sub"> - </button> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { counter: 0 }, methods: { add() { this.counter ++ }, sub() { this.counter -- } } }); </script> </body> </html>
我们给按钮绑定了点击事件. v-on:click = "add"
- v-on: 后面加时间名称
- 需要指定绑定事件的事件名
来看看运行效果
2. 语法糖
我们知道, v-bind指令可以简写为:, 同样v-on也可以简写, 简写为@, 如上写法可以简写如下:
<button @click="add"> + </button> <button @click="sub"> - </button>
3. 事件的参数
- 无参方法
上面的案例都是不带参数. 如果方法没有参数, 那么我们在调用的时候可以省略括号
add方法不带参数, 调用的时候可以有如下两种写法: <button @click="add"> + </button> 或者 <button @click="add()"> + </button>
两种写法都可.
- 有参方法
如果一个方法有参数, 如下案例
<script> var app = new Vue({ el: "#app", data: { num1: 10, num2: 100 }, methods: { print(message) { console.log("打印" + message) } } }); </script>
调用方式1: 那么调用的时候括号里不带参数.
<div id="app"> <button @click="print()"> 打印 </button> </div>
这是方法的参数传入的是undefined.
调用方式2: 不带括号
<div id="app"> <button @click="print"> 打印 </button> </div>
这里和方式1的区别是, 调用方法的括号都省了.
这时会是什么效果呢?
可以看到, 这时不是undefined了, 而是一个MouseEvent鼠标事件.
为什么呢? 其实, 当鼠标点击按钮的时候, 页面会自动生成一个事件, 如果没有传递参数, 那么会自动将这个事件作为参数传递过了, 如果需要调用这个事件, 那么, 可以在方法入参,显示的接收event参数.
调用方式3: 参数中既有普通参数, 又有event参数
这是我们调用的时候, 要使用$event
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <button @click="print"> button1 </button> <button @click="print1('aaa')"> button2 </button> <button @click="print1('aaa', $event)"> button3 </button> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { num1: 10, num2: 100 }, methods: { print(message) { console.log("打印" + message) }, print1(message, event){ console.log("打印" + event + ", message:" + message) }, print2(message, event) { console.log("event:" + event + ", message:" + message) } } }); </script> </body> </html>
然后来看效果
同时将message和event都传递过来了
4. 事件修饰符
- .stop : 调用event.stopPropagation() 阻止冒泡事件
看如下代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app" @click="divClick"> <button @click="btnClick">按钮</button> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "hello" }, methods:{ divClick(){ console.log("divClick") }, btnClick(){ console.log("btnClick") } } }); </script> </body> </html>
div里面有一个btn, div有一个click事件, btn也有一个click事件, 当我们点击btn的时候, 回调用两个方法么?我们来看看效果
确实调用了btn的click()方法, 而且还调用了div的click()方法. 这是事件冒泡机制, 通常我们在页面是要避免这样的情况发生的. 所以会写一个方法阻止事件冒泡.
但是在vue里面, 使用stop修饰符就可以解决这个问题. 在btn按钮的click事件上增加stop修饰符
<div id="app" @click="divClick"> <button @click.stop="btnClick">按钮</button> </div>
这样就阻止了冒泡事件
- .prevent: 调用event.preventDefault() 阻止默认事件
我们现在methods中定义一个方法
stopDefaultEventBtn(){ console.log("stopDefaultEventBtn") }
调用的时候, 我们定义一个submit表单提交按钮, 我们知道表单有自己的提价时间, 点击按钮将跳转到form表单指定的action地址.
<div id="app" @click="divClick"> <button @click.stop="btnClick">阻止冒泡事件</button><br/><br/> <form action="http://www.baidu.com"> <input type = "submit" value="阻止默认事件"></input> </form> </div>
现在我们不想使用submit的自动提交事件, 我们要阻止他, 而是使用我么自定义的stopDefaultEventBtn事件.
<div id="app" @click="divClick"> <button @click.stop="btnClick">阻止冒泡事件</button><br/><br/> <form action="http://www.baidu.com"> <input type = "submit" @click.prevent="stopDefaultEventBtn" value="阻止默认事件"></input> </form> <!-- submit 有自己的模式提交事件, 但通常我们不希望使用默认的提交时间, 而是使用我自定义的事件. --> </div>
这时, 我们在调用方法, 发现不会自动跳转到action指定的事件了, 而是进入到click事件.
但是有个问题, 虽然调用了click指定的事件, 但是依然有事件冒泡, 同时还调用和div的click事件, 这个简单, 在增加阻止冒泡事件就可以了.
<div id="app" @click="divClick"> <button @click.stop="btnClick">阻止冒泡事件</button><br/><br/> <form action="http://www.baidu.com"> <input type = "submit" @click.prevent.stop="stopDefaultEventBtn" value="阻止默认事件"></input> </form> <!-- submit 有自己的模式提交事件, 但通常我们不希望使用默认的提交时间, 而是使用我自定义的事件. --> </div>
- .(keyCode | keyAlias): 只当事件是从特定键触发时才触发回调
我们来监听一个键盘的按键事件 --- 监听键盘上回车按钮的按键事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <input type="text" @keyup.enter="keyup" /><br/><br/> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { message: "hello" }, methods:{ keyup() { console.log("keyup") } } }); </script> </body> </html>
@keyup.enter="keyup" 在keyup事件后面增加.enter即可
- .once: 只触发一次回调
增加了 .once的事件, 只有第一次点击有反应, 后面点击就没有反应了.
二. v-if指令
条件判断, 有三个指令
- v-if
- v-else-if
- v-else
来看案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 我们开发过程中通常会遇到状态, 1:表示启用, 2:表示禁用 3: 表示删除--> <div id="app"> <h2 v-if = "status == 1"> 启用 </h2> <h2 v-else-if = "status == 2"> 禁用 </h2> <h2 v-else> 删除 </h2> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { status: 1 } }); </script> </body> </html>
这个案例很简单,就不说了. 有一点可以说, 就是通常只有两种情况, 很简单的两种情况,我们会使用v-if, v-else. 如果情况很复杂, 不建议在代码写很多v-else-if, 因为可读性差, 我们应该吧条件判断放到methods或者computed中进行计算, 最后返回结果.
案例: 登录界面使用账号登录和邮箱登录的切换
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 我们开发过程中通常会遇到状态, 1:表示启用, 2:表示禁用 3: 表示删除--> <div id="app"> <label v-if="userLogin">账号登录 <input type="text" placeholder="请输入账号"> </label> <label v-else>邮箱登录 <input type="text" placeholder="请输入邮箱"> </label> <button @click="userLogin = !userLogin" >切换</button> </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: "#app", data: { userLogin: true } }); </script> </body> </html>
在这里, 我们定义了一个变量userLogin, 是否是用户登录, 默认是true; 定义两个label和一个button, 点击切换按钮, 来两个label之间来回切换.效果如下图
但是这里有个问题, 当我们输入内容以后, 切换文本框的时候, 内容却不会消失. 如下图
- 存在的问题: 切换了类型, 输入的文字却没有被清空.
我们发现, 在账号登录里面输入了1234, 切换到邮箱登录的时候, 却没有被清空. 这是两个文本框, 但是值怎么被带过来了呢?
- 原因
这是由于vue在进行dom渲染时, 考虑到性能问题, 会尽可能复用已经存在的元素. 而不是每次都创建新的元素. 这就是vue的虚拟dom.
如上图, 我们的dom元素, 之前都是直接渲染到浏览器页面的. 但是增加了vue以后, vue会帮我们将dom元素先进行缓存, 缓存为虚拟dom.
当我们使用v-if指令的时候, 两个div的元素不可能同时执行. 第一个div元素被渲染了以后, 在渲染第二个div的时候, 他发现有类似的元素, 那么vue就缓存一份. 当执行到else的时候, vue判断元素一样, 只是部分内容不同, 那就渲染不同的部分,相同的不会修改. 而我们输入的内容, 不在比较的范围内, 所以, 会被带过去.
- 如何避免这种情况呢? 使用属性key
<div id="app"> <label v-if="userLogin">账号登录 <input type="text" placeholder="请输入账号" key="user"> </label> <label v-else>邮箱登录 <input type="text" placeholder="请输入邮箱" key="email"> </label> <button @click="userLogin = !userLogin" >切换</button> </div>
如果两个key是一样的, 那么就虚拟dom就缓存一份, 如果两个key是不同的, 那么虚拟dom就会缓存两份. 来看看这次的效果