Vue-js基础


title: Vue.js基础

date: 2019-09-07 09:42:13

tags:  # 这里写的分类会自动汇集到 categories 页面上,分类可以多级

  • Vue.js # 一级分类
  • Vue.js基础 # 二级分类

介绍

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。

vue 和 react 都不是 mvvm 框架,只是借鉴

什么是 MVVM

MVVM 是 Model-View-ViewModel 的缩写。MVVM 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。

在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。viewmodel 是组件的实例.

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

Vue.js 的优点:

  1. 低耦合。视图(View)可以独立于 Model 变化和修改,一个 ViewModel 可以绑定到不同的”View”上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 view 重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写
  5. 易用灵活高效

引入

在 html 内引入:

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

工程化时最好使用vue/cli构建.

实例和数据绑定

通过构造函数Vue就可以创建一个Vue的实例.并启动Vue应用.

1
2
3
4
let app = new Vue({
el: "#app",
data: { msg: "hello world" },
});

el用于指定页面中的 DOM 元素来挂载 Vue 实例.可以是标签,可以是 css 语法.

通过Vue实例的data选项,可以声明应用内可以双向绑定的数据.建议所有会用到的数据都预先在data内声明,也可以指向一个已有的变量.值可以先设置默认值.

挂载成功后,可以通过app.$el来访问该元素,Vue实例本身也代理了data对象里的所有属性,所以可以这样访问:

访问 Vue 实例的属性: app.$el, app.$data.

访问 data 元素的属性: app.msg

生命周期钩子

钩子(hook),可以理解为挂载点.在整个 Vue 实例创建过程中,有一些挂载点.

生命周期流程:

所有的生命周期钩子自动绑定this上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。这是因为箭头函数绑定了父上下文,因此 this 与你期待的 Vue 实例不同,this.fetchTodos 的行为未定义。

生命周期钩子 类型 详细
beforeCreate Function 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
created Function 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
beforeMoute Function 在挂载开始之前被调用:相关的render函数首次被调用。
mounted Function el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当mounted被调用时vm.$el也在文档内。
beforeUpdate Function 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
updated Function 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或watcher取而代之
actived Function keep-alive 组件激活时调用。
deactivated Function keep-alive 组件停用时调用。
beforeDestroy Function 实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed Function Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
errorCaptured (err: Error, vm: Component, info: string) => ?boolean 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false以阻止该错误继续向上传播。

