Vue 基础

本文将介绍 Vue 的基础语法。

一、实例参数

1
2
3
var 实例名 = new Vue({
// 参数
})

创建 Vue 实例时,需要为其传入参数,其中:

1. el

决定了 Vue 实例将会管理哪一个元素。

2. data

Vue 实例对应的数据对象。

3. methods

定义属于 Vue 实例的方法。

二、响应式

1. 响应式属性

当一个 Vue 实例被创建时,它会将 data 中的所有属性都加入响应式系统中。当这些值发生改变时,自动发生响应,视图发生改动。

2. 注意事项

需要注意的是:只有当创建实例时就已经存在于 data 中的值才会发生响应。如果在定义了 Vue 实例以后,再为实例添加新的属性,则对该属性的改动将不会触发视图的更新。

如果需要为属性“占坑”,可以在创建 Vue 实例时,为属性设置空的初始值。

1
2
3
4
5
6
7
data: {
newTodoText: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}

3. Object.freeze()

并且,如果使用了 JavaScript 中的 Object.freeze() 方法,则无法改动该对象,因此也就无法利用该对象改变视图层。

Object.freeze() 方法可以冻结一个对象。

一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

三、实例生命周期钩子

1. 生命周期

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。

2. 生命周期钩子

可以在创建 Vue 实例时,在参数中填入对应的(生命周期钩子)函数,它会在实例生命周期中的对应阶段被自动调用。

四、插值

1. Mustache

即双大括号

1
{{数据属性}}
  • 显示时,Mustache 标签将会被替代为对应的数据属性值

  • 当数据属性发生改变时,插值处的内容也会进行更新

  • 双大括号内不仅可以写数据属性,还可以写简单的表达式

    1
    2
    3
    4
    5
    6
    7
    8
    <div>{{num * 2}}</div>

    var app = new Vue({
    el: 'div',
    data: {
    num: 30,
    }
    })

  • 双大括号可以配合括号之外的文本显示

    1
    2
    3
    4
    5
    6
    7
    8
    ><div>数字是:{{num * 2}}</div>

    >var app = new Vue({
    el: 'div',
    data: {
    num: 30,
    }
    >})

2. v-once

通过 v-once 指令,能够进行一次性的插值。数据仅会在最开始时进行一次插值,此后都不会再进行更新。

1
2
3
4
5
6
7
8
9
10
11
<div>
<h1>这个信息可以被改变:{{message}}</h1>
<h1 v-once>这个信息将无法被改变:{{message}}</h1>
</div>

var app = new Vue({
el: 'div',
data: {
messang: "你好",
}
})

3. v-html

如果需要插值的内容并不是简单的字符串,而是 HTML 代码,则可以通过 v-html 实现。此时,不会再将数据属性解释为普通文本,而是将 数据属性直接替换至 HTML 元素之中。

1
2
3
4
5
6
7
8
<div v-html="message"></div>

var app = new Vue({
el: 'div',
data: {
message: '<a href="https://www.baidu.com">百度</a>',
}
})

  • HTML 元素中的内容(包括双引号语法)都将被覆盖,因为它们会直接被替代
  • v-html 应该仅对可信内容使用,如果对未知内容进行 HTML 插值,将可能被恶意攻击

4. v-text

与双引号语法类似,将数据属性解析为文本后替换至 HTML 元素之中。

与 v-html 类似,替换后将覆盖原本的所有内容。

双引号语法相比 v-text 更加灵活

5. v-pre

跳过元素和它的子元素的编译过程,用于输出原始内容。

与 HTML 中的 pre 类似

1
2
3
4
5
6
7
8
9
10
11
<div>
<h1 v-pre>{{message}}</h1>
<h1>{{message}}</h1>
</div>

var app = new Vue({
el: 'div',
data: {
message: '你好',
}
})

6. v-cloak

在实际的网页加载过程中,有可能 HTML 已经加载出来,而 JavaScript 还尚未加载。此时网页中就会显示出未经处理的 HTML 内容,并在 JavaScript 成功后出现”闪烁”。例如:

