vue框架基础

Vue.js优点

1.体积小

压缩后33k

2.更高的运行效率

基于虚拟dom,一种可以预先通过JavaScript进行各种计算,把最终的dom操作计算出来并优化的技术,由于这个dom操作属于预处理操作,并没有真实的操作dom,所以叫虚拟dom。

3.双向数据绑定

让开发者不再去操作dom对象,更多的精力投入到业务逻辑上

4.生态丰富、学习成本低

市场上拥有大量成熟、稳定的基于vue.js的ui框架、常用组件,实现快速开发。对初学者友好、入门容易、学习资料多。

vue的安装与部署

安装:

  • 1.通过script直接引入
1
2
3
4
// <!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
// <!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
  • 2.NPM
1
npm install vue
  • 3.命令行工具(CLI)
1
2
3
npm install -g @vue/cli
// OR
yarn global add @vue/cli

数据与方法

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})

// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的 property 才是响应式的。也就是说如果你添加一个新的 property,比如

1
vm.b = 'hi'

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

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

这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

1
2
3
4
5
6
7
8
9
10
var obj = {
foo: 'bar'
}

Object.freeze(obj)

new Vue({
el: '#app',
data: obj
})
1
2
3
4
5
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! -->
<button v-on:click="foo = 'baz'">Change it</button>
</div>

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便与用户定义的 property 区分开来。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})

生命周期

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
//在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
beforeCreate:function(){
console.log('beforeCreate');
},
/* 在实例创建完成后被立即调用。
在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
然而,挂载阶段还没开始,$el 属性目前不可见。 */
created :function(){
console.log('created');
},
//在挂载开始之前被调用:相关的渲染函数首次被调用
beforeMount : function(){
console.log('beforeMount');
},
//el 被新创建的 vm.$el 替换, 挂载成功
// 注意 mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick
mounted : function(){
console.log('mounted');
},
//数据更新时调用
beforeUpdate : function(){
console.log('beforeUpdate');
},
//组件 DOM 已经更新, 组件更新完毕
updated : function(){
console.log('updated');
},

// 实例销毁之前调用。在这一步,实例仍然完全可用
beforeDestory: function(){
console.log('beforeDestory');
},
// 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
destroyed: function(){
console.log('destroyed');
},
// 被 keep-alive 缓存的组件激活时调用。
activated: function(){
console.log('activated');
},
// 被 keep-alive 缓存的组件停用时调用
deactivated: function(){
console.log('deactivated');
}

模版语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

1.文本
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

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

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

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

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

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

3.Attribute
Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:

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

4.使用javascript表达式

1
2
3
4
5
6
7
{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

指令

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

1.参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind 指令可以用于响应式地更新 HTML attribute:

1
<a v-bind:href="url">...</a>

另一个例子是 v-on 指令,它用于监听 DOM 事件:

1
<a v-on:click="doSomething">...</a>

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

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

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

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

对动态参数的值的约束:
动态参数预期会求出一个字符串,异常情况下值为 null。这个特殊的 null 值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
对动态参数表达式的约束:
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。例如:

1
2
<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>

变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。

在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:

1
2
3
4
5
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>

3.修饰符

修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

1
<form v-on:submit.prevent="onSubmit">...</form>

4.缩写

v-bind缩写:

1
2
3
4
5
6
7
8
<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>

v-on缩写

1
2
3
4
5
6
7
8
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

class和style绑定

  • 绑定html class

1.对象语法

1
2
3
4
5
6
<div v-bind:class="{ active: isActive }"></div>

<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

我们也可以在这里绑定一个返回对象的计算属性:

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

2.数组语法

1
2
3
4
5
6
<div v-bind:class="[activeClass, errorClass]"></div>

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

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

3.用在组件上

1
<my-component v-bind:class="{ active: isActive }"></my-component>
  • 绑定内联样式

1.对象语法

1
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

或者

1
<div v-bind:style="styleObject"></div>
1
2
3
4
5
6
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}

2.数组语法

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

条件渲染

  • v-if
    v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

  • v-else
    你可以使用 v-else 指令来表示 v-if 的“else 块”:

  • v-else-if
    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 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染.如果两个元素是完全独立的,不要复用它们,只需添加一个具有唯一值的key属性即可

  • v-show
    另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

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

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS property 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-if与v-for一起使用

当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级

列表渲染

  • 用 v-for 把一个数组对应为一组元素

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

你也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法:

  • 在 v-for 里使用对象
1
2
3
4
5
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
1
2
3
4
5
6
7
8
9
10
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})

可以提供第二个的参数为 property 名称 (也就是键名):

1
2
3
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>

还可以用第三个参数作为索引:

1
2
3
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>

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

  • 维护状态

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染

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

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

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

  • 数组更新检查

变更方法:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()

替换数组:变更方法,顾名思义,会变更调用了这些方法的原始数组。相比之下,也有非变更方法,例如 filter()、concat() 和 slice()。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组

1
2
3
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})

你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化

事件处理

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码

1
2
3
4
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>

然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

1
2
3
4
<div id="example-2">
<!-- `greet` 是在下面定义的方法名 -->
<button v-on:click="greet">Greet</button>
</div>

除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法

1
2
3
4
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<button v-on:click="say('what')">Say what</button>
</div>

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

1
2
3
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>

事件修饰符

.stop
.prevent
.capture
.self
.once
.passive

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

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

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

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

按键修饰符

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

<!-- 你可以直接将 KeyboardEvent.key 暴露的任意有效按键名转换为 kebab-case 来作为修饰符。 -->
<input v-on:keyup.page-down="onPageDown">

表单输入绑定

你可以用 v-model 指令在表单 <input><textarea> <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理

v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value 作为 prop 并将 change 作为事件

修饰符

  • .lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

1
2
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
  • .number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

1
<input v-model.number="age" type="number">

这通常很有用,因为即使在 type=”number” 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值

  • .trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

1
<input v-model.trim="msg">

组件基础

1
2
3
4
5
6
7
8
9
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

组件的复用

你可以将组件进行任意次数的复用:

1
2
3
4
5
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

data 必须是一个函数
当我们定义这个 <button-counter> 组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

1
2
3
data: {
count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝

1
2
3
4
5
data: function () {
return {
count: 0
}
}

如果 Vue 没有这条规则,点击一个按钮就可能会影响到其它所有实例

通过prop向自组件传递数据

1
2
3
4
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})

监听子组件事件

1
2
3
4
5
6
7
8
9
<blog-post
...
v-on:enlarge-text="postFontSize += 0.1"
></blog-post>
<!-- 同时子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件:
-->
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>

使用事件抛出一个值

1
2
3
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>

在组件上使用v-model

1
2
3
4
5
6
7
<input v-model="searchText">
<!-- 等价于 -->

<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>

当用在组件上时,v-model 则会这样:

1
2
3
4
5
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 必须:
将其 value attribute 绑定到一个名叫 value 的 prop 上
在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

写成代码之后是这样的:

1
2
3
4
5
6
7
8
9
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})

现在 v-model 就应该可以在这个组件上完美地工作起来了

1
<custom-input v-model="searchText"></custom-input>

通过插槽分发内容

1
2
3
4
5
6
7
8
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})

动态组件

有的时候,在不同组件之间进行动态切换是非常有用的

1
2
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

组件注册

定义组件名的方式:

  • 使用kebab-case:以短横线分隔命名,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>
  • 使用 PascalCase:首字母大写命名定义组件,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name><MyComponentName> 都是可接受的

全局注册

1
2
3
Vue.component('my-component-name', {
// ... 选项 ...
})

局部注册

1
2
3
4
5
6
7
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})