Skip to content

组件基础

每当你使用一个组件,就创建了一个新的实例

vue
<h1>Here is a child component!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />

在单文件组件中,推荐为子组件使用 PascalCase 的标签名,以此来和原生的 HTML 元素作区分。虽然原生 HTML 标签名是不区分大小写的,但 Vue 单文件组件是可以在编译中区分大小写的。

如果你是直接在 DOM 中书写模板 (例如原生 <template> 元素的内容),模板的编译需要遵从浏览器中 HTML 的解析行为。在这种情况下,你应该需要使用 kebab-case 形式并显式地关闭这些组件的标签。

html
<!-- 如果是在 DOM 中书写该模板 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

传递Props

Props 是一种特别的 attributes,你可以在组件上声明注册。

vue
<script setup>
defineProps(['title'])
</script>

<template>
  <h4>{{ title }}</h4>
</template>

defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象,其中包含了可以传递给组件的所有 props:

ts
const props = defineProps(['title'])
console.log(props.title)

当一个 prop 被注册后,可以像这样以自定义 attribute 的形式传递数据给它:

vue
<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />

监听事件

父组件可以通过 v-on@ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样:

vue
<BlogPost
  ...
  @enlarge-text="postFontSize += 0.1"
 />

子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出一个事件:

BlogPost.vue
vue
<!-- 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

我们可以通过 defineEmits 宏来声明需要抛出的事件:

vue
<script setup>
defineProps(['title'])
defineEmits(['enlarge-text'])
</script>

这声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。同时,这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素。

defineProps 类似,defineEmits 仅可用于 <script setup> 之中,并且不需要导入,它返回一个等同于 $emit 方法的 emit 函数。它可以被用于在组件的 <script setup> 中抛出事件,因为此处无法直接访问 $emit

vue
<script setup>
const emit = defineEmits(['enlarge-text'])

emit('enlarge-text')
</script>

DOM内模板解析

当你直接在 DOM 中写 Vue 模板(不使用 .vue 文件或字符串模板),浏览器会先解析 HTML,再交给 Vue。因为浏览器会对 HTML 标签有自己的解析和修正规则,所以有一些必须注意的限制。

大小写区分

HTML 标签和属性名称是不分大小写的,所以浏览器会把任何大写的字符解释为小写。这意味着当你使用 DOM 内的模板时,无论是 PascalCase 形式的组件名称、camelCase 形式的 prop 名称还是 v-on 的事件名称,都需要转换为相应等价的 kebab-case (短横线连字符) 形式:

ts
// JavaScript 中的 camelCase
const BlogPost = {
  props: ['postTitle'],
  emits: ['updatePost'],
  template: `
    <h3>{{ postTitle }}</h3>
  `
}
vue
<!-- HTML 中的 kebab-case -->
<blog-post post-title="hello!" @update-post="onUpdatePost"></blog-post>