可以通过 v-cloak 和 CSS 样式解决这一问题。

  • v-cloak:这个属性将被添加在 HTML 中,并在 Vue 实例成功加载后被移除

  • CSS 样式:可以通过属性选择器选中元素,并将其设置为 display: none ,使它在加载完成之前被隐藏

    display: none 是官方写法,但个人认为 visibility: hidden 更好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div>
<h1 v-cloak>{{message}}</h1>
<h1 v-cloak>{{name}}</h1>
</div>

[v-cloak] {
display: none;
}

setTimeout(function(){
var app = new Vue({
el: 'div',
data: {
message: '你好',
name: '小张',
}
})
}, 2000)

五、绑定属性

1. 绑定一般属性

通过 v-bind 动态绑定 HTML 属性值,从而为实现响应式的属性

(1) 基本写法

语法:
1
v-bind:HTML属性="数据属性"
示例:
1
2
3
4
5
6
7
8
9
10
11
<div>
<a v-bind:href="weblink">{{webname}}</a>
</div>

var app = new Vue({
el: 'div',
data: {
webname: '百度',
weblink: 'https://www.baidu.com'
}
})

(2) 语法糖

v-bind:HTML属性="数据属性" 可以简写为 :HTML属性="数据属性"

在绑定 classstyle 时,支持其它类型的值,如数组或对象。并且动态绑定的 class/style 能够与普通的 class/style 共存。

2. 绑定 class

(1) 基本写法

(2) 对象写法

语法:
1
:class="{类名1: 布尔值, 类名2: 布尔值}"

若布尔值为 true ,则类名会生效;若布尔值为 false,则类名无效。

1
:class="{name1: true, name2: false}"

等效于

1
class="name1"
通常做法:

为布尔值设置一个 Vue 属性,用于指示类名是否生效。

1
2
3
4
5
6
7
8
9
:class="{类名1: is类名1, 类名2: is类名2}"

var app = new Vue({
el: 'div',
data: {
is类名1: true,
is类名2: false,
}
})
示例:
1
2
3
4
5
6
7
8
9
10
11
12
<div :class="{red: isred, background: isbackground}">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
message: '这是一段话',
isred: true,
isbackground: false,
}
})

(3) 对象+方法

可以通过方法返回类的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div :class="getClasses()">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
message: '这是一段话',
isred: true,
isbackground: false,
},
methods: {
getClasses: function() {
return {red: this.isred, background: this.isbackground};
}
}
})

(4) 数组写法

语法:
1
:class="['类名1', '类名2']"
通常写法:

不直接写类名(因为直接写类名的话根本没有意义),而是在其中填入数据属性,从而实现响应式类名

1
2
3
4
5
6
7
8
9
:class="[类变量1, 类变量2]"

var app = new Vue({
el: 'div',
data: {
类变量1: "类1",
类变量2: "类2",
}
})

(5) 数组+方法

可以通过方法返回类的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div :class="getClasses()">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
类变量1: "类1",
类变量2: "类2",
},
methods: {
getClasses: function() {
return [this.类变量1, this.类变量2];
}
}
})

3. 绑定 style

(1) 基本写法

(2) 对象写法

语法:
1
:style="{CSS属性1: '属性值', CSS属性2: '属性值'}"
通常做法:

设置数据属性,方便根据需要修改 CSS 样式。

1
2
3
4
5
6
7
8
9
:style="{CSS属性1: 属性变量1, CSS属性2: 属性变量2}"

var app = new Vue({
el: 'div',
data: {
属性变量1: '属性值',
属性变量2: '属性值',
}
})
示例:
1
2
3
4
5
6
7
8
9
10
11
12
<div :style="{color: divcolor, fontSize: divsize}">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
message: '这是一段话',
divcolor: 'red',
divsize: '50px',
}
})

(3) 对象+方法

