初识Vue
Vue (/vjuː/) 是一套用于构建用户界面的渐进式框架
渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验。
Vue中的MVVM

- View层:
视图层,在前端开发中,通常是DOM层,主要的作用是给用户展示各种信息。 - Model层:
数据层,数据可能是固定的死数据,更多是请求来自服务器的数据。 - VueModel层:
视图模型层,它是View和Model沟通的桥梁。
一方面它实现了Data Binding,也就是数据绑定,将Model的改变实时反映到View中; 另一方面它实现了DOM Listener,也就是DOM监听,当DOM发生一些事件(点击、滚动、touch等)时,可以监听到,并在需要的情况下改变对应的Data。
Vue的生命周期

基础语法
插值操作(Mustache)
通过Mustache语法(也就是{{}})可以将data中的文本数据插入到HTML中
// html
<div id="app">
<h2>Hello, {{massage}}</h2>
</div>
// js
let app = new Vue({
el: "#app",
data: {
massage: "Vuejs"
}
})
v-once
使用场景:
- 希望元素和组件只渲染一次,不会随着数据的改变而改变。
使用说明:
- 该指令后面不需要跟任何表达式
v-html
使用场景:
- 希望数据按照HTML格式进行解析,并且显示对应的内容。
使用说明:
- 该指令后面往往会跟上一个string类型,会将string的html解析出来并进行渲染。
v-text
使用场景:
- v-text作用和Mustache比较相似:都是用于将数据显示在界面中。
使用说明:
- 该指令后面往往会跟上一个string类型,会将string的html解析出来并进行渲染。
v-pre
使用场景:
- v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法
v-cloak
使用场景:
- 当后台请求的数据为返回时,浏览器会直接显然出未编译的Mustache标签,对用户不友好,页面渲染后v-cloak会被移除。
绑定属性(v-bind)
- 作用:动态绑定属性
- 语法糖::
- 预期:any(with argument) | Object(without argument)
- 参数:attrOrProp(optional)
v-bind用于绑定一个或多个属性值,或者向另外一个组件传递props值。
// html
<div id="app">
<a v-bind:href="link">Vue官网</a>
<img v-bind:src="logoURL" alt="">
// 语法糖:简写
<a :href="link">Vue官网</a>
<img :src="logoURL" alt="">
</div>
// js
let app = new Vue({
el: "#app",
data: {
link: "https://vuejs.org/",
logoURL: 'https://vuejs.org/images/logo.png'
}
})
v-bind绑定class
绑定class有两种方式:
- 对象语法::class后面跟的是一个对象
- 数组语法::class后面跟的是一个数组
// 对象语法
// 一、直接通过{}绑定一个类
<h2 :class="{active: isActive}">Hello Vue</h2>
// 二、绑定多个类
<h2 :class="{active: isActive, line: isLine}">Hello Vue</h2>
// 三、与普通类并不冲突,可共存
<h2 class="tittle" :class="{active: isActive}">Hello Vue</h2>
// 四、可以放在一个methods或computed中
<h2 class="tittle" :class="classes">Hello Vue</h2>
// 数组语法
// 一、直接通过[]绑定一个类
<h2 :class="['active']">Hello Vue</h2>
// 二、绑定多个类
<h2 :class="['active', 'line']">Hello Vue</h2>
// 三、与普通类并不冲突,可共存
<h2 class="tittle" :class="['active']">Hello Vue</h2>
// 四、可以放在一个methods或computed中
<h2 class="tittle" :class="classes">Hello Vue</h2>
let app = new Vue({
el: "#app",
data: {
isActive: true,
isLine: false
},
methods: {
classes() {
// 对象语法
return {active: this.isActive, line: this.isLine}
// 数组语法
return ['active', 'line']
}
},
computed: {
classes() {
// 对象语法
return {active: this.isActive, line: this.isLine}
// 数组语法
return [active, 'line']
}
}
})
v-bind绑定style
在写CSS属性名时,例如font-size:
- 使用驼峰式:fontSize
- 短横线分割,用单引号括起来:'font-size'
绑定style有两种方式:
- 对象语法::style后面跟的是一个对象
- 数组语法::style后面跟的是一个数组
// 对象语法
<h2 :style="{color: currentColor, fontSize: fontSize + 'px'}">Hello Vue</h2>
// 数组语法
<h2 :style="[colorStyle, fontSizeStyle]">Hello Vue</h2>
let app = new Vue({
el: '#app',
data: {
currentColor: 'red',
fontSize: 50,
colorStyle: {color: 'red'},
fontSizeStyle: {fontSize: '50px'}
}
})
计算属性
使用场景:
在某些情况,我们需要对数据镜像一些转化后再显示
使用说明:
计算属性是写在实例的computed中的
<div id="app">
<h2>书籍总价值:{{totalPrice}}</h2>
</div>
let vm = new Vue({
el: '#app',
data: {
books: {
{name: 'HTML', price: 99, count: 3},
{name: 'CSS', price: 45, count: 4}
{name: 'JS', price: 76, count: 5},
}
},
computed: {
totalPrice() {
return this.books.reduce((total, book) => {
return total + book.price * book.count;
}, 0);
}
}
})
计算属性的setter和getter
每个计算属性都包含一个getter和setter,当只使用getter时,可缩写为上面的例子所示。
<div id="app">
<h2>{{fullName}}</h2>
</div>
let vm = new Vue({
el: '#app',
data: {
firstName: 'apple',
lastName: 'banana'
},
computed: {
fullname {
setter(newValue) {
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
},
getter() {
return this.firstName + ' ' + this.lastName.
}
}
}
})
methods和computed的区别:
计算属性会进行缓存,如果多次使用时,计算属性只会调用一次。当计算属性中涉及到的变量值发生改变时,计算属性会重新进行计算。
事件监听 v-on
- 作用:绑定事件监听器
- 语法糖:@
- 预期:Function | inline Statement | Object
- 参数:event
v-on用于监听用户交互的事件,例如:点击、拖拽、键盘等。
// 基础用法
<div id="app">
<h2>点击次数:{{counter}}</h2>
<button v-on:click="this.counter++">按钮1</button>
<button v-on:click="btnClick">按钮2</button>
// 语法糖
<button @click="this.counter++">按钮1</button>
<button @click="btnClick">按钮2</button>
</div>
let vm = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
btnClick() {
this.counter++;
}
}
});
v-on参数问题
当通过methods中定义方法以供@click调用时,需要注意参数问题:
- 如果该方法不需要额外参数,那么click事件调用方法的()可以省略
- 如果定义的方法有一个参数,那么会默认将原生事件event参数传递进去。
- 如果需要同时传入某个参数,同时需要event时,通过$event传入事件。
<div id="app">
<h2>点击次数:{{counter}}</h2>
<button @click="btnClick1">按钮1</button>
<button @click="btnClick2">按钮2</button>
<button @click="btnClick3(2)">按钮3</button>
<button @click="btnClick4(2, $event)">按钮4</button>
<button @click="btnClick5">按钮5</button>
</div>
let vm = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
btnClick1() {
this.counter++;
}
btnClick2(event) {
console.log(event); // 默认传入原生事件event
this.counter++;
}
btnClick3(count) {
console.log(count); // 2
this.counter = count;
}
btnClick4(count, event) {
console.log(count); // 默认传入原生事件event
this.counter = count;
}
}
});
v-on修饰符
Vue提供了修饰符来帮助我们方便的处理一些事件:
- .stop → 调用event.stopPropagation()
- .prevent → 调用event.preventDefault()
- .{keyCode|keyAlias} → 只当事件从特定键触发时才触发回调
- .native → 监听组件根元素的原生事件
- .once → 只触发一次回调
<!-- 阻止冒泡 -->
<button @click.stop="btnClick"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="btnClick"></button>
<!-- 阻止表单自动提交的默认行为 没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="btnClick"></button>
<!--只有按回车键才回调-->
<!--键修饰符,键别名-->
<input @keyup.enter="onEnter">
<!--键修饰符,键代码-->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button @click.once="btnClick"></button>
条件判断
Vue的条件指令可以根据表达式的值在DOM中渲染或销毁元素和组件。相关指令: v-if v-else-if v-else
<div id="app">
<div v-if="score>=90">优秀</div>
<div v--else-if="score>=80">良好</div>
<div v-else-if="score>=60">及格</div>
<div v-else>不及格</div>
</div>
<!--但是不建议将复杂逻辑判断放在html中,推荐使用方法调用-->
小案例
通过点击按钮切换使用账号登陆还是邮箱地址登陆
<div id="app">
<div v-if="type === "userName">
<label for="userName">用户名登陆:</label>
<input placeholder="请输入用户名">
</div>
<div v--else>
<label for="email">邮箱登陆:</label>
<input placeholder="请输入邮箱">
</div>
<botton @click="switchType">切换</button>
</div>
let vm = new Vue({
el: '#app',
data: {
type: 'userName'
},
methods: {
switchType() {
this.type = this.type='userName' ? 'email' : 'userName';
}
}
});
问题: 你会发现当我们在有输入内容的情况下,切换了类型,文字依然显示之前输入的内容,但是按道理讲,我们应该切换到另一个input元素中并且没有输入内容。
解答: Vue在进行DOM渲染时,出于性能考虑,会尽可能服用已经存在的元素,而不是重新创建新的元素。在上面的案例中,Vue内部会发现原来的input元素不再使用(v-if为false会销毁),就将它直接作为input来使用了。
解决方案: 给对应的input添加key并保证key的唯一性。
<div id="app">
<div v-if="type === "userName">
<label for="userName">用户名登陆:</label>
<input placeholder="请输入用户名" key="userName">
</div>
<div v--else>
<label for="email">邮箱登陆:</label>
<input placeholder="请输入邮箱" key="email">
</div>
<botton @click="switchType">切换</button>
</div>
v-show
v-show和v-if的用法相似,都决定一个元素是否渲染。
区别:
v-if当条件为false时,不会有对应的元素在DOM中;
v-show当条件为false时,仅将元素的display属性设置为none。
使用场景:
当需要在显示与隐藏之间切换很频繁时,用v-show;
当只有一次切换时,使用v-if。
v-for
当我们有一组数据需要渲染时,就可以使用v-for来完成。
<div id="app">
<ul>
<!--不需要索引index-->
<li v-for="item in movies">{{item}}</li>
<!--需要使用索引index-->
<li v-for="(item, index) in movies">{{index+1}}.{{item}}</li>
</ul>
</div>
let vm = new Vue({
el: '#app',
data: {
movies: ['火影忍者','海贼王','进击的巨人']
}
});
v-for同样可以遍历对象
<div id="app">
<ul>
<li v-for="(value, key, index) in info">
{{value}} - {{key}} - {{index}} // apple - name - 0
</li>
</ul>
</div>
let vm = new Vue({
el: '#app',
data: {
info: {
name: 'apple',
age: 18,
height: 1.88
}
}
});
组件的key属性
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性。
原因:和Vue的虚拟DOM的Diff算法有关系。
当某一层有很多相同的节点,我们希望插入一个新的节点,例如我们希望在B和C之间插入一个F,Diff算法默认执行起来是将C更新成F,后面依次更新,这样的效率太低。