不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.nextTick替换掉mounted`:

1
2
3
4
5
6
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}

注意updated不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用vm.$nextTick替换掉updated

1
2
3
4
5
6
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}

文本插值和表达式

使用双大括号(Mustache 语法)的方法是最基本的文本插值的语法,它会自动将双向绑定的数据实时显示出来.

1
<span>Message: {{ msg }}</span>

通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

1
<span v-once>这个将不会改变: {{ msg }}</span>

v-html

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

1
2
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。

v-bind

v-bind可缩写成:.

双大括号语法不能作用在 HTML 特性上,遇到这种情况应该使用 v-bind 指令:

1
<div v-bind:id="dynamicId"></div>

对于布尔特性 (它们只要存在就意味着值为 true),v-bind 工作起来略有不同,在这个例子中:

1
<button v-bind:disabled="isButtonDisabled">Button</button>

如果isButtonDisabled的值是null、undefinedfalse,则disabled特性甚至不会被包含在渲染出来的 <button> 元素中。

js 表达式

Vue .js 只支持单个表达式,不支持语句和流控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
{
number + 1;
}
}
{
{
ok ? "YES" : "NO";
}
}
{
{
message.split("").reverse().join("");
}
}
<div v-bind:id="'list-' + id"></div>; //文本插值的形式,其中不能书写表达式,支持单个表达式

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。

有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

1
2
3
4
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。

计算属性

1
2
3
<div id="example">
{{ message.split('').reverse().join('') }}
</div>

这里是想要显示变量 message 的翻转字符串,但是更复杂的就需要计算属性.

所有的计算属性都以函数的形式写在 Vue 实例内的computed选项内,最终返回计算后的结果。

范例:

1
2
3
4
<div id="example">
<p>message: "{{ message }}"</p>
<p>reversed message: "{{ reversedMessage }}</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var vm = new Vue({
el: '#example',
data: {
message: 'hello'
},
computed: {
//计算属性的getter
reversedMessage: function(){
//this指向vm实例
return this.message.split('').reverse.().join('')
}
}
})
//结果:
//message: "hello"
//reversed message: "olleh"

计算属性缓存

1
2
3
4
5
6
// 在组件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}

使用 methods 方法也可以起到和计算属性同样效果

区别:

methods: 如果调用方法,只要页面重新渲染.方法就会重新执行,不需要渲染就不需要重新执行

计算属性: 不管渲不渲染,只要计算属性依赖的数据不变,就不会变

何时使用: 取决于是否需要缓存,当遍历大量数组或大量计算,应该使用计算属性.

计算属性缓存 vs 方法 methods

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

1
2
3
4
5
computed: {
now: function () {
return Date.now()
}
}

相比之下,每当触发重新渲染时,调用方法 methods 将总会再次执行函数。

计算属性 vs 侦听属性 watch

watch:主要用来监听数据变化.可以监听:props,data,computed 内的数据.

属于我的变化影响别人.

计算属性 computed: 依赖 data 中的数据,只有该依赖的数据发生变化才会变化.

属于别人的变化影响我.

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="demo">{{ fullName }}</div>;
var vm = new Vue({
el: "#demo",
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;
},
},
});

计算属性版本:

1
2
3
4
5
6
7
8
9
10
11
12
var vm = new Vue({
el: "#demo",
data: {
firstName: "Foo",
lastName: "Bar",
},
computed: {
fullName: function () {
return this.firstName + " " + this.lastName;
},
},
});

计算属性的 setter

计算属性默认只有getter,不过在需要时你也可以提供一个setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...

现在再运行 vm.fullName = ‘John Doe’ 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。

侦听器 watch

当需要在数据变化时执行异步或开销较大的操作时,更适合用 watch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// `_.debounce` 是一个通过 Lodash 创建的函数防抖.
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
//如果要检索的字符串值没有出现,则indexOf返回 -1。
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>

在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

v-bind 与 class 和 style 的绑定

应用场景: dom 元素经常绑定一些 class 类名或 style 样式

对象语法

可以给v-bind:class传一个对象,动态切换 class:

1
<div v-bind:class="{ active: isActive }"></div>

也可以传入多个属性来动态切换 class.v-bind:class指令也可以与普通 class 属性共存.

绑定的数据对象也可以写在data里.

1
2
3
4
5
6
7
8
9
10
<div v-bind:class="classObject"></div>

<script>
data: {
classObject: {
active: true,
'text-danger': false
}
}
</script>

当 class 的表达式过长或者逻辑复杂,可以绑定一个计算属性.一般当条件多于两个时,就可以使用datacomputed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div v-bind:class="classObject"></div>
<script>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal'
}
}
}
</script>

数组语法

当应用多个 class 时,可以使用数组语法,给v-bind:class绑定一个数组.数组成员直接对应类名.

1
2
3
4
5
6
7
<div v-bind:class="[activeClass, errorClass]"></div>
<script>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
</script>

渲染为

1
<div class="active text-danger"></div>

根据条件切换,也可以使用三元表达式:

1
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

也可以数组语法,对象语法混用:

1
<div v-bind:class="[{ active: isActive }, errorClass]"></div>

绑定内联样式

对象语法

v-bind:style的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象.CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:

1
2
3
4
5
6
7
<div v-bind:style="{color: activeColor, fontSize: fontSize + 'px' }"></div>
<script>
data: {
color: 'red',
fontSize: 30
}
</script>

同样的,对象语法常常结合返回对象的计算属性使用。

数组语法

数组语法可以将多个样式对象应用到同一个元素上:

1
<div v-bind="[baseStyles, overridingStyles]"></div>

应用多个样式对象时,可以使用数组语法.在实际业务中,style 的数组语法并不常用,因为往往可以写在一个对象里面.而较为常用的应当是计算属性.

使用:style时,Vue 会自动给特殊的 css 属性名称增加前缀,比如 transform,无需再加前缀属性.

基本指令

v-cloak

解决因为初始化慢,导致页面闪动的情况.这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕.

一般和CSS规则[v-cloak] { display: none }配合使用(需要写到 css 里).

v-once

只渲染元素和组件一次.之后的重新渲染被当做静态内容跳过,用于优化性能.

写在标签里.

v-pre

跳过这个元素和他子元素的编译过程.可以加快编译.

其他的单独写

v-text, v-html, v-show, v-if, v-else, v-else-if, v-for, v-on, v-bind, v-model, v-slot.

条件渲染

v-if,v-else,v-else-if

用法: 根据表达式值的真假条件渲染元素.在切换时元素及它的数据绑定/组件被销毁并重建.

如果元素是<template>,将提出他的内容作为条件块.

当条件变化时,该指令触发过渡效果.

当和v-for一起使用时,v-for的优先级更高.

也可以用 v-else 添加一个“else 块”:

1
2
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

<template>上使用v-if

因为v-if是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template>元素当做不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含<template>元素。

1
2
3
4
5
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>

v-else-if,顾名思义,充当 v-if 的“else-if 块”,可以连续使用:

1
2
3
4
5
6
7
8
9
10
11
12
<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>

key管理可复用元素

Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染.<input>不会被替换掉——仅仅是替换了它的 placeholder

如果要表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的key属性即可.

1
2
3
4
5
6
7
8
<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>

v-show

1
<h1 v-show="ok">Hello!</h1>

v-show只是切换 css 属性display.v-show不支持<template>元素,也不支持v-else.

v-if 和 v-show 相比

v-if: 真正的条件渲染,条件为真就渲染,为 false 就移除.

v-show: 不管初始条件是什么,都会渲染.只是切换 css 的 display 属性.

总结: 如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好

v-for 列表渲染

两种使用场景:

  1. 遍历多个对象
  2. 遍历一个对象的多个属性

遍历数组语法: v-for="(item, index) in items"

第一个是数组元素名,第二个是索引.

遍历对象的多个属性语法: v-for="(value, name, index) in object"

第一个是属性值,第二个是属性名,第三个是索引.

在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性:

1
2
3
<div v-for="item in items" v-bind:key="item.id">
<!-- 内容 -->
</div>

建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。

Vue 中的 key

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

这里的就地复用的策略复用的是没有发生改变的元素,其他的还要依次重排。

所以我们需要使用 key 来给每个节点做一个唯一标识,Vue 的 Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点,所以一句话,key 的作用主要是为了高效的更新虚拟 DOM.

v-for 和 v-if

当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个v-for 循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:

1
2
3
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>

上面的代码将只渲染未完成的 todo。

而如果你的目的是有条件地跳过循环的执行,那么可以将 v-if 置于外层元素 (或 )上。如:

1
2
3
4
5
6
<ul v-if="todos.length">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
<p v-else>No todos left!</p>

在组件上使用 v-for

当在组件上使用 v-for 时,key 现在是必须的.

然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop,

不自动将 item 注入到组件里的原因是,这会使得组件与 v-for的运作紧密耦合。明确组件数据的来源能够使组件在其他场合重复使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//html
<div id="todo-list-example">
<form v-on:submit.prevent="addNewTodo">
<label for="new-todo">Add a todo</label>
<input
v-model="newTodoText"
id="new-todo"
placeholder="E.g. Feed the cat"
>
<button>Add</button>
</form>
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
</div>

注意这里的 is="todo-item"属性。这种做法在使用 DOM 模板时是十分必要的,因为在 <ul> 元素内只有<li> 元素会被看作有效内容。这样做实现的效果与 <todo-item> 相同,但是可以避开一些潜在的浏览器解析错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//js
Vue.component("todo-item", {
template:
"\
<li>\
{{ title }}\
<button v-on:click=\"$emit('remove')\">Remove</button>\
</li>\
",
props: ["title"],
});

new Vue({
el: "#todo-list-example",
data: {
newTodoText: "",
todos: [
{
id: 1,
title: "Do the dishes",
},
{
id: 2,
title: "Take out the trash",
},
{
id: 3,
title: "Mow the lawn",
},
],
nextTodoId: 4,
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText,
});
this.newTodoText = "";
},
},
});

数组更新过滤排序

改变数组的一系列方法:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

两个数组变动 Vue 检测不到:

  1. 改变数组指定项,vm.items[indexOfItem] = newValue
  2. 改变数组长度,vm.items.length = newLength

解决办法:

改变指定项: Vue.set(app.arr, 1, ‘car’)

改变数组长度: app.arr.splice(1)

显示过滤/排序后的结果

有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际改变或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。

计算属性

1
2
3
4
5
6
7
8
9
10
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}

使用 methods

1
2
3
4
5
6
7
8
9
10
11
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}

v-on

v-on用来绑定事件监听器.v-on可以缩写成@.

在普通元素上,v­-on可以监听原生的 DOM 事件,除了click外,还有dblclickkeyup,mousemove等。

表达式可以是一个方法名,这些方法都写在 Vue 实例的 methods 属性内,并且是函数的形式,

函数内的this指向的是当前 Vue 实例本身,因此可以直接使用this.xxx的形式来访问或修改数据.

如果方法中带有参数,但是没有加括号,默认传原生事件对象 event

动态参数

从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

<a v-bind:[attributeName]="url"> ... </a>

方括号里的属性可以被替换.

可以使用动态参数为一个动态的事件名绑定处理函数:

<a v-on:[eventName]="doSomething"> ... </a>

eventName的值为 “focus” 时,v-on:[eventName]将等价于 v-on:focus

动态参数表达式有一些语法约束,因为某些字符,例如空格和引号,放在 HTML 特性名里是无效的。同样,在 DOM 中使用模板时你需要回避大写键名。

修饰符

在 Vue 中传入 event 对象用$event

修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。

.stop 阻止单击事件冒泡 <a v-on:click.stop="doThis"></a>

.prevent 提交事件不再重载页面

.capture 添加事件侦听器时使用时间捕获模式

.self 只当事件在该元素本身(而不是子元素)触发时触发回调

.once只执行一次的方法

.passive

Vue 还对应 addEventListener 中的 passive 选项提供了 .passive修饰符。

1
2
3
4
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

这个 .passive 修饰符尤其能够提升移动端的性能。

不要把 .passive.prevent 一起使用,因为 .prevent将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

按键修饰符

1
2
<!-- 只有在 `key``Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。