可以通过方法返回 CSS 的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div :style="getStyles()">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
message: '这是一段话',
divcolor: 'red',
divsize: '50px',
},
methods: {
getStyles: function() {
return {color: this.divcolor, fontSize: this.divsize};
}
}
})

(4) 数组写法

语法:
1
2
3
4
5
6
7
8
9
:style="[CSS对象1, CSS对象2]"

var app = new Vue({
el: 'div',
data: {
CSS对象1: {CSS属性1: '属性值', CSS属性2: '属性值'},
CSS对象2: {CSS属性3: '属性值'},
}
})
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div :style="[baseStyles, fontStyles]">
{{message}}
</div>

var app = new Vue({
el: 'div',
data: {
message: '这是一段话',
baseStyles: {
color: "red",
backgroundColor: "blue",
},
fontStyles: {
fontSize: "50px",
}
}
})

六、计算属性

1. 计算属性是什么?

可以理解为对于原有属性进行逻辑运算后的结果。

2. 为什么要用计算属性?

由 Vue 实例如下:

1
2
3
4
5
6
7
var app = new Vue({
el: 'div',
data: {
firstname: 'Steve',
lastname: 'Jobs',
}
})

假设需要获得完整的名字,此时有几种做法:

(1) 表达式

1
<p>{{firstname}} + " " + {{lastname}}</p>

这样的做法臃肿且难以维护

(2) 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<p>{{getFullName()}}</p>

var app = new Vue({
el: 'div',
data: {
firstname: 'Steve',
lastname: 'Jobs',
},
method: {
getFullName: function() {
return this.firstname + " " + this.lastname;
}
}
})

这样的方法相比于表达式的做法更加简洁,但也存在问题:在应填入 数据属性的位置填入方法,略显奇怪

(3) 计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<p>{{fullname}}</p>

var app = new Vue({
el: 'div',
data: {
firstname: 'Steve',
lastname: 'Jobs',
},
computed: {
fullname: function() {
return this.firstname + " " + this.lastname;
}
}
})

相比于函数的做法:

  • 更加简洁
  • 计算属性以属性的形式在 HTML 中使用,更加符合习惯
  • 计算属性有缓存,可以避免重复计算

3. get 和 set

(1) 计算属性的完整写法

1
2
3
4
5
6
7
8
9
10
computed: {
计算属性名: {
get: function () {
// getter 指示应该如何取计算属性
},
set: function (newValue) {
// setter 指示计算属性被"赋值"后,应该如何处理
}
}
}

(2) 简写计算属性

set 一般不会使用,因此可以将其留空或省略,还可以直接简写计算属性。

1
2
3
4
5
computed: {
计算属性名: function () {
// getter 指示应该如何取计算属性
}
}

4. 计算属性的缓存

计算属性拥有缓存,它会且仅会在它的相关属性发生改变时才重新求值。因此,在需要多次获得某个“复合值”时,如果通过函数实现,则系统需要进行多次求值;而通过计算属性实现时,系统只会计算一次,并在下一次获取时直接返回计算结果。

在非特殊情况下,尽量使用计算属性,它将能够大大减少性能开销。

七、监听器

1. 什么是监听器?

监听器用于监听数据的变化,当数据变化时,执行对应的方法。

2. 语法

1
2
3
4
5
6
7
8
9
10
11
var app = new Vue({
el: 'div',
data: {
数据属性名: 数据属性值,
},
watch: {
数据属性名() {
···
}
}
})

监听器有两个可选参数,其中第一个参数会被传入更新前的数据,第二个参数会被传入更新后的数据。

3. 示例

(1) 打印数据的改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<h1>{{message}}</h1>
<input v-model="inputtext" placeholder="请输入" >
<p>您输入的是:{{inputtext}}</p>
</div>

var app = new Vue({
el: '#app',
data: {
message: '输入框:',
inputtext: '',
},
watch: {
inputtext(){
console.log(this.inputtext)
}
}
})

