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-bind指令可以简写为:, 同样v-on也可以简写, 简写为@, 如上写法可以简写如下:
<button @click="add"> + </button>
<button @click="sub"> - </button>
上面的案例都是不带参数. 如果方法没有参数, 那么我们在调用的时候可以省略括号
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都传递过来了
看如下代码
<!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>
这样就阻止了冒泡事件
我们现在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>
我们来监听一个键盘的按键事件 --- 监听键盘上回车按钮的按键事件
<!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的事件, 只有第一次点击有反应, 后面点击就没有反应了.
条件判断, 有三个指令
来看案例
<!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判断元素一样, 只是部分内容不同, 那就渲染不同的部分,相同的不会修改. 而我们输入的内容, 不在比较的范围内, 所以, 会被带过去.
<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就会缓存两份. 来看看这次的效果
v-show很简单, 就是隐藏/显示元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 我们开发过程中通常会遇到状态, 1:表示启用, 2:表示禁用 3: 表示删除-->
<div id="app">
<h2 v-if = "isShow"> 启用 </h2>
<h2 v-show = "isShow"> 启用 </h2>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
isShow: true
}
});
</script>
</body>
</html>
这两个都是可以显示出来, 我们设置isShow=false, 都隐藏了,但隐藏的结果是不同的. 我们来看看html代码
我们发现代码里只有useShow对象, 并且增加了display:none样式, 而没有useIf对象. 也就是被直接删除了
遍历有遍历数组, 遍历对象两种形式
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Title</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="item in languages">{{item}}</li>
<li v-for="(item, index) in languages"> {{index}}--{{item}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
languages: ["java", "php", "python"]
}
});
</script>
</body>
</html>
遍历数组如果不带下标, 可以这么写
<li v-for="item in languages">{{item}}</li>
如果带有下标, 可以这么写
<li v-for="(item, index) in languages"> {{index}}--{{item}}</li>
2. 遍历对象
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Title</title>
</head>
<body>
<div id="app">
<p> 只显示对象的value </p>
<ul>
<li v-for="item in students">{{item}}</li>
</ul>
<p> 显示对象的key 和 value </p>
<ul>
<li v-for="(value, key) in students">{{key}} -- {{value}}</li>
</ul>
<p> 显示对象的编号 </p>
<ul>
<li v-for="(value, key, index) in students">{{index}} -- {{key}} -- {{value}} </li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
students: {
name: "zhangsan",
age: 20,
sex: "male"
}
}
});
</script>
</body>
</html>
具体写法如上图. 需要注意的是, 当显示key和value的时候, value写在前面, key写在后面
官方推荐, 我们在使用v-for的时候, 应该给对应的元素添加一个:key属性
为什么要添加key属性呢?
详细参考这篇文章: https://www.jianshu.com/p/4bd5e745ce95
增加了:key以后,会让内部的计算性能提高, 那么应该怎么加呢?
<ul>
<li v-for="item in students" :key="item">{{item}}</li>
</ul>
使用的时候, 如上示例: :key="item",
其实, 通常我们在遍历数组, 修改数组的值的时候, 习惯于使用下标修改.
如:
this.languages[0] = "aaaa";
但是, 我们看看,下标修改在vue中会怎么样呢?
我们发现修改了languages,但是页面没变化, 实际上确实是修改了
所以, 我们得出结论: 直接通过下标修改元素, 是非响应式的.
那么数组的其他方法呢?看下面的案例
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item, index) in languages"> {{index}}--{{item}}</li>
</ul>
<br><br/><br/><br/><br/>
<button @click="pushMethod">push</button>
<button @click="popMethod">pop</button>
<button @click="shiftMethod">shift</button>
<button @click="unShiftMethod">unshift</button>
<button @click="updateElement"></button>
<button @click="spliceMethod">splice</button>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
languages: ["java", "php", "python", "go", "c语言"]
},
methods: {
pushMethod() {
this.languages.push("其他语言")
},
popMethod() {
this.languages.pop()
},
shiftMethod() {
this.languages.shift()
},
unShiftMethod() {
this.languages.unshift("其他语言")
},
updateElement(){
this.languages[0] = "aaaa";
},
spliceMethod() {
/**
* splice可以进行
* 1. 添加元素 splice(从第几个元素开始, 0, "未来语言1", "未来语言2")
* 2. 删除元素 splice(从第几个元素开始, 删除几个元素)
* 3. 修改元素: 其实是删除以后在添加 splice(从第几个元素开始, 修改几个元素, 修改的元素内容)
*/
// 删除元素
//this.languages.splice(1, 1)
// 修改元素
//this.languages.splice(1, 2, "pthoon语言", "go语言")
// 添加元素
this.languages.splice(1, 0, "未来语言1", "未来语言2")
}
}
});
</script>
</body>
</html>
通过测试, 这几个方法都是响应式的.
所以, 如果我们想要修改/删除元素, 建议使用splice, 这样可以立刻看到效果.
v-model指令用来实现表单元素和数组元素的双向绑定
案例: 文本框输入双向同步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<form>
<input type="text" placeholder="请输入" v-model="message">
</form>
message的内容是:{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
message: "hello"
}
});
</script>
</body>
</html>
我们定义了一个变量message, 然后在input输入框中使用v-model="message"就可以和这个变量进行双向绑定了
其实v-model包含了两个操作
案例: 模拟v-model的两步操作
首先, 我们知道让文本框显示data中message的值, 可以直接使用v-bind:value, 这样我们修改了message的值, 文本框自动响应
<input type="text" placeholder="请输入" :value="message">
这是将数据响应给文本框. 那么, 如何将文本框修改的内容,同步给数据呢? 使用文本框的输入事件: v-on:input
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" placeholder="请输入" :value="message" @input="change">
<p>message的内容是:{{message}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
message: "hello"
},
methods: {
change(event) {
this.message = event.target.value;
}
}
});
</script>
</body>
</html>
这里使用了@input给文本框绑定了input事件, change()方法没有加括号, 会自动将默认的参数event传过去, 如果想要显示的设置event参数, 可以试用@input="change($event)".
然后在change方法中, 设置this.message=event.target.value;
<input type="text" v-model="message">
等同于
<input type="text" v-bind:value="message" v-on:input="message=$event.target.value">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<form>
<input type="radio" name="sex" id="male" value="男" v-model="sex">男
<input type="radio" name="sex" id="female" value="女" v-model="sex">女
</form>
当前选中的是: {{sex}}
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
sex: "男"
}
});
</script>
</body>
</html>
使用了v-model以后, 就可以不使用name属性了, 默认将具有同一个v-model值的radio作为一组
案例: 是否同意协议
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<label for="agreeContent">
<input type="checkbox" name="agreeContent" id="agreeContent" value="同意协议" v-model="agreeContent">统一协议
<br/>当前选中的是: {{agreeContent}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
agreeContent: false
}
});
</script>
</body>
</html>
是否同意协议: data数据中agreeContent: 值是true或者false. true表示选中文本框, false表示取消选中
input被包在了label中, 这样的好处是, 点击文字也可以选中和取消. 如果不放在lable中,就必须选择复选框.
复选框的值是一个数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>单选框</h2>
<label for="agreeContent">
<input type="checkbox" name="agreeContent" id="agreeContent" value="同意协议" v-model="agreeContent">同意协议
<br/>当前选中的是: {{agreeContent}}
</label>
<br/><br/><br/><br/><br/>
<h2>复选框</h2>
<form>
<input type="checkbox" name = "hobby" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" name = "hobby" value="篮球" v-model="hobbies">篮球
<input type="checkbox" name = "hobby" value="足球" v-model="hobbies">足球
<input type="checkbox" name = "hobby" value="排球" v-model="hobbies">排球
<br/>当前选中的爱好是: {{hobbies}}
</form>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
agreeContent: false,
hobbies:[]
}
});
</script>
</body>
</html>
在data中定义了一个hobbies数组. 并且将这个数组绑定给checkbox付款框的每一项. 即可实现效果
1) select单选
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<select v-model="selectOne">
<option id="apple" value="苹果" >苹果</option>
<option id="banana" value="香蕉" >香蕉</option>
<option id="strawberry" value="草莓" >草莓</option>
<option id="grape" value="葡萄" >葡萄</option>
</select>
<br/>
<p>当前选中的是: {{selectOne}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
sex: "男",
selectOne:"苹果"
}
});
</script>
</body>
</html>
注意: 在select单选下拉框中, v-model要写在select元素上
2) select多选
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<br/>
<br/>
<br/>
<select v-model="favoriteFrite" multiple>
<option id="apple" value="苹果" >苹果</option>
<option id="banana" value="香蕉" >香蕉</option>
<option id="strawberry" value="草莓" >草莓</option>
<option id="grape" value="葡萄" >葡萄</option>
</select>
<br/>
<p>当前选中的是: {{favoriteFrite}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
favoriteFrite:[]
}
});
</script>
</body>
</html>
在使用多选的时候, 在select中增加一个multiple即可设置为多复选.
在数据中, 我们需要将其设置为一个数组. 如favoriteFrite:[]
下面来看看效果
1. Lazy修饰符
默认情况下, v-model是实时双向同步的. 但有时候, 我们不希望他每次变化都实时响应, 那么, 可以使用lazy修饰符
v-model.lazy , 使用了lazy以后按回车或者文本框是去焦点即同步内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model.lazy="message"/>
<p>message的内容是:{{message}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
count: 0,
name:"zhangsan"
}
});
</script>
</body>
</html>
看效果
通常我们在写一个只能输入数字的文本框时, 会这么写<input type="number" v-model = "count">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="number" v-model = "count">
<p>count的类型: {{count}} --- {{typeof count}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
message: "hello",
count: 0,
name:"zhangsan"
}
});
</script>
</body>
</html>
但这么写获取到的count值是什么类型呢? 我们使用{{typeof count}}来看看count的类型. 如下图
我们看到count的类型是String. 而不是number类型 如何才能让count是number类型呢? 使用v-model.number="count", 如下图:
<input type="number" v-model.number = "count">
通常我们在文本框输入文本的时候, 可能会误输入空格, 我们可以使用trim修饰符, 去掉文本框左右的空格
<input type="text" v-model.trim="name">