computed
模板内表达式只能用于简单运算,难以处理复杂逻辑。对于任何复杂逻辑,都应当使用计算属性。
- HTML例
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
在模板中的使用与模板表达式没有区别。
- JS例
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
全写与简写
完整的计算属性函数包括 getter
setter
函数:
computed: {
message: {
// 计算属性的 getter,返回到模板
get(){
return this.message
}
// 计算属性的 setter,返回到data
set(val){
return val
}
}
}
一般情况下计算属性只有 getter
需求,则可以简写为一个函数:
computed: {
// 计算属性的 getter
message() {
return this.message
}
}
和调用方法的区别——缓存
通过在表达式中调用方法可以达到与计算属性同样的效果,两种方式的最终结果确实是完全相同的。
不同点:
- 计算属性的值依赖计算函数中依赖的其它响应式数据,如果依赖的其它响应式数据没有发生变化,计算属性的值可以缓存,得到结果是最近一次变化产生的值。如果响应式数据没有改变,直接返回之前缓存的结果,不必再次执行函数。
- 每当触发重新渲染时,不管相关数据变不变,调用方法将总会再次执行函数。
如果计算量大且复杂,用计算属性;如果不希望有缓存,用方法代替。
watch
当需要在数据变化时执行异步或开销较大的操作时,watch监听器(侦听器)是最有用的。
- HTML例
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
- JS例
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...'
}
},
})
多层监听
对于多层对象数据的监听,可以使用字符串和点的语法。
data: {
a:{
b:{
c:''
}
}
}
watch: {
'a.b.c': function() {
//...
}
}
深度监听
默认情况下,watch
只对当前指定的值进行一层监听,如果需要对对象进行深度监听,可以使用下面的形式:
watch: {
a: {
handler() {
console.log('a deep');
},
deep: true
}
}
指令
指令 (Directives) 是特殊 attribute。指令的职责是,当表达式的值改变时,响应式地作用于 DOM,进行更强大的 DOM 操作。
在 vue
中,指令是一个带有 v-
前缀的属性,与普通属性不一样的地方在于,指令的值是引号括起来的 表达式 ,不同的指令有不同的作用。vue
内置了一些常用的指令,我们还可以自定义属于自己的指令。
注意: 指令值中直接写表达式,不能使用
{{}}
内置指令
功能 | 指令 | 功能和用法 |
---|---|---|
- 显示与隐藏 | v-show | 根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)。适用于状态切换比较频繁的情况 |
- 条件渲染 | v-if / v-else / v-else-if | 根据表达式的值(布尔值),创建或销毁元素。适用于状态切换不频繁的情况 |
- 列表渲染 | v-for | 根据数据循环渲染 v-for 指令所在的元素及其子元素。可以循环的数据:Array,Object,number,string,Iterable (2.6 新增) |
- 属性绑定 | v-bind | 绑定数据(表达式)到指定的属性上。对应的缩写为:
|
- 事件 | v-on | 绑定事件函数(表达式)到指定的属性上。缩写 @
|
- 双向绑定 | v-model | 数据 title 更新,视图中 input 的 value 就会更新。同时,当 input 中的 value 更新的时候,数据 title 也会更新。 |
指令修饰符
一个指令可以包含的内容包括:
- 指令名称
- 指令值
- 指令参数
- 指令修饰符
<组件 指令:参数.修饰符1.修饰符2="值" />
v-model的修饰符
修饰符 | 功能和用法 |
---|---|
.lazy | 取代 input 监听 change 事件 |
.number | 输入字符串转为有效的数字 |
.trim | 输入首尾空格过滤 |
v-on的修饰符
修饰符 | 功能和用法 |
---|---|
.stop | 调用 event.stopPropagation()。 |
.prevent | 调用 event.preventDefault()。 |
.capture | 添加事件侦听器时使用 capture 模式。 |
.self | 只当事件是从侦听器绑定的元素本身触发时才触发回调。 |
.once | 只触发一次回调。 |
.passive | (2.3.0) 以 { passive: true } 模式添加侦听器 |
自定义指令
我们还可以通过 Vue
提供的方法来自定义指令。
注册指令
vue
提供了两种指令注册方式
- 全局指令
- 局部指令
全局指令注册
Vue.directive('指令名称', {指令配置});
局部指令注册
new Vue({
el: '#app',
directives: {
'指令名称': {指令配置}
}
});
在使用指令的时候,需要使用
v-指令名称
的方式来调用,注册的时候不需要。
指令生命周期(钩子函数)
指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了
- <u>bind</u> : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- <u>inserted</u> : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
- <u>update</u> : 所在组件更新的时候调用(但是可能发生在其子 VNode 更新之前)。
- <u>componentUpdated</u> : 所在组件及其子组件更新完成后调用
- <u>unbind</u> : 只调用一次,指令与元素解绑时调用
Vue.directive('drag', {
bind(el, binding) {
},
inserted(el, binding) {
},
update(el, binding) {
},
componentUpdated(el, binding) {
}
unbind(el, binding) {
},
});
指令钩子函数会被传入以下参数(主要):
- <u>el</u> : 指令所绑定的元素,可以用来直接操作 DOM
- <u>binding</u> : 一个对象,包含以下属性:
- <u>name</u> : 指令名,不包括
v-
前缀 - <u>value</u> : 指令的绑定值(作为表达式解析后的结果)
- <u>expression</u> : 指令绑定的表达式(字符串)
- <u>arg</u> : 传给指令的参数,可选
- <u>modifiers</u> : 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值
- <u>oldValue</u> : 指令绑定的前一个值,仅在 <u>update</u> 和 <u>componentUpdated</u> 钩子中可用,无论值是否改变都可用
- <u>name</u> : 指令名,不包括
组件
组件是可复用的 Vue 实例,且带有一个名字。自定义的组件可以像HTML标签一样使用。
- 示例:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
// 通过 new Vue 创建的 Vue 根实例
new Vue({ el: '#components-demo' })
我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:
<div id="components-demo">
<button-counter></button-counter>
</div>
组件注册
2-1、全局定义
Vue.component('组件名称', {/*组件选项*/})
可以在任意位置(多个不同的 Vue 应用中)使用。
2-2、局部定义
new Vue({
// ...,
components: {
'组件名称': {/*组件选项*/}
}
})
局部组件只能在其定义的组件内使用,不能在其子组件内部使用。
组件的数据
组件内部私有数据——data
组件的 data
必须是函数,且该函数必须返回一个对象作为组件最终的 data
,这样每个实例才可以维护一个独立的被返回对象:
data: function () {
return {
count: 0
}
}
组件外部传入数据——props
如同一个函数一样,函数除了可以定义内部私有变量,有时候为了提高函数的复用性,我们通过会通过参数来接收外部传入的数据。组件内部私有数据存储在 data
中;外部传入的数据,则通过 props
选项接收。
组件内部通过 props
来定义可以接收的数据名称,就相当于是函数的形参。然后,在使用该组件的时候可以通过标签属性的方式进行传参(可配合 v-bind
传入表达式)。
注意事项
- 如果传入的
props
值为一个表达式,则必须使用v-bind
- 组件中的
data
和props
数据都可以通过组件实例进行直接访问 -
data
中的key
与props
中的key
不能冲突
Prop
通过 Prop 向子组件传递数据
- 示例:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
一个组件默认可以拥有任意数量的 prop
,任何值都可以传递给任何 prop
。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data
中的值一样。一个 prop
被注册之后,你就可以像这样把数据作为一个自定义 attribute
传递进来:
<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>
Prop 验证
组件的 props
就是组件的参数,为了确保传入的数据在可控的合理范围内,我们需要对传入的 props
的值类型进行必要的验证。
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取,避免默认值的引用
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
// validator 规定的名称
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
// return value > 10 && value < 100;
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
type 可以是下列原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
组件生命周期
组件生命周期指的是组件从创建到销毁的过程,在这个过程中的一些不同的阶段,vue
会调用指定的一些组件方法。
基本生命周期函数有下面几个阶段,每一个阶段都对应着 之前
和 之后
两个函数:
阶段 | 函数 | 介绍 |
---|---|---|
- 创建阶段 | beforeCreate | 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。初始化阶段,应用不多。 |
created | 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用。 |
|
- 挂载阶段 | beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用。 |
mounted | 该阶段执行完了模板解析,以及挂载。同时组件根组件元素被赋给了 $el 属性,该阶段可以通过 <u>DOM</u> 操作来对组件内部元素进行处理了。 |
|
- 更新阶段 | beforeUpdate | 数据更新时调用,但是还没有对视图进行重新渲染,这个时候,可以获取视图更新之前的状态。 |
updated | 由于数据的变更导致的视图重新渲染,可以通过 <u>DOM</u> 操作来获取视图的最新状态。 | |
- 卸载阶段 | beforeDestroy | 实例销毁之前调用,移除一些不必要的冗余数据,比如定时器。 |
destroyed | Vue 实例销毁后调用。 | |
- 其它 | .$nextTick | 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。 |
errorCaptured | 当捕获一个来自子孙组件的错误时被调用,此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。 |