(2) 防止修改数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<h1>{{message}}</h1>
<input v-model="inputtext" placeholder="请输入" >
<p>您输入的是:{{inputtext}}</p>
</div>

var app = new Vue({
el: '#app',
data: {
message: '输入框:',
inputtext: '',
},
watch: {
inputtext(){
this.inputtext = '嘿嘿 改不了'
}
}
})

(3) 应用可选参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
<h1>{{message}}</h1>
<input v-model="inputtext" placeholder="请输入" >
<p>您输入的是:{{inputtext}}</p>
<p>现在是:{{newValue}}</p>
<p>之前是:{{oldValue}}</p>
</div>

var app = new Vue({
el: '#app',
data: {
message: '输入框:',
inputtext: '',
newValue: '还没输入呢',
oldValue: '还没输入呢',
},
watch: {
inputtext(newva, oldva){
this.newValue = newva;
this.oldValue = oldva;
}
}
})

4. 监听器的动态注册与注销

(1) 为什么需要动态生成与注销?

有时候,我们不希望监听器始终生效,而是希望它在适当的时候出现,并在适当的时候消失。

(2) 动态注册

可以通过 app.$watch() 方法动态注册监听器,具体语法如下:

1
app.$watch("属性名", function)

(3) 动态注销

调用 app.$watch() 方法时会返回一个 unWatch() 方法,只需要保存它并在适当的时候调用,就可以动态注销监听器。

(4) 示例

有一个数字,它将会随时间 “正常递增”,也可能被用户 “非法篡改”。

我们希望监听它的 “非法篡改”。

代码如下:

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
<template>
<div class="home">
<el-input style="width: 300px" v-model="num"></el-input>
<p v-if="isEdit">你进行了编辑</p>
</div>
</template>

<script>
export default {
name: "Test",
data() {
return {
num: 1,
isEdit: false
}
},
mounted() {
setInterval(() => {
this.num++
},2000)
},
watch: {
num() {
this.isEdit = true
}
}
}
</script>

理想状态下,监听器只应该监听 “非法篡改”,而不应该监听 “正常递增”。

但实际情况是,监听器会监听每一个改变,包括 “正常递增”。

此时,我们便可以使用动态注册与注销监听器的方法。

代码如下:

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
<template>
<div class="home">
<el-input style="width: 300px" v-model="num"></el-input>
<p v-if="isEdit">你进行了编辑</p>
</div>
</template>

<script>
export default {
name: "Test",
data() {
return {
num: 1,
isEdit: false,
watch: null
}
},
mounted() {
setInterval(() => {
this.watch && this.watch()
this.num++
this.watch = this.$watch('num', () => {
this.isEdit = true
})
},2000)
}
}
</script>

在 “正常递增” 之前注销监听器,并在 “正常递增” 之后注册监听器,便可以实现我们想要的效果。

八、事件处理

1. 事件监听

可以用 v-on 监听 DOM 事件,当事件触发时执行相应的代码。

(1) 直接嵌入代码

1
v-on:事件="代码"

(2) 绑定方法

1
2
3
4
5
6
7
8
9
10
v-on:事件="方法名"

var app = new Vue({
el: 'div',
methods: {
方法名() {
···
}
}
})

(3) 调用方法

1
2
3
4
5
6
7
8
9
10
v-on:事件="方法名(参数)"

var app = new Vue({
el: 'div',
methods: {
方法名(参数) {
···
}
}
})

2. 语法糖

v-on:事件="代码" 可以简写为 @事件="代码"

3. 参数

如果方法本身无需传递参数,在监听事件时省略括号,方法将正常执行。

1
2
3
4
5
6
@事件="方法名"

// 方法将正常执行
方法名() {
···
}

如果方法本身需要传递参数,在监听事件时不传入参数且不省略括号,则参数会被设为 undefined 。

1
2
3
4
5
6
@事件="方法名()"

// parameter将被赋值为undefined
方法名(parameter) {
···
}

