Vue3基础文档-组件基础

监听子组件事件

功能:点击按钮,放大字体。

办法:父组件往子组件定义回调函数,子组件定义触发方法,并在 emits 添加已抛出的事件。

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
<template>
<div id="blog-posts">
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
@enlarge-text="postFontSize += 0.1"
></BlogPost>
</div>
</div>
</template>

<script>
import BlogPost from './BlogPost.vue'

export default {
data() {
return {
posts: [
{ id: 1, title: "My journey with Vue" },
{ id: 2, title: "Blogging with Vue" },
{ id: 3, title: "Why Vue is so fun" },
],
postFontSize: 1,
};
},
components:{
BlogPost
}
};
</script>

// 子组件
<template>
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlarge-text')">Enlarge text</button>
</div>
</template>

<script>
export default {
props: ["title"],
emits: ['enlarge-text']
};
</script>

因为 click 事件是原生事件,可以直接在子组件监听点击事件,并且不需要在 emits 添加事件名。因为 对于子组件中被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了 inheritAttrs: false)。

1
2
3
4
5
6
7
8
9
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
@click="postFontSize += 0.1"
></BlogPost>

<button>Enlarge text</button>
// emits: ['enlarge-text']

使用事件抛出一个值

有的时候用一个事件来抛出一个特定的值是非常有用的。例如我们可能想让 <blog-post> 组件决定它的文本要放大多少。这时可以使用 $emit 的第二个参数来提供这个值:

1
2
3
<button @click="$emit('enlarge-text', 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
}
}

在组件上使用 v-model

首先需要了解

1
2
3
4
5
6
7
8
9
10
11
12
<custom-input v-model="searchText"></custom-input> ===等价于===》 
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>

// 子组件里的
<input v-model="value"> ===等价于==》
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
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
<template>
<div class="custom">
{{ searchText }}
<CustomInput v-model="searchText"></CustomInput>
</div>
</template>

<script>
import CustomInput from "./CustomInput.vue";

export default {
data() {
return {
searchText: "",
};
},
components: {
CustomInput,
},
};
</script>

// 子组件
<template>
<div class="custom-input">
<input v-model="value" />
</div>
</template>

<script>
export default {
props: ["modelValue"],
emits: ["update:modelValue"],
computed: {
value: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
},
},
},
};
</script>

解析 DOM 模板时的注意事项

有些 HTML 元素,诸如 <ul><ol><table><select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li><tr><option>,只能出现在其它某些特定的元素内部。

这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:

1
2
3
<table>
<blog-post-row></blog-post-row>
</table>

这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 v-is attribute 给了我们一个变通的办法:

1
2
3
<table>
<tr v-is="'blog-post-row'"></tr>
</table>

WARNING

v-is 值应为 JavaScript 字符串文本:

1
2
3
4
5
<!-- 错误的,这样不会渲染任何东西 -->
<tr v-is="blog-post-row"></tr>

<!-- 正确的 -->
<tr v-is="'blog-post-row'"></tr>