1
<input v-on:keyup.page-down="onPageDown">

在上述示例中,处理函数只会在 $event.key 等于 PageDown 时被调用。

系统修饰键

.ctrl

.alt

.shift

.meta

.exact

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

1
2
3
4
5
6
7
8
<!-- 即使 AltShift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

v-model

用于在表单类元素上双向绑定

可以用于 input 框,以及 textarea 等

注意:所显示的值只依赖于所绑定的数据,不再关心初始化时的插入的 value

v-model的实际

v-model其实是一个语法糖,其实绑定了两层操作:

  1. v-bind绑定一个 value 值
  2. v-on指令给当前元素绑定 input 事件
1
2
3
4
5
6
<input v-model="total">
//等价于
<input
v-bind:value="total"
v-on:input="total = $event.target.value"
>

要使用v-model,要做到:

  1. 接收一个 value 属性
  2. 在有新的 value 时触发 input 事件

文本

1
2
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

多行文本

v-model绑定到textarea

1
2
3
4
5
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>


<textarea v-model="message" placeholder="add multiple lines"></textarea>

单选按钮

  1. 单个单选按钮,直接用 v-­bind 绑定一个布尔值,用 v-­model 是不可以的
  2. 如果是组合使用,就需要 v-­model 来配合 value 使用,绑定选中的单选框的 value 值.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>