如果方法本身需要传递参数,在监听事件时不传入参数且省略括号,此时 Vue 会将浏览器自动生成的 event 事件对象传入方法。

Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。

1
2
3
4
5
6
@事件="方法名"

// parameter将被赋值为event
方法名(parameter) {
···
}

如果需要为方法传入 event 事件,可以使用 $event 将其传入。

1
2
3
4
5
6
@事件="方法名(值, $event)"

// 值将依次传给参数,event事件也将传递给参数
方法名(parameter, event) {
···
}

4. 修饰符

(1) 语法

1
@事件.修饰符="代码"

(2) .stop

用于阻止事件冒泡。

例如:

1
2
3
<div>
<p></p>
</div>

点击 p 后事件冒泡,div 也将触发点击事件,这便是事件冒泡。可以通过 .stop 阻止事件冒泡。

(3) .self

仅在事件绑定的元素与触发事件的元素一致时才执行方法。即仅在事件与元素相对应时,才会执行方法。

(4) .capture

正常而言,当事件触发时,从子层开始执行,并逐个向上冒泡。

给元素添加了 .capture 修饰符后,当发生冒泡时,将会首先触发带有 .capture 的元素。若多个元素带有 .capture ,则这些元素由外向内触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div @click="div">
<h1 @click="h1">
<p @click="p">
<span @click="span">
测试
</span>
</p>
</h1>
</div>

methods: {
div() {
console.log('div')
},
h1() {
console.log('h1')
},
p() {
console.log('p')
},
span() {
console.log('span')
}
}

默认情况下,触发顺序为从子层开始执行,逐个向上冒泡:

若设置 .capture,则设置了 .capture 的元素将优先被执行:

1
2
3
4
5
6
7
8
9
<div @click="div">
<h1 @click.capture="h1">
<p @click="p">
<span @click="span">
测试
</span>
</p>
</h1>
</div>

若同时有多个元素设置了 .capture ,则这些元素由外向内触发。

1
2
3
4
5
6
7
8
9
<div @click.capture="div">
<h1 @click.capture="h1">
<p @click="p">
<span @click="span">
测试
</span>
</p>
</h1>
</div>

(5) .prevent

用于阻止默认事件。

例如阻止 submit 默认的提交事件。

(6) .once

事件将只会触发一次。

(7) 按键修饰符

可以通过按键修饰符来使系统只监听特定按键。

1
2
<!--只有在enter键被抬起时,才会执行方法-->
<input v-on:keyup.enter="fun">

(8) .native

对于自定义组件,Vue 只会监听通过 $emit 触发的事件,.native 修饰符用于监听组件的原生事件。

例如组件中的 click

5. 为什么在 HTML 中监听事件?

你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用 v-on 有几个好处:

  1. 扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
  2. 因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
  3. 当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

九、条件渲染

1. v-if

v-if 指令用于根据条件决定是否渲染一块内容。

  • v-if 为 true 时,元素将正常显示

  • v-if 为 false 时,元素及其子元素都不会被渲染

1
2
3
<div v-if="isShow">
isShow为真时,显示我
</div>

2. v-else

v-else 紧跟在 v-if 或者 v-else-if 之后,用于在 v-if 为 false 时显示另一个元素。

1
2
3
4
5
6
<div v-if="isShow">
isShow为真时,显示我
</div>
<div v-else>
isShow为假时,显示我
</div>

3. v-else-if

v-else-if 紧跟在 v-if 或者 v-else-if 之后,类似 else if

1
2
3
4
5
6
7
8
9
<div v-if="score>=80">
优秀
</div>
<div v-else-if="score >= 60">
及格
</div>
<div v-else>
不及格
</div>

4. 元素的复用

(1) 元素复用问题

在通过 v-if 进行元素的切换,会发现一个问题:

1
2
3
4
5
6
7
8
9
10
11
<div>
<div v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="请输入用户账号">
</div>
<div v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="请输入用户邮箱">
</div>
<input type="button" value="点击切换" @click="isUser=!isUser">
</div>

