1、组件(Component) 介绍
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码,可以帮助你将用户界面拆分成独立和可复用的部分。
每个 Vue 组件都是一个独立的 Vue 实例,具有自己的模板、数据、方法和生命周期钩子,使得组件可以自包含地定义和管理自己的功能和样式。
2、定义一个组件
(1)、单文件组件
当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue
文件中,这被叫做单文件组件 (简称 SFC):
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
(2)、Vue 特定选项 JavaScript 对象
不使用构建步骤(如 webpack 或 Vite)的情况下,使用 Vue 3 的组合式 API 定义一个 Vue 组件。这种定义方式通常用于直接在浏览器中通过 <script>
标签引入 Vue.js 库的场景:
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
// template: '#my-template-element'
}
以下是代码的详细解释:
导入 ref
函数:
import { ref } from 'vue';
这行代码从 Vue 库中导入了 ref
函数。ref
是一个用于创建响应式引用的函数,它接受一个初始值,并返回一个响应式的对象。
定义组件对象:
export default {
setup() {
const count = ref(0);
return { count };
},
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
};
这是一个 Vue 组件的 JavaScript 对象定义。它使用了 Vue 3 推荐的组合式 API。
setup
函数:
setup() {
const count = ref(0);
return { count };
}
setup
函数是组合式 API 的入口点。在这里,我们使用 ref
创建了一个名为 count
的响应式引用,并将其初始值设置为 0
。setup
函数返回一个对象,其中包含了我们想要在模板中使用的响应式数据。在这个例子中,我们返回了 count
。
模板:
template: `
<button @click="count++">
You clicked me {{ count }} times.
</button>`
组件的 template
属性定义了组件的 HTML 模板。在这个模板中,我们有一个 <button>
元素,当它被点击时,会触发 count++
操作。这表示 count
的值将增加 1
。模板中的 {{ count }}
是一个插值表达式,它将显示 count
的当前值。
内联模板的注释:
// 也可以针对一个 DOM 内联模板:
// template: '#my-template-element'
这一行被注释掉了,但它说明了另一种定义模板的方法。你可以通过指定一个已经在 DOM 中存在的元素的 ID 来使用内联模板。
3、使用组件
要使用一个子组件,我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 ButtonCounter.vue
的文件中,这个组件将会以默认导出的形式被暴露给外部。
ButtonCounter.vue 组件内容如下:
使用方法如下:
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here are many child components!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />
</template>
效果如下:
-
通过
<script setup>
,导入的组件都在模板中直接可用。 -
组件可以被重用任意多次
-
每当你使用一个组件,就创建了一个新的实例
-
在单文件组件中,推荐为子组件使用
PascalCase
的标签名,以此来和原生的 HTML 元素作区分。虽然原生 HTML 标签名是不区分大小写的,但 Vue 单文件组件是可以在编译中区分大小写的。
4、子组件与父组件通讯 props
(1) 、defineProps()
在 Vue.js 中,props
是一种机制,允许父组件向子组件传递数据。props
是子组件声明的可接收的属性,这些属性由父组件提供,子组件可以通过 props
访问这些传递过来的值。
defineProps
是<script setup>
中的一个宏,用于定义组件的props
。它接受一个字符串数组,数组中的每个字符串都是一个prop
的名称。
子组件中 使用 defineProps 声明 props 属性:
<script setup>
import { ref } from 'vue'
const count = ref(0)
defineProps(['message'])
</script>
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
在这个子组件中,我们使用 defineProps
函数来声明 props
。这个函数是 <script setup>
语法的一部分,用于定义组件的 props
。
父组件中 传参:
<script setup>
import ButtonCounter from './ButtonCounter.vue'
import { ref } from 'vue';
const parentMessage = ref('Hello from parent!');
</script>
<template>
<ButtonCounter :message="parentMessage" />
</template>
在这个父组件中,我们导入了子组件,并使用 ref
函数创建了一个响应式的 parentMessage
变量。然后,我们通过 :
前缀将 parentMessage
作为 prop
传递给子组件。
效果如下:
(2) 、defineEmits()
在 Vue 3 中,defineEmits
是一个用于在组件的 setup
函数中定义 emits 选项的宏。它允许你指定可以从组件触发的事件及其相应的参数。
定义 emits 选项: 在 <script setup>
中,使用 defineEmits
来定义组件可以触发的事件。
<!-- ChildComponent.vue -->
<script setup>
import { defineEmits } from 'vue';
// 定义组件可以触发的事件
const emit = defineEmits(['custom-event']);
function handleClick() {
emit('custom-event', { key: 'abc',value:'325r35' });
}
</script>
<template>
<button @click="handleClick">
Click me to trigger custom event
</button>
</template>
父组件中处理子组件事件:
<!-- ParentComponent.vue -->
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const eventPayload = ref(null);
// 定义处理子组件事件的方法
function handleCustomEvent(payload) {
eventPayload.value = payload;
}
</script>
<template>
<div>
<ChildComponent
@custom-event="handleCustomEvent"
/>
<p>Last event payload: {{ eventPayload }}</p>
</div>
</template>
实现效果如下,当点击按钮时,触发事件:
注意事项
defineEmits
只能在<script setup>
中使用。- 定义的事件名应与模板中使用的
v-on
或@
指令的事件名一致。 - 使用
defineEmits
定义的事件可以在模板中通过v-on
或@
指令监听,并在父组件中处理。
(3)、defineModel()
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model
来使用。
在底层,这个宏声明了一个 model prop 和一个相应的值更新事件。
-
如果第一个参数是一个字符串字面量,它将被用作 prop 名称
-
否则,prop 名称将默认为
"modelValue"
在这两种情况下,都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。
以下是使用 defineModel
演示子组件和父组件的使用示例:
首先,创建一个子组件 ChildComponent
:
<script setup>
const msg = defineModel('msg', { type: String, default: ''});
const score = defineModel('score', { type: Number, default: 0 });
/**
* 更新score(同步更新父元素中的数据)
*/
const onClick = () => {
score.value +=1;
};
</script>
<template>
<div style="margin: 10px;padding: 8px;background-color: azure;">
<span>子组件</span>
<div>
<input v-model="msg" />
</div>
<span>dubble score: {{score * 2}}</span>
<br />
<button @click="onClick">score + 1</button>
</div>
</template>
这里使用了 defineModel
宏来定义两个模型,msg
和 score
。这些模型在内部创建了对应的 prop
和 emit
事件,用于实现双向绑定。msg
是一个字符串类型的 prop
,默认值为空字符串,score
是一个数字类型的 prop
,默认值为 0。
defineModel
是 Vue 3 中的一个 API,用于在<script setup>
中定义具有双向绑定能力的props
。v-model
指令用于创建表单输入元素和应用状态之间的双向绑定。@click
指令用于监听点击事件,并在事件发生时执行指定的函数。
然后,创建一个父组件:
<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from 'vue';
const msg = ref('test');
const score = ref(1);
</script>
<template>
<div style="border: 1px solid goldenrod">
<span>父组件</span>
<h1>{{ msg }}</h1>
<h2>origin-score: {{score}}</h2>
</div>
<ChildComponent
v-model:msg="msg"
v-model:score="score"
/>
</template>
v-model:msg
和v-model:score
是v-model
指令的自定义版本,用于创建子组件的msg
和score
属性与父组件的msg
和score
数据的双向绑定。这意味着当子组件更改这些值时,父组件的相应数据也会更新,反之亦然。- 这种双向绑定是通过子组件内部使用
defineModel
或defineProps
和emit
实现的。子组件需要触发相应的更新事件(如update:msg
和update:score
),以便父组件可以响应这些变化。 - 由于
<script setup>
语法的简洁性,父组件中不需要export default
,也不需要显式定义setup
函数。
实现效果如下:
当在子组件中输入 或者点击按钮事件,父组件都会同步更新。