所以我们需要使用key来给每个节点做一个唯一标识。Diff算法就可以正确识别此节点,找到正确的位置插入新的节点。

检查数组更新
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发试图的更新。
methods: {
updateData() {
<!--1. push方法 可同时传入多个参数,用','隔开-->
<!--在数组末尾插入元素-->
this.names.push('apple');
<!--2. pop方法-->
<!--弹出数组最后一个元素-->
this.names.pop();
<!--3. unshift方法 可同时传入多个参数,用','隔开-->
<!--在数组顶部插入元素-->
this.names.unshift('apple')
<!--4. shift-->
<!--弹出数组第一个元素-->
this.names.shift()
<!--5. splice-->
<!--可以对数组进行删除,更新,替换操作-->
this.names.splice(1,)
<!--6. sort-->
<!--对数组进行排序, 可以传入比较函数-->
this.names.sort()
<!--7. reverse-->
<!--数组反转-->
this.names.reverse()
<!--不会修改页面-->
this.names[0] = 'apple';
<!--替换↓-->
this.names.splice(0, 1, 'apple');
Vue.set(this.names, 0, 'apple');
}
}
表单绑定v-model
Vue中使用v-model指令来实现表单元素和数据的双向绑定
v-model其实时一个语法糖,它的背后本质上是包含两个操作:
- v-bind绑定一个value属性
- v-on指令给当前元素绑定input事件
<div id="app">
<input type="text" v-model="message">
<h2>{{message}}</h2>
<!--等同于↓-->
<input type="text" :value="message" @input="message = $event.target.value">
<!--绑定textarea元素-->
<textarea type="text" v-model="textMessage"></textarea>
<h2>{{textMessage}}</h2>
</div>
const vm = new Vue({
el: "#app",
data: {
message: 'hello',
textMessage: ''
}
});
v-model绑定radio
<div id="app">
<label for="male">
<input type="radio" :value="type" id="male" v-model="gender">男
</label>
<label for="female">
<input type="radio" value="female" id="female" v-model="gender">女
</label>
</div>
const vm = new Vue({
el: "#app",
data: {
type: 'male',
gender: 'male'
}
});
v-model绑定checkbox