虽然切换了两个不同的输入框,但输入框中的内容并没有被清除,这在某些情况下是不合理的。这是因为 Vue 进行了元素复用。

(2) Vue 的渲染方式

Vue 为了尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染,通过这样方式使 Vue 更”快”。

(3) 确保被重新渲染

如果需要确保输入框被重新渲染,可以为不同元素添加不同的 key 值。

1
2
3
4
5
6
7
8
9
10
11
<div>
<div v-if="isUser">
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="请输入用户账号" key="username">
</div>
<div v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="请输入用户邮箱" key="email">
</div>
<input type="button" value="点击切换" @click="isUser=!isUser">
</div>

5. v-show

v-D 指令用于根据条件决定是否显示元素。

  • v-show 为 true 时,元素将正常显示

  • v-show 为 false 时,元素及其子元素都不会显示

6. v-if 和 v-show

  • v-if 决定元素是否被渲染,当条件为假时,元素并不会出现在 DOM 中

    v-show 仅仅决定元素是否显示

  • v-if 有更高的切换开销,因为每次切换需要进行元素的摧毁与重建

    v-show 有更高的促使渲染开销,因为它的初始化必定会渲染元素

  • 如果需要频繁切换,使用 v-show

    如果条件改变较少,使用 v-if

十、列表渲染

1. v-for

(1) 语法

1
v-for="item in items"

(2) item

item 为元素的别名,可以任意修改,例如 i in items

(3) items

items 为数据来源,

  • 可以是 Vue 实例中的数组、对象、计算属性、方法

  • 可以是数值

    1
    2
    3
    v-for="n in 10"

    遍历 1 ~ 10

2. 遍历数组

元素、索引的别名并不严格要求,可以随意取

(1) 获得元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div>
<ul>
<li v-for="item in items">{{item}}</li>
</ul>
</div>

