文章目录
前言
本文是对于Vue学习的记录,主要参考了Vue的官方中文文档,为了方便以后对相关内容的查询,同时希望能够帮助到同样Vue的初学者。
Vue实例对象
Vue所有的实例都起始与Vue函数,Vue函数接受一个对象作为参数,该对象中包含对于Vue行为的定义,因此对于Vue的学习,大多都是针对这些对象选项值的学习。
如下就是一个最基础的Vue实例创建过程
var vm = new Vue({
//对象内的属性,用于对Vue的行为进行定义
});
Vue的MVVM模式
Vue的设计是受到MVVM的启发,在使用Vue开发的项目中,数据的改变会直接影响到视图的渲染,这种数据与视图的同步由称为双向数据绑定(有关双向数据绑定的文章可参考),正是由于Vue的这种设计才让前端程序员从重复的DOM操作中解放出来,将更多时间放在页面交互与数据交互上。
Vue的数据绑定:
// Vue实例代码
var vm = new Vue({
el:"#app"
data:{
message: "Hello Vue!"
}
})
// HTML代码
<div id = "app">
{{message}}
</div>
//Chrome Console中输入下述代码
vm.$data.message = "Goodbye Vue!"
/***
页面的显示也会随着message的改变而改变,从而避免了使用
document.getElementById("app").innerHTML = "Goodbye Vue!"的方式修改代码
***/
Vue数据绑定注意点:
- 只有在实例创建时
data
中存在的属性才是响应式的 - 使用Object.freeze(obj)之后将会阻止修改现有的data属性,那么就无法再追踪变化
- Vue实例的暴露一些属性与方法给用户,并且使用前缀 $ 来与用户定义属性区别
Vue的生命周期
数据绑定手段
Mustache
Mustache语法(双大括号)的文本插值是最常见的
<span> Message: {{msg}} </span>
Mustache中可以使用JavaScript表达式,但是只能包含单个表达式,如果是语句就会报错
JavaScript表达式如下:
{{number + 1}}
{{ok ? 'YES' : 'NO'}}
特性
在希望将Data中的数据作为HTML的特性使用的时候,应该使用v-bind
指令:
<div v-bind:id = "dynamicId"></div>
/***此时该div在浏览器上将会显示为dynamic的值***/
指令
Vue指令时v-
前缀的特殊特性,指令特性的值预期是单个JavaScript表达式(v-for
是例外),指令的职责是当表达式的值改变时,将其产生的连带影响,响应式的作用于DOM
<p v-if = "seen">You can see me, now.</p>
/*** v-if指令根据表达式seen的值的真假来插入/移除<p>元素
指令之后还能接收一个"参数",在指令之后以冒号表示。例如,v-bind
指令可以用于响应式的更新HTML特性
<a v-bind:href = "url"></a>
另一个指令是v-on
指令,其可以用于监听DOM事件
<a v-on:click = "doSomething"></a>
在Vue2.6之后Vue指令还可以接上"动态参数",
动态参数的使用如下:
<a v-bind:[attributeName] = "url"></a>
/***
attributeName的值会作为JavaScript表达式进行动态求值,
比如Vue的一个data属性attributeName的值为"href",那么这个绑定等价与v-bind:href
***/
除了参数与动态参数,Vue指令之后还可以接上修饰符.
,用于指出一个指令应该以特殊方式绑定,例如.prevent修饰符告诉v-on指令对于触发的事件使用event.preventDefault()
<form v-on:submit.prevent = "onSubmit">...</form>
v-on 与 v-bind的缩写
v-bind缩写:
/*** 完整语法 ***/
<a v-bind:href = "url"></a>
/*** 缩写 ***/
<a :href = "url"></a>
v-on缩写
/*** 完整语法 ***/
<a v-on:click = "doSomething"></a>
/*** 缩写 ***/
<a @click = "doSomething"></a>
Computed计算属性
Vue提供Computed计算属性的作用是为模板中过于复杂的逻辑提供一种响应式依赖基础数据的解决方案。
例如:
/*** 过于复杂的模板表达式 ***/
<div>
{{message.split('').reverse().join('')}}
</div>
/*** 使用Computed计算属性之后 ***/
<div id = "example">
{{reversedMessage}}
</div>
new Vue({
el:'example',
data:{
message: "Hello"
},
Computed:{
reversedMessage:function(){
return this.message.split('').reverse().join('')
}
}
})
计算属性的特性
- 计算属性是基于它们的响应式依赖进行缓存的,也就是说当所依赖的值不发生改变时,计算属性的值也不会改变,并且多次获取计算属性的值会使用之前缓存的结果。(在希望缓存大量计算结果时,推荐使用计算属性)
上述功能还可以通过方法实现,具体细节如下:
/*** 使用方法来对完成reverseMessage ***/
<div id = "example">
{{reversedMessage()}}
</div>
new Vue({
el:'example',
data:{
message: "Hello"
},
Computed:{
reversedMessage:{
get:function(){
return this.message.split('').reverse().join('')
},
set:function(newValue){
this.message = newVale.split('').reverse().join('')
}
}
}
})
那么既然方法能完成该功能,还需要计算属性做什么呢?
计算属性与方法最大的区别就是计算属性的特性,即计算属性能够缓存上次的结果,如果依赖值没有改变,那么计算属性就返回之前的计算值,而方法的每次调用意味着一次新的计算。
计算属性不仅只有getter,用户还可以自定义一个setter:
new Vue({
el:"example",
data:{
messsage:"Hello Method"
},
methods:{
reversedMessage:function(){
return this.message.split('').reverse().join('')
}
}
})
watch侦听属性
Vue提供了一种方式来观察和相应Vue实例上的数据变动:侦听属性
一个侦听属性的示例如下:
/*** 使用侦听属性 ***/
<div id = "app-9">
{{fullName}}
</div>
var app9 = new Vue({
el:"#app-9",
data:{
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch:{
firstName:function(val){
this.fullName = val + ' ' + this.lastName
},
lastName:function(val){
this.fullName = this.firstName + ' ' + val
}
}
})
上述内容可以看到,我们对firstName和lastName都进行了侦听,可是代码显得很冗余,因为最终所操作的都是fullName,在这种情况下就可以使用Computed计算属性对代码结构进行优化
示例如下:
/*** 使用计算属性 ***/
<div id = "app-9">
{{fullName}}
</div>
var app9 = new Vue({
el: "#app-9",
data:{
firstName: "Foo",
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch:{
fullName:function(){
fullName = this.firstName + ' ' + this.lastName
}
}
})
watch并不是毫无用武之地,当需要在数据变化时执行异步或开销较大的操作时,watch方式是最有用的。
Class与Style
Style的语法与Class的语法类似,此处就不详细介绍Style的语法了,如果想要查看请浏览Class与Style绑定
对象语法
Vue中Class与Style都是HTML中的特性,因此如果想要绑定数据到class或style上,则需要使用v-bind。以下就是一个简单的class在vue中用例:
<div v-bind:class = "{ active: isActive }"></div>
/***
* 如果isActive为true,那么结果为<div class = "active"></div>
* 如果isActive为false,那么结果为<div class></div>
***/
对于一个HTML节点存在多个class的情况,参见如下实例
/****** HTML ******/
<div id = "vm"
class = "static"
v-bind:class = "{active: isActive, 'text-danger': hasError}">
</div>
/****** Vue ******/
var vm = new Vue({
el:'#vm',
data:{
isActive:true,
hasError:false
}
})
/***
* 最终结果为 <div id = "vm" class = "static active"></div>
***/
绑定的数据对象还可以定义在模板中
/****** HTML ******/
<div id = "vm"
v-bind:class = "classObject">
</div>
/****** Vue ******/
/* 1. 使用data属性作为数据源 */
var vm = new Vue({
el: '#vm',
data:{
classObject:{
active:true,
'text-danger':true
}
}
})
/* 2. 使用computed属性作为数据源 */
var vm = new Vue({
el: '#vm',
data:{
isActive: true,
hasError: true
},
computed:{
classObject:function(){
return {
active: isActive,
'text-danger': hasError
}
}
}
})
/***
* 最终结果为 <div id = "vm" class = "static active text-danger"></div>
***/
数组语法
除了上述的对象绑定语法之外,Vue还支持数组语法
/****** HTML ******/
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
/****** Vue ******/
data: {
isActive: false,
activeClass: 'active',
errorClass: 'text-danger'
}
/***
* 最终结果为 <div id = "vm" class = "text-danger"></div>
***/
组件语法
当在一个自定义组件上使用class
属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。
例如,如果你声明了这个组件:
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
然后在使用它的时候添加一些 class:
<my-component class="baz boo"></my-component>
HTML将被渲染为:
<p class="foo bar baz boo">Hi</p>
对于带数据绑定class也同样适用:
<my-component v-bind:class="{ active: isActive }"></my-component>
当isActive
为truthy
时,HTML将被渲染成为:
<p class="foo bar active">Hi</p>
条件渲染
v-if
Vue中使用v-if来进行条件渲染,当v-if中的条件满足时virtual DOM所对应的DOM元素才会渲染出来。Vue不仅提供了v-if,还有v-else,v-else-if条件。下面就贴上一个示例:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>
条件渲染的情况下可能由于Vue diff算法的复用机制导致意想不到的结果,如下示例
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
那么在上面的代码中切换loginType将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的 placeholder。
这是由于label和input元素在Virtual DOM中处于同级,所以Diff算法就直接复用了label和input元素。如果想要避免上述问题,可以使用key来处理,处理结果如下:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
此时只有label会被复用,而input则不会复用。
v-show
另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注意,v-show
不支持<template>
元素,也不支持 v-else。
v-if vs v-show
-
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
-
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
列表渲染
v-for
使用v-for可以基于一个数组来渲染一个列表,需要使用item in items来遍历Items中的数据,并且items可以是Vue中的一个数组或者对象,针对数组或者对象的遍历,子项的返回值是有区别的。例如
<ul id = "example-1">
<li v-for = "item in items">
{{ item.message }}
</li>
</ul>
var example = new Vue({
el: '#example-1',
data:{
items:[
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
/***
* 上述v-for中得到的子项item是一个数组中的对象
* 每次遍历结果如下:
* 1. item = { message: 'Foo' }
* 2. item = { message:'Bar' }
*
* v-for还支持第二个参数,如下:
* (item, index) in items, 每次遍历如下
* 1. item = { message: 'Foo' }; index = 0;
* 2. item = { message: 'Bar' }; index = 1;
***/
v-for维护策略
对于Vue的v-for渲染元素列表,使用就地复用策略,如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是直接的就地更新每个元素。
v-for数组方法
以下的Vue数组方法的调用都会触发视图的更新:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
对于其他的方法如filter()
, concat()
, slice()
是不会触发v-for数组更新的,但是可以使用新生成的数组替换原有的数组,从而触发更新。
v-for数组注意项
- 使用
vm.items[indexOfItem] = newValue
无法触发Vue视图的更新 - 修改数组长度
vm.items.length = newLength
时,也无法触发Vue视图的更新
针对上述情况,Vue提供如下解决方案
- Vue.set()方法
//Vue.set
Vue.set(vm.items, indexOfItem, newValue)
- splice()方法
//Arrary.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
- vm.$set()方法(Vue.set的别名)
//vm.$set
vm.$set(vm.items, indexOfItem, newValue)
v-for对象注意项
与v-for数组相似,v-for对象也存在限制,Vue不能检测对象属性的添加和删除
var vm = new Vue({
data:{
a: 1
}
})
//vm.a是响应式的
vm.b = 2
//vm.b不是响应式的
为了解决对象无法扩展响应式属性的问题,Vue提供了与Arrary相同的重载方法Vue.set()
来解决
var vm = new Vue({
data:{
userProfile:{
name: 'Anika'
}
}
})
//此方法可以用于新建相应式对象
Vue.set(vm.userProfile, 'age', 17);
并且由于Vue中对象的响应式判断是通过引用来区分的
例如:
Object.assign(vm.userProfile,{
age: 27
})
//此方法不会触发Vue的视图更新,因为Object.assign之后vm.userProfile的引用并没有改变,
那么Vue就不认为vm.userProfile有更新,自然不会重新渲染视图
Object.assign({}, vm.userProfile, {
age: 27
})
//由于{}的引用与vm.userProfile的引用不同,自然Vue会认为vm.userProfile有所更新,自然会重新渲染视图
事件处理
v-on
vue中使用v-on来监听DOM的事件,并在事件触发之后触发相应的JavaScript
<div id="example">
<button v-on:click = "emitFunc">Click here to emit function</button>
</div>
var example1 = new Vue({
el: '#example',
methods:{
emitFunc: function(){
alert('emit function by customer');
}
}
})
事件修饰符
Vue提供一系列事件修饰符:
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
//阻止事件冒泡 同event.stopPropagation()
<a v-on:click.stop = "doThis"></a>
//阻止默认事件 同event.preventDefault()
<form v-on:submit.prevent = "onSubmit"></form>
//在事件捕获阶段触发 同document.addEventListener('xxx', func, true)
<div v-on:click.capture = "doThis"></div>
//当event.target为自身时触发,即不能是冒泡事件所触发的
<div v-on:click.self = "doThis"></div>
//doThis事件只触发第一次
<div v-on:click.once = "doThis"></div>
//滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 `onScroll` 完成,
//即使这其中包含 `event.preventDefault()` 的情况
<div v-on:click.passive = "dotThis"></div>
描述摘自Vue事件修饰符(二)(作者:苏茶茉芳_亚泽伊)
passive这个修饰符会执行默认方法。你们可能会问,明明默认执行为什么会设置这样一个修饰符。这就要说一下这个修饰符的本意了。
[浏览器只有等内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否会调用preventDefault函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。]
通俗点说就是每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。我们加上passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。
这里一般用在滚动监听,@scoll,@touchmove 。因为滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿。我们通过passive将内核线程查询跳过,可以大大提升滑动的流畅度。注:passive和prevent冲突,不能同时绑定在一个监听器上。
按键修饰符
Vue 为v-on:keyup提供了一系列按键修饰符:
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
系统修饰键
Vue 为v-on:keyup还提供了系统修饰符,用于修饰Ctrl, Alt等键位
- .ctrl
- .alt
- .shift
- .meta
- .exact
.exact时vue 2.5.0新增的修饰符,使用如下:
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>
鼠标按钮修饰符
Vue还为鼠标按键提供了修饰符
- .left
- .right
- .middle
表单输入绑定
v-model指令在表单<input>
, <textarea>
, <select>
元素上可以创建双向数据绑定,在绑定了v-model后,html上的value
, checked
, selected
等特性都会被忽略而直接使用Vue对象data中的对应值作为数据源。
以下列出对于不同html元素,v-model所使用的属性和抛出的事件
- text和textarea使用
value
属性和input
事件 - checkbox和radio使用
checked
属性和change
事件 - select字段使用
value
属性和change
事件
v-model修饰符
.lazy
使用.lazy修饰符后,v-model的input
事件会被转变为change
事件触发时才进行数据同步
.number
使输入值自动转换成数值类型,因为即使在有type="number"的情况下,input
的返回值也是字符串。
.trim
自动过滤用户输入的首尾空白
结语
本文章旨在记录Vue基础,内容多参考于Vue官方中文文档,如需了解更多Vue的知识,请移步该文档