<div id="app">
<!--单个复选框-->
<label for="check">
<input type="checkbox" id="check" v-model="checked">同意协议
</label>
<!--多个复选框-->
<label for="篮球">
<input type="checkbox" id="篮球" v-model="hobbies">篮球
</label>
<label for="足球">
<input type="checkbox" id="足球" v-model="hobbies">足球
</label>
<label for="台球">
<input type="checkbox" id="台球" v-model="hobbies">台球
</label>
</div>
const vm = new Vue({
el: "#app",
data: {
checked: false,
hobbies: []
}
});
v-model绑定select

<div id="app">
<!--单个下拉框-->
<select v-model="fruit">
<option value="apple">苹果</option>
<option value="banana">香蕉</option>
<option value="orange">橘子</option>
</select>
<!--多个下拉框-->
<select v-model="fruits" multiple>
<option value="apple">苹果</option>
<option value="banana">香蕉</option>
<option value="orange">橘子</option>
</select>
</div>
const vm = new Vue({
el: "#app",
data: {
fruit: '',
fruits: []
}
});
修饰符

<div id="app">
<!--1. lazy修饰符-->
<input type="text" v-model.lazy="message">
<p>{{message}}</p>
<!--2. number修饰符-->
<input type="number" v-model.number="age">
<p>{{age}} - {{typeof age}}</p>
<!--3. trim修饰符-->
<input type="text" v-model.trim="message">
<p>{{message}}</p>
</div>
案例
// HTML
<div id="app" v-cloak>
<div v-if="list.length">
<table>
<thead>
<tr>
<th></th>
<th>名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in list" :key="item.id">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>{{item.price | showPrice}}</td>
<td>
<button @click="decrement(index)" :disable="item.count === 1">-</button>
{{item.count}}
<button @click="increment(index)">+</button>
</td>
<td>
<button @click="handleRemove(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<div>总价:{{totalPrice | showPrice}}</div>
</div>
<div v-else>
购物车为空
</div>
</div>
// CSS
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th,td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
// JS
const app = new Vue({
el: '#app',
data: {
list:[{
name: 'HTML入门到放弃',
id: '01',
date: '2020-06-18',
price: 11,
count: 1
},{
name: 'CSS入门到放弃',
id: '02',
date: '2020-06-18',
price: 12,
count: 1
},{
name: 'JS入门到放弃',
id: '03',
date: '2020-06-18',
price: 13,
count: 1
},{
name: 'Vue入门到放弃',
id: '041',
date: '2020-06-18',
price: 14,
count: 1
}]
},
methods: {
decrement(index) {
this.list[i].count--;
},
increment(index) {
this.list[i].count++;
},
handleRemove(index) {
this.list.splice(index, 1);
}
},
<!--过滤器-->
filters: {
showPrice() {
return '$' + value.toFix(2);
}
},
computed: {
totolPrice() {
return this.list.reduce((total, next) = > {
return total + next.price * next.count;
}, 0);
}
}
});