Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。
安装
- 安装
Node
略… - 安装脚手架
npm install --g vue-cli
- 基于模板初始化项目
vue init webpack my-project
- 切换到目录并且安装依赖
cd my-project && npm install
- 调试
npm run dev
- 编译
npm run build
起步
组件化应用构建
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、自包含和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
- 定义一个最简单的组件
1
2
3
4// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
template: '<li>这是个待办项</li>'
}) - 使用组件
1
2
3
4<ol>
<!-- 创建一个 todo-item 组件的实例 -->
<todo-item></todo-item>
</ol> - 定义一个带属性的组件
1
2
3
4
5
6
7Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义属性
// 这个属性名为 todo。
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
}) - 使用一个带属性的组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<div id="app-7">
<ol>
<!-- 现在我们为每个todo-item提供待办项对象 -->
<!-- 待办项对象是变量,即其内容可以是动态的 -->
<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
</ol>
</div>
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ text: '蔬菜' },
{ text: '奶酪' },
{ text: '随便其他什么人吃的东西' }
]
}
})
属性与方法
- 每个 Vue 实例都会代理其 data 对象里所有的属性:
1
2
3
4
5
6
7
8
9
10
11var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // -> 2
// ... 反之亦然
data.a = 3
vm.a // -> 3注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。我们将在后面详细讨论响应系统。
- 除了 data 属性, Vue 实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $,以便与代理的 data 属性区分。例如:
1
2
3
4
5
6
7
8
9
10
11var 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 (newVal, oldVal) {
// 这个回调将在 `vm.a` 改变后调用
})注意,不要在实例属性或者回调函数中(如 vm.$watch(‘a’, newVal => this.myMethod()))使用箭头函数。因为箭头函数绑定父级上下文,所以 this 不会像预想的一样是 Vue 实例,而是 this.myMethod 未被定义。
实例生命周期
模板语法
插值
- 文本
- 绑定渲染
<span>Message: {{ msg }}</span>
- 一次性渲染
<span v-once>This will never change: {{ msg }}</span>
- 绑定渲染
- 纯HTML
<div v-html="rawHtml"></div>
注意防范XSS攻击渲染的模板不会进行数据绑定 如果需要请使用组件实现
- 属性
- 使用
v-bind
绑定<div v-bind:id="dynamicId"></div>
- 对于
boolean
类型的数据 如果结果为false
则属性会被移除1
<button v-bind:disabled="isButtonDisabled">Button</button>
- 使用
- 使用 JavaScript 表达式
- 可以被支持的语法
1
2
3
4{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>每个绑定只能包含单个表达式
- 错误的语法
1
2
3
4<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 。你不应该在模板表达式中试图访问用户定义的全局变量。
- 可以被支持的语法
指令
指令(Directives)是带有
v-
前缀的特殊属性 其预期值是单一 JavaScript 表达式
- 参数
- 指令可以接受参数 在指令后用
:
指定v-bind
绑定属性1
<a v-bind:href="url"></a>
v-on
绑定方法1
<a v-on:click="doSomething">
- 指令可以接受参数 在指令后用
- 修饰符
- 修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
- 例如
.prevent
修饰符告诉v-on
指令对于触发的事件调用event.preventDefault()
1
<form v-on:submit.prevent="onSubmit"></form>
- 例如
- 修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
过滤器
Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。
1 | <!-- in mustaches --> |
Vue 2.x 中,过滤器只能在 mustache 绑定和 v-bind 表达式(从 2.1.0 开始支持)中使用,因为过滤器设计目的就是用于文本转换。为了在其他指令中实现更复杂的数据变换,你应该使用计算属性。
- 过滤器函数总接受表达式的值作为第一个参数
1
2
3
4
5
6
7
8
9new Vue({
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
}) - 过滤器可以串联
1
{{ message | filterA | filterB }}
- 过滤器可以传参(由于第一个参数为传入值 所以 参数往后延续)
1
2// arg1 实际上为 filterA 方法的第二个参数 以此类推
{{ message | filterA('arg1', arg2) }}
缩写
v-bind
缩写1
2
3
4<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>v-on
缩写1
2
3
4<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
计算属性(computed)
Computed vs Methods
- Computed 是基于它们的依赖进行缓存的 Computed 只有在它的相关依赖发生改变时才会重新求值
- Method 只要发生重新渲染 则始终会执行该函数
Computed 属性 vs Watched 属性 (简化代码)
- 使用
watch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var 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
}
}
}) - 使用
computed
1
2
3
4
5
6
7
8
9
10
11
12var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
- 使用
计算属性添加
Setter
1
2
3
4
5
6
7
8
9
10
11
12
13
14computed: {
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]
}
}
}
观察 Watchers
- 虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher 。这是为什么 Vue 提供一个更通用的方法通过 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
48
49
50
51
52
53
54
55<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraries -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also -->
<!-- gives you the freedom to just use what you're familiar with. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/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) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle), 参考: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function () {
var vm = this
if (this.question.indexOf('?') === -1) {
vm.answer = 'Questions usually contain a question mark. ;-)'
return
}
vm.answer = 'Thinking...'
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
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})
</script>