Vue (读音 /vjuː/,类似于 view)
Vue是一个渐进式的框架,什么是渐进式的呢?
Vue有很多特点和Web开发中常见的高级功能
<div id="app">
<!-- mustach语法,不仅仅可以直接写变量,也可以写简单的表达式 -->
<h2>{
{
message}}</h2>
<h2>{
{
firstName + lastName}}</h2>
<h2>{
{
firstName + ' ' + lastName}}</h2>
<h2>{
{
firstName}} {
{
lastName}}</h2>
<h2>{
{
counter * 2}}</h2>
</div>
该指令后面不需要跟任何表达式(比如之前的v-for后面是由跟表达式的)
该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。
<div id="app">
<h2>{
{
message}}</h2>
<h2 v-once>{
{
message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello',
}
})
</script>
某些情况下,我们从服务器请求到的数据本身就是一个HTML代码,如果我们直接通过{ {}}来输出,会将HTML代码也一起输出。 但是我们可能希望的是按照HTML格式进行解析,并且显示对应的内容。
如果我们希望解析出HTML展示
<div id="app">
<h2>{
{
url}}</h2>
<h2 v-html="url"></h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
url: 'Baidu'
}
})
</script>
v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法。
比如下面的代码:
<div id="app">
<h2>{
{
message}}</h2>
<h2 v-pre>{
{
message}}</h2>
</div>
在某些情况下,我们浏览器可能会直接显然出未编译的Mustache标签。
cloak: 斗篷
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h2 v-cloak>Hello {
{
message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
# 在vue解析之前,div中有一个属性v-cloak
# 在vue解析之后,div中没有一个属性v-cloak
const app = new Vue({
el: "#app",
data: {
message: 'Andy Low'
}
})
</script>
代码是由上而下执行的,当执行到div标签时,页面会直接显示{ {message}},执行到js代码后才会将{ {message}}替换为Andy Low,这样页面在显示时会出现一个闪动效果。
前面我们学习的指令主要作用是将值插入到我们模板的内容当中。
但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。
这个时候,我们可以使用v-bind指令:
v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值
在开发中,有哪些属性需要动态进行绑定呢?
还是有很多的,比如图片的链接src、网站的链接href、动态绑定一些类、样式等等
<div id="app">
<img v-bind:src="imgUrl" alt=""> # imgUrl如同上面的message一样,是data中定义的变量
<a :href="aHref">Baidu</a> # v-bind可以省略(语法糖)
</div>
v-bind可以绑定普通字符串,对象,数组
<div id="app">
<!-- {
}表示是一个对象,里面写键值对 -->
<h2 :class="{active:isActive, line:isLine}">{
{
message}}</h2>
<button @click="btnClick">Button</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Andy Low',
isActive: true,
isLine: true
},
methods: {
btnClick: function() {
this.isActive = !this.isActive
}
}
})
</script>
如果过于复杂,可以入在一个methods或者computed中:
<div id="app">
<!-- {
}表示是一个对象,里面写键值对 -->
<h2 :class="getClasses()">{
{
message}}</h2>
<button @click="btnClick">Button</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Andy Low',
isActive: true,
isLine: true
},
methods: {
btnClick: function() {
this.isActive = !this.isActive
},
getClasses: function() {
return {
active:this.isActive, line:this.isLine}
}
}
})
</script>
v-bind的数组语法:
<div id="app">
<!-- [active, line]数组中的元素不加引号是变量,加引号是普通字符串 -->
<h2 :class="[active, line]" class="title">{
{
message}}</h2>
</div>
在写CSS属性名的时候,比如font-size
绑定方式一:对象语法
:style="{color: currentColor, fontSize: fontSize + 'px'}"
绑定方式二:数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>
如果过于复杂,可以放到methods或computed中:
<template>
..................................
<div :style="activeStyle"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default {
name: "TabBarItem",
props: {
path: String,
activeColor: {
type: String,
default: 'red',
}
},
data() {
return {
}
},
computed: {
isActive() {
return this.$route.path.indexOf(this.path) != -1
},
activeStyle() {
return this.isActive ? {
color: this.activeColor} : {
}
}
},
....................
}
</script>
我们知道,在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
<div id="app">
<h2>{
{
fullName}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: 'Andy',
lastName: 'Low'
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName
}
}
})
</script>
计算属性较为复杂的操作:
<div id="app">
<h2>总价格:{
{
totalPrice}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
books: [
{
id: 110, name: 'Unix', price: 119},
{
id: 113, name: 'Linux', price: 90},
{
id: 115, name: 'Python', price: 50},
{
id: 118, name: 'Java', price: 158}
]
},
computed: {
totalPrice: function() {
let result = 0
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price
}
return result
}
}
})
</script>
计算属性中的方法完整的写法如下:
computed: {
fullName: {
set: function() {
// 设置fullName值时会被调用
console.log('------');
},
get: function() {
return 'abc'
}
}
}
由于一般我们在计算属性中的方法不需要做设置值的操作,也就是方法中只有get方法,于是我们简写成了上面5.1节中的写法
我们可能会考虑这样的一个问题:
在前端开发中,我们需要经常和用户进行交互。
当通过methods中定义方法,以供@click调用时,需要注意参数问题:
<div id="app">
<button @click="btnClick()">Button1</button>
<button @click="btnClick">Button2</button>
<!-- 如果btn2Click方法需要一个参数,但这里在监听时没有给参数,那么vue会默认将浏览器生成的event事件作为参数传递到函数中 -->
<button @click="btn2Click(123)">Button3</button>
<button @click="btn3Click(10, $event)">Button4</button>
</div>
在某些情况下,我们拿到event的目的可能是进行一些事件处理。
Vue提供了修饰符来帮助我们方便的处理一些事件:
<div id="app">
<div @click="divClick">
<button @click.stop="btnClick">Button</button> # .stop阻止冒泡
</div>
</div>
其它修饰符:
<button @click.prevent></button> # 阻止默认行为
<form action="" @submit.prevent></form> # 阻止默认行为,没有表达式
<button @click.stop.prevent></button> # 串联修饰符
<input type="text" @keyup.enter="onEnter"> # 键修饰符,键别名
<input type="text" @keyup.13="onEnter"> # 键修饰符,键代码
<button @click.once="doThis"></button> # 点击回调只会触发一次
v-if、v-else-if、v-else
v-if的原理:
<div id="app">
<h2 v-if="score>=90">优秀</h2>
<h2 v-else-if="score>=80">良好</h2>
<h2 v-else-if="score>=60">及格</h2>
<h2 v-else>不及格</h2>
</div>
<div id="app">
<span v-if="isUser">
<label for="username">username</label>
<input type="text" id="username" placeholder="username">
</span>
<span v-else>
<label for="email">email</label>
<input type="text" id="email" placeholder="email">
</span>
<button @click="isUser = !isUser">Switch</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isUser: true
}
})
</script>
该案例很简单,点击按钮时input输入框进行切换。但是你会发现如果我们在有输入内容的情况下,切换了类型,会发现文字依然显示之前的输入的内容。按道理讲,我们应该切换到另外一个input元素中了。在另一个input元素中,我们并没有输入内容。为什么会出现这个问题呢?
这是因为Vue在进行DOM渲染时,出于性能考虑,会尽可能的复用已经存在的元素,而不是重新创建新的元素。在上面的案例中,Vue内部会发现原来的input元素不再使用,直接作为else中的input来使用了。
解决方案:如果我们不希望Vue出现类似重复利用的问题,可以给对应的input添加key,并且我们需要保证key的不同
<input type="text" id="username" placeholder="username" key="username">
<input type="text" id="email" placeholder="email" key="email">
v-show的用法和v-if非常相似,也用于决定一个元素是否渲染
v-if与v-show的区别:
开发中如何选择呢?
<ul>
<li v-for="item in movies">{
{
item}}</li>
<li>-------------------------------</li>
<li v-for="(item,index) in movies">{
{
index}} - {
{
item}}</li> # index为数组下标
</ul>
<ul>
<!-- 下面item获取到的是对象的value -->
<li v-for="item in info">{
{
item}}</li>
<!-- 下面的遍历获取到的是对象的value,key -->
<li v-for="(value,key) in info">{
{
key}} - {
{
value}}</li>
<!-- 下面的遍历获取到的是对象的value,key,index -->
<li v-for="(value,key,index) in info">{
{
key}} - {
{
value}} - {
{
index}}</li>
</ul>
官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性
一句话,key的作用主要是为了高效的更新虚拟DOM
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。
Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。
// 1: push
this.letters.push('FF')
// 2: pop
this.letters.pop()
// 3: unshift 向数组的开头添加一个或更多元素,并返回新的长度
this.letters.unshift('why', 'zs')
// 4: shift 把数组的第一个元素从其中删除,并返回第一个元素的值
this.letters.shift()
// 5: splice 传递一个参数(index),将对应index以及后面的所有元素删除
this.letters.splice(2)
// 6: sort 排序
this.letters.sort()
// 7:reverse
this.letters.reverse()
并不是所有的操作都是响应式的:
btnClick() {
// this.letters[1] = 'kobe' # 该操作改变了数组,但页面不会立即渲染
# 下面的操作都是响应式
this.letters.splice(1,1,'kobe')
# set(要修改的对象,索引值,修改后的值)
Vue.set(this.letters, 1, 'kobe')
}
表单控件在实际开发中是非常常见的。特别是对于用户信息的提交,需要大量的表单。
Vue中使用v-model指令来实现表单元素和数据的双向绑定。
<input type="text" v-model="message">
<h2>{
{
message}}</h2>
当我们在输入框输入内容时
因为input中的v-model绑定了message,所以会实时将输入的内容传递给message,message发生改变。
当message发生改变时,因为上面我们使用Mustache语法,将message的值插入到DOM中,所以DOM会发生响应的改变。所以,通过v-model实现了双向的绑定。
当然,我们也可以将v-model用于textarea元素
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
<div id="app">
<input type="text" :value="message" @input="valueChange">
<h2>{
{
message}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello'
},
methods: {
valueChange(event) {
this.message = event.target.value
}
}
})
</script>
<div id="app">
<label for="male">
<input type="radio" id="male" name="sex" value="male" v-model="sex">Male
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="female" v-model="sex">Female
</label>
<h2>你的选择是:{
{
sex}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
sex: 'male' # 默认选择male
},
})
</script>
注意:radio类型中必须有name属性,否则不能互斥(两个都可以选择),如果使用了v-model进行双向绑定时,name属性可以不写,radio也是互斥的
单个勾选框:
<div id="app">
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h2>你选择的是:{
{
isAgree}}</h2>
<button :disabled="!isAgree">下一步</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isAgree: false
},
})
</script>
多个复选框:
<div id="app">
<input type="checkbox" value="football" v-model="hobbies">Football
<input type="checkbox" value="basketball" v-model="hobbies">BasketBall
<input type="checkbox" value="tennis" v-model="hobbies">Tennis
<h2>你的爱好是:{
{
hobbies}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
hobbies: []
},
})
</script>
单选:只能选中一个值。
<div id="app">
<select name="fruit" id="" v-model="fruit">
<option value="Apple">Apple</option>
<option value="Pear">Pear</option>
<option value="Banana">Banana</option>
<option value="Orange">Orange</option>
</select>
<h2>your choice is: {
{
fruit}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruit: 'Orange'
},
})
</script>
多选:可以选中多个值。
<div id="app">
<select name="fruits" id="" v-model="fruits" multiple>
<option value="Apple">Apple</option>
<option value="Pear">Pear</option>
<option value="Banana">Banana</option>
<option value="Orange">Orange</option>
</select>
<h2>your choice is: {
{
fruits}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits: []
},
})
</script>
lazy修饰符:
<input type="text" v-model.lazy="message">
<p>当前内容:{
{
message}}</p>
number修饰符:
<input type="number" v-model.number="age">
<p>age: {
{
age}}, type: {
{
typeof age}}</p>
trim修饰符:
<input type="text" v-model.trim="message">