var app = new Vue({
el: 'div',
data: {
items: [
"zhang",
"wang",
"li",
"liu"
]
})

(2) 获得元素及索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div>
<ul>
<li v-for="(item, index) in items">
第{{index+1}}个是{{i}}
</li>
</ul>
</div>

var app = new Vue({
el: 'div',
data: {
items: [
"zhang",
"wang",
"li",
"liu"
]
})

3. 遍历对象

属性值、属性名的别名并不严格要求,可以随意取

(1) 获得属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div>
<ul>
<li v-for="value in person">{{value}}</li>
</ul>
</div>

var app = new Vue({
el: 'div',
data: {
person: {
name: "zhang",
age: 18,
gender: "man",
score: 95,
},
}
})

(2) 获得属性值和属性名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div>
<ul>
<li v-for="(value, key) in person">
{{key}}:{{value}}
</li>
</ul>
</div>

var app = new Vue({
el: 'div',
data: {
person: {
name: "zhang",
age: 18,
gender: "man",
score: 95,
},
}
})

(3) 获得属性值、属性名及索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div>
<ul>
<li v-for="(value, key, index) in person">
{{index+1}}.{{key}}:{{value}}
</li>
</ul>
</div>

var app = new Vue({
el: 'div',
data: {
person: {
name: "zhang",
age: 18,
gender: "man",
score: 95,
},
}
})

4. :key

建议在使用 v-for 时,给对应元素加上一个 :key 属性。

1
<li v-for="item in items" :key="item">{{item}}</li>

通过 :key ,可以使 Vue 在进行数据项的更新时,能够更加高效地更新虚拟 DOM 。

5. 数组更新的监听

(1) 响应式的方法

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

这些方法对于数组的改动是响应式的,将会触发视图更新。

(2) 非响应式的改动

通过索引值直接修改元素,将无法触发视图的更新。

解决方法:用 splice(索引, 1, 新元素) 替代

部分方法并不会修改原始数组,而是返回新数组,这种方法也无法触发视图的更新。

解决方法:数组 = 方法(数组) ,即用新的数组替代原来的数组

6. v-for 和 v-if 配合使用

不推荐在同一元素上使用 v-ifv-for

(1) 同一节点中

v-if 将重复运行于每一次 v-for 循环中,可以借此来渲染符合条件的元素。

(2) v-if 放置于外层

能够实现通过条件决定是否跳过循环。

十一、双向绑定

1. v-model

v-model 可以在 input、textarea、select 上创建双向绑定。从而能够根据页面输入更新数据,根据数据更新改变页面。

本质上 v-model 是一个语法糖,是属性绑定和事件监听的结合。

v-model 会忽略所有表单元素的 valuecheckedselected 而总是将 Vue 实例的数据作为初始值。

2. v-model 的自适应

v-model 会为不同的元素绑定不同的属性并监听不同的事件,例如:

  • text 和 textarea 元素绑定 value 并监听 input 事件
  • checkbox 和 radio 绑定 checked 并监听 change 事件
  • select 绑定 value 并监听 change 事件

3. 绑定输入框

绑定至字符串,与输入框中输入的内容相对应。

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

1
2
<textarea v-model="message"></textarea>
<p>Message is: {{ message }}</p>

4. 绑定 radio

绑定至字符串,与选中项的 value 相对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div>
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>

new Vue({
el: '#example-4',
data: {
picked: ''
}
})

如果需要默认选中某个选项,可以在 Vue 实例中,将 picked 设为对应选项的 value 。

5. 绑定 checkbox

(1) 单个复选框

绑定至布尔值,与复选框的选中/未选中的状态相对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
<div>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">是否选择?</label>
<br>
<span>checked: {{ checked }}</span>
</div>

var app = new Vue({
el: 'div',
data: {
checked: false,
}
})

(2) 多个复选框

绑定至数组,数组元素为已选中选项的 value 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div>
<input type="checkbox" id="jack" value="jack" v-model="checkedNames">
<label for="jack">jack</label>
<br>
<input type="checkbox" id="john" value="john" v-model="checkedNames">
<label for="john">john</label>
<br>
<input type="checkbox" id="mike" value="mike" v-model="checkedNames">
<label for="mike">mike</label>
<br>
<span>checkedNames: {{ checkedNames }}</span>
</div>

var app = new Vue({
el: 'div',
data: {
checkedNames: [],
}
})

6. 绑定 select

(1) 单选

绑定至字符串,与选中项相对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div>
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>Selected: {{ selected }}</p>
</div>

var app = new Vue({
el: 'div',
data: {
selected: '',
}
})

如果 v-model 表达式的初始值未能匹配任何选项,<select> 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

(2) 多选

绑定至数组,数组元素为已选中选项的 value 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>Selected: {{ selected }}</p>
</div>

var app = new Vue({
el: 'div',
data: {
selected: [],
}
})

7. 值绑定

即将选项从静态的字符串改成动态绑定的值,从而实现了选项的动态生成和动态修改。

8. 修饰符

(1) .lazy

正常情况下,每次 input 触发时,输入框的值都将与数据进行同步。如果不希望这么”频繁”,便可以通过 .lazy 修饰符解决。它使 v-model 仅在 change 事件触发后才进行同步,具体体现为敲回车或失去焦点时,才进行同步。

(2) .number

用于将用户输入的值自动转换为数值类型。

否则 Vue 会自动将数据转换为字符串类型,即使原本输入的是数值

(3) .trim

自动过滤用户输入的首尾空白字符。

十二、runtime-only 和 runtime-compiler

  • runtime-only 运行步骤为:template -> ast -> render -> 虚拟dom ->真实dom
  • runtime-compiler 运行步骤为:render -> 虚拟dom ->真实dom

比较而言,runtime-only 运行更快,代码更少,但无法直接解析 Vue 中的 template ,需要由 vue-template-compiler 插件预先进行解析。

Vue-CLI 会帮我们安装并配置

参考