Vue3学习笔记:Day7 组件概论

Vue中使用组件来模块化管理代码,并且实现了代码复用,并且通过组件向外暴露的接口控制组件的行为

组件实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建一个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应用的component方法创建组件,此处使用的是字符串模板,将组件的HTML以字符串的形式写在组件配置中,在调用时只需要使用组件的标签就可以实现组件模块中定义的功能

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

并且组件是可以复用的,可以当作一个模块放到你想使用的地方,因为每次使用一次组件,就会生成一次组件的实例,所以所有相同组件内的数据时相互独立的,是由各个组件独立维护的

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

组件的结构

在Vue应用中的组件是相互嵌套形成树的结构

因此要确定组件的作用域,之前使用的是全局注册的方法注册组件,组件可以在整个应用中使用,还可以使用局部注册的方法,将子组件限制在某个组件内使用

组件的Prop

组件可以自定义属性,在调用组件的时候可以将特定的属性传入,增加组件的复用程度

可以在配置组件的对象中加入props项,这个项的值是一个数组,储存所有的属性

1
2
3
4
5
6
7
8
const app = Vue.createApp({})

app.component('blog-post', {
props: ['title'],
template: `<h4>{{ title }}</h4>`
})

app.mount('#blog-post-demo')

代码中的title即一个属性名,在调用此组件时,可以传入这个属性值

1
2
3
4
5
<div id="blog-post-demo" class="demo">
<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>
</div>

同样可以使用v-bind指令来将变量绑定到自定义组件的属性上

组件的事件

从父组件中调用时,可以绑定组件的任意事件

1
<blog-post ... @enlarge-text="postFontSize += 0.1"></blog-post>

在组件中要接收到这个事件,就需要调用$emit方法,传入事件名称

1
2
3
<button @click="$emit('enlargeText')">
Enlarge text
</button>

中间的过程类似广播,从子组件发送的广播消息的频道是enlargeText,父组件必须监听这个频道才能收到消息

因为在HTML中不区分大消息,所以和JS交互的命名规则都为短横线,在JS中使用时可以切换成驼峰命名

还可以提前将事件填入emits内,这样可以验证他们

1
2
3
4
app.component('blog-post', {
props: ['title'],
emits: ['enlargeText']
})T

组件向上抛值

组件中使用$emit第二个参数来向上抛出一个值

1
2
3
<button @click="$emit('enlargeText', 0.1)">
Enlarge text
</button>

在父组件监听时可以使用$event来访问到这个值或者调用一个方法,而这个值会当作方法的第一个参数

1
<blog-post ... @enlarge-text="postFontSize += $event"></blog-post>

或者

1
<blog-post ... @enlarge-text="onEnlargeText"></blog-post>
1
2
3
4
5
methods: {
onEnlargeText(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}

这个过程就好像原始DOM事件返回信息一样,比如处理点击事件逻辑时可以通过$event来访问到点击事件的一些等等,就好比组件使用广播发送信息,在enlargeText频道上发送信息,发送的信息就是一些参数,父节点切换到对应频道接收信息

组件使用v-model

自定义组件要实现v-model,就需要理解v-model指令的本质

其本质是双向绑定,即表单上的值会更新Vue变量,Vue变量也会更新表单上的值。所以v-model指令可以分为两部分

  • Vue变量可以更新表单,即使用v-bind绑定表单value属性,实现变量更新,value跟随着更新
  • 表单值更新Vue变量,即使用v-on指令接受表单改变事件,将事件中的更改后的表单值赋值给Vue变量可以实现

所以我们要自定义组件实现v-model指令,就需要实现上述两种操作

1
2
3
4
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
1
2
3
4
5
6
7
8
9
10
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
})

model-value绑定的就是searchText变量和自定义组件中input表单的value值,update:model-value事件监听自定义组件中input表单的input事件,并将从事件中传出的值赋值给searchText变量

插槽分发内容

组件的模板中可以使用<slot>插槽标签来占位,在组件调用时,将组件传入的值插入到插槽中

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

即可将Something bad happened.插入到<slot></slot>位置

动态组件

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

使用is属性可以改变此处绑定的组件类型,可以绑定这个属性,用变量来控制

其中currentTabComponent变量可以是一个已经注册了的组件名字,也可以是一个组件的实例对象