组件基础
基本用法
可以像下面一样定义一个Vue组件实例:
// 创建一个Vue 应用
const app = Vue.createApp({})
// 定义一个名为 button-counter 的新全局组件
app.component('button-counter', {
data() {
return {
count: 0
}
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
})
新定义的组件就可以在Vue实例的模板中使用了:
<div id="components-demo">
<button-counter></button-counter>
</div>
通过 Prop 向子组件传递数据
Prop 是可以在组件上注册的一些自定义 attribute:
app.component('blog-post', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})
上面的组件中,title就是一个prop,通过父组件传递而来:
<div id="blog-post-demo" class="demo">
<blog-post title="My journey with Vue"></blog-post>
</div>
title也可以通过v-bind绑定到一个属性上面
<div id="blog-post-demo" class="demo">
<blog-post :title="myTitle"></blog-post>
</div>
...
data() {
return {
myTitle: 'My journey with Vue'
}
}
监听子组件事件
子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件:
<button @click="$emit('enlargeText')">
Enlarge text
</button>
可以在子组件的 emits 选项中列出已抛出的事件:
app.component('blog-post', {
props: ['title'],
emits: ['enlargeText']
})
这样,在父组件中就可以监听这个事件:
<blog-post ... @enlarge-text="onEnlargeText"></blog-post>
在组件上使用 v-model
例如我们要在自定义组件的input标签中使用v-model,为了使其正常工作,必须满足以下两点:
- 将其 value attribute 绑定到一个名叫 modelValue 的 prop 上;
- 在其 input 事件被触发时,将新的值通过自定义的 update:modelValue 事件抛出;
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
})
<custom-input v-model="searchText"></custom-input>
组件注册
全局注册
const app = Vue.createApp({})
app.component('component-a', {
/* ... */
})
app.component('component-b', {
/* ... */
})
app.component('component-c', {
/* ... */
})
app.mount('#app')
通过component方法来创建组件,在注册之后可以用在任何新创建的组件实例的模板中。
在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。
局部注册
const ComponentA = {
/* ... */
}
const ComponentB = {
/* ... */
}
const app = Vue.createApp({
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
通过一个普通的JavaScript对象来定义一个组件,然后在 components 选项中定义这一个组件。对于 components 对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。
组件的Props
Props类型与类型检查
prop可以以字符串数组形式列出:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常是希望每个 prop 都有指定的值类型。这时,可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
}
在传入错误的类型之后,会从浏览器的Javascript控制台提示错误信息。
除此之外,可以为组件的 prop 指定验证要求,如果有一个需求没有被满足,则 Vue 会在浏览器控制台中提出警告:
app.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: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
// 具有默认值的函数
propG: {
type: Function,
// 与对象或数组默认值不同,这不是一个工厂函数 —— 这是一个用作默认值的函数
default: function() {
return 'Default function'
}
}
}
})
type 可以是下列原生构造函数中的一个:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
此外,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。
另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着不应该在一个子组件内部改变 prop。
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
非 Prop 的 Attribute
Attribute 继承
当组件返回单个根节点时,非 prop attribute 将自动添加到根节点的 attribute 中:
app.component('date-picker', {
template: `
<div class="date-picker">
<input type="datetime" />
</div>
`
})
<!-- 具有非prop attribute的Date-picker组件-->
<date-picker data-status="activated"></date-picker>
<!-- 渲染 date-picker 组件 -->
<div class="date-picker" data-status="activated">
<input type="datetime" />
</div>
禁用 Attribute 继承
如果不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false。
通过将 inheritAttrs 选项设置为 false,你可以访问组件的 $attrs property,该 property 包括组件 props 和 emits property 中未包含的所有属性 (例如,class、style、v-on 监听器等)。
多个根节点上的 Attribute 继承
与单个根节点组件不同,具有多个根节点的组件不具有自动 attribute 回退行为。如果未显式绑定 $attrs,将发出运行时警告:
// 这将发出警告
app.component('custom-layout', {
template: `
<header>...</header>
<main>...</main>
<footer>...</footer>
`
})
// 没有警告,$attrs被传递到<main>元素
app.component('custom-layout', {
template: `
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
`
})