<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>


<span>Picked: {{ picked }}</span>
</div>
<script>
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
</script>

复选框

单个复选框,绑定到布尔值:

1
2
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ label }}</label>

多个复选框,绑定到同一个数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>


<span>Checked names: {{ checkedNames }}</span>
</div>
<script>
new Vue({
el: "#example-3",
data: {
checkedNames: []
}
})
</script>

下拉框

单选下拉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="example-5">
<select v-model="selected">
// 将v-model绑定到select上,data中selected使用空字符串
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>

<script>
new Vue({
el: "#example-5",
data: {
selected: ''
}
})
</script>

多选下拉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="example-6">
<select v-model="selected" multiple style="width: 50px">
// 将v-model绑定到select上,data中selected使用空数组
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>

<script>
new Vue({
el: '#example-6',
data: {
selected: []
}
})
</script>

使用v-for配合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>

<script>
new Vue({
el: "...",
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
</script>

绑定值

  • 单选按钮

只需要用 v-­bind 给单个单选框绑定一个 value 值,此时,v­-model 绑定的就是他的 value 值

  • 复选框
  • 下拉框

在 select 标签上绑定 value 值对 option 并没有影响

修饰符

修饰符 解释
lazy v-­model 默认是在 input 输入时实时同步输入框的数据,而 lazy 修饰符,可以使其在失去焦点或者敲回车键之后再更新
number 将输入的字符串转化为 number 类型
trim trim 自动过滤输入过程中首尾输入的空格