数据绑定最常见的形式就是使用Mustache
语法 (双大括号) 的文本插值
<span>Message: {
{ msg }}span>
<span>Message: {
{ Firstname+lastName }}span>
插值表达式,可以是一个变量,也可以是一个表达式,可以进行简单的计算
但一般不推荐这么干,Vue中有更优雅的实现方式
<div id=app>
<h1 v-once>{
{message}}h1>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
}
})
script>
被v-once
修饰后,{ {message}}
只会在初始化获取一次值,后续message
的变化,不会影响dom
中的数据展示
有了插值表达式,可以让dom标签的内容实现动态化,但是我们想让dom标签的属性动态化该怎么办?
我们使用v-bind
,将dom标签的属性进行绑定,实现动态
<div id=app>
<img src="imgUrl" alt="测试">
<img v-bind:src="imgUrl" alt="测试">
div>
<script>
let app = new Vue({
el: '#app',
data: {
imgUrl: 'http://tuchuang.zhangln.com/xbVhJM.png'
}
})
script>
实际被浏览器解析的html如下
因为使用了v-bind
指令,src的值可以动data中获取了
可以缩写成
即v-bind
是可以省略的,保留:
即可
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<title>Documenttitle>
<style>
.active1 {
color: red;
}
.active2 {
color: gold;
}
style>
head>
<body>
<div id=app>
<h2 :class="active">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
active: 'active1'
}
})
script>
body>
html>
通过修改active的值,动态的将不同的样式绑定到了h2标签
<div id=app>
<h2 :class="{active1:isActive1,active2:isActive2}">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
isActive1: true,
isActive2: false
}
})
script>
active1
和active2
是两个样式,通过isActive1和isActive2的布尔值,确定是否需要将对应的样式添加上来。
如果一次性将一个json对象添加在class上,代码有点复杂,我们可以使用计算属性(computed,后续讲到)或methods中
<div id=app>
<h2 :class="getClass()">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
isActive1: true,
isActive2: false
},
methods: {
getClass: function () {
return {
active1: this.isActive1, active2: this.isActive2};
}
}
})
script>
当我们的需求中,需要对一个元素不停的切换样式的时候,就可以使用这种办法。
作用和对象语法一样,用的比较少,我们来简单做个演示
<div id=app>
<h2 :class="classes">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
classes:['active1','active2']
}
})
script>
相当于把两个样式都绑定给了标签
与对象语法一样,数组语法也可以使用计算属性或methods获取
需求
:在用v-for
生成的列表中,点击哪个元素,哪个元素就变色
<body>
<div id=app>
<ul>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="getClass(index)">{
{index}} - {
{item}}li>
ul>
div>
<script>
let app = new Vue({
el: '#app',
data: {
movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
isActive: [false, false, false, false]
},
methods: {
getClass: function (index) {
console.log('计算' + index + '的class');
return {
active1: this.isActive[index]};
},
changeClass: function (index) {
console.log('点击了' + index);
console.log('当前配置:' + this.isActive);
//数组的深拷贝
let newIsActive = JSON.parse(JSON.stringify(this.isActive));
for (let i = 0; i < newIsActive.length; i++) {
newIsActive[i] = i == index ? true : false;
}
console.log('新的配置:' + newIsActive);
this.isActive = newIsActive;
}
}
})
script>
body>
<div id=app>
<ul>
<li v-for="(item,index) in movies" @click="changeClass(index)" :class="getClass(index)">{
{index}} - {
{item}}li>
ul>
div>
<script>
let app = new Vue({
el: '#app',
data: {
movies: ['海王', '海尔兄弟', '火影忍者', '进击的巨人'],
currentIndex: -1
},
methods: {
getClass: function (index) {
return index == this.currentIndex ? {
active1: true} : {
active1: false};
},
changeClass: function (index) {
this.currentIndex = index;
}
}
})
script>
<h2 :style="{
fontSize:'50px'}">{
{message}}h2>
<div id=app>
<h2 :style="{
fontSize:finalSize+'px'}">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
finalSize: 50
}
})
script>
觉得style属性太长了的话,可以抽取成方法
<div id=app>
<h2 :style="getStyles()">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
finalSize: 50
},
methods: {
getStyles: function () {
return {
fontSize: this.finalSize + 'px'};
}
}
})
script>
<div id=app>
<h2 :style="[style1,style2]">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
style1: {
fontSize: this.finalSize + 'px'},
style2: {
backgroundColor: 'red'},
}
})
script>
这个数组语法,就和绑定class的数组语法非常像了,只不过class的时候,每个数组元素是一个class样式,这里的每个数组元素是一个对象类型的内联样式
<body>
<div id=app>{
{fullName}}div>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: function () {
return this.firstName + this.lastName;
}
}
})
script>
当插值表达式中的结果计算逻辑比较复杂的时候,就可以使用计算属性
每个计算属性其实都包含了一个setter和getter方法
上例中,我们就使用了getter方法来进行读取,有时候也可以提供setter方法进行设置
<div id=app>{
{fullName}}div>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: {
get() {
console.log("调用了get");
return this.firstName + " " + this.lastName;
},
set(newValue) {
console.log("调用了set");
const names = newValue.split(" ");
this.firstName = names[0];
this.lastName = names[1];
}
}
}
})
script>
setter方法会在设置fullName值的时候被调用
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
<div id=app>{
{fullName}}div>
<script>
let app = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三',
fullName: ''
},
watch: {
//如果firstName发生变化,则函数执行
firstName() {
this.fullName = this.firstName + this.lastName;
}
}
})
script>
v-if
、v-else-if
、v-else
<div id=app>
<div v-if="isA">Adiv>
<div v-else-if="isB">Bdiv>
<div v-else>Cdiv>
div>
<script>
let app = new Vue({
el: '#app',
data: {
isA: true,
isB: false
}
})
script>
<div id=app>
<div v-if="showUserName">
<input type="text" placeholder="用户名..." key="u1">
<button>登陆button>
div>
<div v-else-if="showEmail">
<input type="text" placeholder="邮箱..." key="u2">
<button>登陆button>
div>
<button @click="changeLoginWay">切换button>
div>
<script>
let app = new Vue({
el: '#app',
data: {
showUserName: true,
showEmail: false
},
methods: {
changeLoginWay() {
this.showUserName = !this.showUserName;
this.showEmail = !this.showEmail;
}
}
})
script>
这里为两个input设置了key,并且设置key的值不同。
是为了不想让Vue复用input。
这里的原理性解释涉及到Vue的虚拟DOM
,后续再说
<div id=app>
<h2 v-show="isShow">{
{message}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue',
isShow: false
}
})
script>
与v-if类似,v-show也可以用于决定元素的显示与否,但是实现形式是不同的。
v-if是对dom的操作,是在增删dom节点,v-show只是操作
Hello Vue
为元素设置一下内联样式而已
当页面元素显示与隐藏频繁切换的时候,建议使用v-show,如果是一次性操作,则选择使用v-if
<div id=app>
<ul>
<li v-for="item in names">{
{item}}li>
ul>
div>
<script>
let app = new Vue({
el: '#app',
data: {
names: ['中国', '美国', '英国']
}
})
script>
<li v-for="(item,index) in names">{
{index}} - {
{item}}li>
<div id=app>
<ul>
<li v-for="(item,key) in info">{
{key}} - {
{item}}li>
ul>
div>
<script>
let app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
})
script>
key就是对象的属性名,item就是对象属性的值
官方推荐,使用v-for的时候,为对应的元素或组件添加上一个:key属性,
为节点做一个唯一标识。
在Vue虚拟DOM的Diff算法中会用到。
可以帮助更高效的更新虚拟DOM
注意,不要用index作为:key
的值
v-on:
v-on:
可以简写为@
<div id="example-2">
<button v-on:click="greet">Greetbutton>
div>
<script>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 对象中定义方法
methods: {
//可以简写为greet(){......}
greet: function (event) {
// `this` 在方法里指向当前 Vue 实例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
script>
上述案例中,我们没有传递event参数,但是function中是可以使用的
可是,如果我们需要传递普通参数,同时也要获取event,那该怎么办?
答案:greet(args,$event)
.stop
修饰符<div id=app>
<div @click="divClick">
哇哈哈
<button @click="btnClick">点我呀button>
div>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
},
methods: {
divClick() {
console.log("div被点击了");
},
btnClick() {
console.log("button被点击了");
}
}
})
script>
在这段代码中,如果点击了button,div上的click也是会被触发的,怎么办呢?
答案:添加.stop
修饰符:
除了.stop
修饰符外,还有很多修饰符可以支持
.prevent
修饰符比如对于submit类型的input,默认是会提交表单的
<div id=app>
<form>
<input type="submit" value="提交" @click.prevent="submitClick">
form>
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
},
methods: {
divClick() {
console.log("div被点击了");
},
btnClick() {
console.log("button被点击了");
},
submitClick() {
console.log("点击提交");
}
}
})
script>
用了修饰符后,就不会自动提交表单了
keyup/keydown
事件与.enter
修饰符监听按键按下与弹起,
<div id=app>
<input type="text" @keyup="keyClickUp">
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue'
},
methods: {
divClick() {
console.log("div被点击了");
},
btnClick() {
console.log("button被点击了");
},
submitClick() {
console.log("点击提交");
},
keyClickUp() {
console.log("点击了按键");
}
}
})
script>
如果只想监听回车键,则
还可以设置要监听的键的代码,如
.once
修饰符<button @click.once="btnClick">按钮button>
事件仅被触发一次
你可以用
v-model
指令在表单、
及
元素上创建双向数据绑定。
<div id=app>
输入:<input type="text" v-model="message">
<br/>
输出:<input type="text" :value="message">
div>
<script>
let app = new Vue({
el: '#app',
data: {
message: ''
}
})
script>
在输入中输入的数据,绑定到message中,message的变化,又反馈到输出中。
最终效果就是输入内容同步显示在输出中。
双向绑定,我们可以这么理解:以前都是template从Vue对象中取数据,现在可以在template中将数据绑定到Vue对象中。同时,如果message对象由于其他因素被改变,输入中也是会随之改变的。
<div id=app>
<input type="radio" id="male" name="sex" value="男" v-model="sex">男
<input type="radio" id="female" name="sex" value="女" v-model="sex">女
<hr>
选中:{
{sex}}
div>
<script>
let app = new Vue({
el: '#app',
data: {
sex: ''
}
})
script>
<div id=app>
<label>
<input type="checkbox" id="agree" v-model="isAgree">同意协议
label>
<hr>
选中结果:{
{isAgree}}
div>
<script>
let app = new Vue({
el: '#app',
data: {
isAgree: false
}
})
script>
<div id=app>
<label>
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="排球" v-model="hobbies">排球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
label>
<hr>
选中结果:{
{hobbies}}
div>
<script>
let app = new Vue({
el: '#app',
data: {
hobbies:[]
}
})
script>
<div id=app>
<label>
<select name="abc" id="" v-model="fruit">
<option value="苹果">苹果option>
<option value="香蕉">香蕉option>
<option value="榴莲">榴莲option>
<option value="葡萄">葡萄option>
select>
label>
<hr>
选中结果:{
{fruit}}
div>
<script>
let app = new Vue({
el: '#app',
data: {
fruit: '香蕉'
}
})
script>
<div id=app>
<label>
<select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果option>
<option value="香蕉">香蕉option>
<option value="榴莲">榴莲option>
<option value="葡萄">葡萄option>
select>
label>
<hr>
选中结果:{
{fruit}}
div>
<script>
let app = new Vue({
el: '#app',
data: {
fruit: ['']
}
})
script>
输入:<input type="text" v-model.lazy="message">
这样一来,就只有按回车键或光离开输入框后,才会触发绑定。
<input type="number" v-model.number="age">
限定输入必须是数字
<input type="text" v-model.trim="message">
删除输入中左右两边的空格,这个就非常实用了
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<title>Documenttitle>
head>
<body>
<div id=app>
<table border="1">
<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 | showPrice}}td>
<td>
<button @click="decrement(item.id)">1button>
{
{item.count}}
<button @click="increment(item.id)">+button>
td>
<td>
<button @click="removeThisBook(item.id)">移除button>
td>
tr>
tbody>
table>
<h2>总价:{
{totalPrice | showPrice}}h2>
div>
<script>
let app = new Vue({
el: '#app',
data: {
books: [
{
id: 1, name: '《算法导论》', date: '2006-9', price: 85.9, count: 1},
{
id: 2, name: '《UNIX编程艺术》', date: '2006-2', price: 59, count: 1},
{
id: 3, name: '《编程珠玑》', date: '2006-1', price: 39, count: 1},
{
id: 4, name: '《代码大全》', date: '2006-10', price: 120, count: 1}
]
},
computed: {
//总价
totalPrice() {
//使用reduce汇总计算
return this.books.reduce((preValue, book) => {
return preValue += book.price * book.count;
}, 0);
}
},
methods: {
//删除指定id的书籍
removeThisBook(id) {
console.log("移除id:" + id);
for (let i = 0; i < this.books.length; i++) {
//获取对应id在数组中的索引
if (this.books[i].id === id) {
//删除选中的元素
this.books.splice(i, 1);
}
}
},
//加减书籍数量
decrement(id) {
//当前想要操作的书
for (let i = 0; i < this.books.length; i++) {
if (this.books[i].id === id) {
if (this.books[i].count == 0) {
alert("不能再减少了");
} else {
this.books[i].count--;
}
break;
}
}
},
increment(id) {
//当前想要操作的书
for (let i = 0; i < this.books.length; i++) {
if (this.books[i].id === id) {
this.books[i].count++;
break;
}
}
}
},
filters: {
showPrice(price) {
return "¥" + price.toFixed(2);
}
}
})
script>
body>
html>