Vue3学习笔记

学习时间:2024年7月30日

学习来源:Vue官方文档

1 简介

Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。

1.1 单文件组件

在大多数启用了构建工具的 Vue 项目中,我们可以使用一种类似 HTML 格式的文件来书写 Vue 组件,它被称为单文件组件 (也被称为 *.vue 文件,英文 Single-File Components,缩写为 SFC)。顾名思义,Vue 的单文件组件会将一个组件的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。

计数器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

<template>
<button @click="count++">Count is: {{ count }}</button>
</template>

<style scoped>
button {
font-weight: bold;
}
</style>

1.2 API风格

Vue 的组件可以按两种不同的风格书写:选项式 APIOptions API) 和组合式 APIComposition API)。

1.2.1 Options API

使用选项式 API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 datamethodsmounted。选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例。

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
<script>
export default {
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
count: 0
}
},

// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
increment() {
this.count++
}
},

// 生命周期钩子会在组件生命周期的各个不同阶段被调用
// 例如这个函数就会在组件挂载完成后被调用
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>

<template>
<button @click="increment">Count is: {{ count }}</button>
</template>

1.2.2 Composition API

在单文件组件中,组合式 API 通常会与 <script setup> 搭配使用。这个 setup 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup> 中的导入和顶层变量/函数都能够在模板中直接使用。

下面是使用了组合式 API 与 <script setup> 改造后和上面的模板完全一样的组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script setup>
import { ref, onMounted } from 'vue'

// 响应式状态
const count = ref(0)

// 用来修改状态、触发更新的函数
function increment() {
count.value++
}

// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>

<template>
<button @click="increment">Count is: {{ count }}</button>
</template>

核心概念

  1. setup 函数:这是 Composition API 的核心。setup 函数在组件实例创建之前执行,并且作为组件选项传递。它用于初始化组件的状态、定义响应式数据和计算属性、并声明生命周期钩子。
  2. 响应式引用(refs):使用 ref 创建响应式数据。响应式引用是包含 .value 属性的对象,可以用来追踪和更新数据变化。ef() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。
  3. 响应式对象(reactive):使用 reactive 创建一个响应式对象。与 ref 不同,reactive 直接返回一个响应式代理对象,适用于更复杂的嵌套对象。
  4. 计算属性(computed):使用 computed 创建计算属性,它依赖于响应式数据并会在依赖发生变化时自动更新。
  5. 生命周期钩子:Vue 3 提供了一组新的生命周期钩子函数,可以在 setup 函数中调用。

与选项式API的对比

image-20240730203603732

图中色块表示逻辑一致的代码块。可以看到组合式 API允许我们编写更有条理的代码。

生命周期函数对比

Vue2 Options-based API Vue Composition API
beforeCreate setup()
created setup()
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
errorCaptured onErrorCaptured

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

2 基础

以下内容均基于组合式API。

2.1 模板语法

2.1.1 文本插值

最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

1
<span>Message: {{ msg }}</span>

双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

2.1.2 原始HTML

双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,需要使用 v-html 指令:

1
2
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

rawHtml设置为:<span style="color: red">This should be red.</span>时的效果:

1
2
3
Using text interpolation: <span style="color: red">This should be red.</span>

Using v-html directive: This should be red.

2.1.3 v-bind绑定

双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令:

1
<div v-bind:id="dynamicId"></div>

简写:

1
<div :id="dynamicId"></div>

2.2 响应式基础

2.2.1 ref

在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:

1
2
3
import { ref } from 'vue'

const count = ref(0)

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

1
2
3
4
5
6
7
const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

ref会根据初始化时的值推导其类型:

1
2
3
4
5
6
7
import { ref } from 'vue'

// 推导出的类型:Ref<number>
const year = ref(2020)

// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'

其他的示例:

1
2
3
let hMS = ref(null); // null
let titleName = ref(""); // 空字符串
let bigScreenFlightDetail = ref([]); // 数组

要在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { ref } from 'vue'

export default {
// `setup` 是一个特殊的钩子,专门用于组合式 API。
setup() {
const count = ref(0)

// 将 ref 暴露给模板
return {
count
}
}
}
1
2
3
<button @click="count++">
{{ count }}
</button>

2.2.2 <script setup>

① 基本语法

setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用单文件组件 (SFC) 来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script setup>
import { ref } from 'vue'
// 导入
import { capitalize } from './helpers'

// 声明的变量
const count = ref(0)

// 声明的函数
function increment() {
count.value++
}
</script>

<template>
<button @click="increment">
{{ count }}
</button>
<div>{{ capitalize('hello') }}</div>
</template>

<script setup> 中的顶层的导入、声明的变量和函数可在同一组件的模板中直接使用。这是实际开发时最常用的格式。

<script setup> 中的代码会在每次组件实例被创建的时候执行

definePropsdefineEmits

definePropsdefineEmits 是 Vue 3 中 <script setup> 语法的两个核心 Composition API 函数,用于简化组件的 props 和事件声明。使用 TypeScript 时,能提供更好的类型推断。

  • defineProps
1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>{{ message }}</div>
</template>

<script setup>
// 引入 defineProps
const props = defineProps({
message: String
});

// 现在可以直接使用 props.message
</script>

使用类型推断:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>{{ message }}</div>
</template>

<script setup lang="ts">
// 使用 TypeScript 进行类型声明
const props = defineProps<{
message: string; // TypeScript 会推断 props.message 的类型为 string
}>();

// 现在可以直接使用 props.message
</script>
  • defineEmits
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<button @click="handleClick">Click me</button>
</template>

<script setup>
// 1.定义了一个名为 emit 的函数
// 2.类型注解定义了一个名为 click 的事件,并且该事件不接受任何参数(即参数类型为 void),返回值为void
const emit = defineEmits<{
(event: 'click'): void;
}>();


const handleClick = () => {
// 调用emit函数,触发其中的click事件
emit('click'); // TypeScript 会确保 emit 事件名称和参数类型是正确的
}
</script>

名称对应关系:

image-20240731140104011

更复杂的事件示例:

1
2
3
4
const emit = defineEmits<{
(event: 'click'): void; // 无参数,无返回值
(event: 'submit', payload: { username: string; password: string }): void; // 参数为payload的对象;返回值为void
}>();
  • 结合使用
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
<template>
<button @click="handleClick">{{ label }}</button>
</template>

<script setup>
import { ref } from 'vue';

// 定义 props
const props = defineProps({
label: {
type: String,
default: 'Click me'
}
});

// 定义 emit 事件
const emit = defineEmits<{
(event: 'click'): void;
}>();

// 事件处理函数
const handleClick = () => {
emit('click');
};
</script>

TypeScript的类型推断

TypeScript 自动推断出变量、函数返回值、参数等的类型,而无需显式地声明这些类型。这使得 TypeScript 能够在许多情况下提供类型安全,而无需过多的类型注释。

变量初始化推断: 当你给一个变量赋值时,TypeScript 可以根据这个值自动推断出变量的类型。

1
2
let num = 42; // TypeScript 推断 num 的类型为 number
let message = "Hello"; // TypeScript 推断 message 的类型为 string

函数返回值推断: TypeScript 可以根据函数体中的代码自动推断函数的返回类型。

1
2
3
function add(a: number, b: number) {
return a + b; // TypeScript 推断返回值类型为 number
}

参数类型推断: 当你传递参数给一个函数时,TypeScript 会根据参数的类型推断函数的类型。

1
2
3
function greet(name: string) {
return `Hello, ${name}`; // TypeScript 推断 name 的类型为 string
}

泛型推断: 当使用泛型函数或类时,TypeScript 可以根据传递给它的参数推断出泛型的具体类型。

1
2
3
function identity<T>(value: T): T {
return value; // TypeScript 推断 T 的类型为传递给 identity 函数的具体类型
}
defineExpose

在 Vue 3 Composition API 中,defineExpose 是一个函数,用于在组合式组件中暴露内部方法或数据,以便其父组件可以访问这些方法或数据。

示例

考虑一个简单的计数器组件,我们想要在父组件中调用这个计数器组件内部的递增和重置方法。

  • Counter.vue
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
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="reset">Reset</button>
</div>
</template>

<script setup>
import { ref, defineExpose } from 'vue';

const count = ref(0);

const increment = () => {
count.value++;
};

const reset = () => {
count.value = 0;
};

// 将需要暴露给父组件的方法暴露出去
defineExpose({
increment,
reset
});
</script>
  • 父组件
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
<!-- ParentComponent.vue -->
<template>
<div>
<Counter ref="counterRef" />
<button @click="callIncrement">Call Increment</button>
<button @click="callReset">Call Reset</button>
</div>
</template>

<script setup>
import { ref } from 'vue';
import Counter from './Counter.vue';

const counterRef = ref(null);

const callIncrement = () => {
if (counterRef.value) {
counterRef.value.increment();
}
};

const callReset = () => {
if (counterRef.value) {
counterRef.value.reset();
}
};
</script>

2.2.3 DOM更新时机 — nextTick

当修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在nextTick更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。

函数类型:

1
function nextTick(callback?: () => void): Promise<void>
  1. function nextTick:这是一个函数声明,函数名为 nextTick
  2. callback?: () => void:这是一个可选的回调函数参数,类型是 () => void,表示这个回调函数不接受任何参数,也不返回任何值(void)。问号 ? 表示这个参数是可选的。
  3. : Promise<void>:表示这个函数的返回类型是一个 Promise,这个 Promise 不会返回任何值(void)。

示例:

  • 使用回调函数
1
2
3
4
5
6
7
8
9
import { nextTick } from 'vue'

async function increment() {
count.value++
nextTick(() => {
console.log('DOM has been updated');
// 可以在这里安全地操作更新后的 DOM
})
}

在这个示例中,nextTick 接受一个回调函数,该回调函数将在 DOM 更新之后执行

  • 使用Promise
1
2
3
4
5
6
7
8
import { nextTick } from 'vue'

async function increment() {
count.value++
await nextTick(); // 停在这里,等待DOM更新
console.log('DOM has been updated');
// 可以在这里安全地操作更新后的 DOM
}

在这个示例中,nextTick 返回一个 Promise,可以使用 async/await 语法等待这个 Promise,确保在 DOM 更新之后再执行后续代码。

异步相关补充

  • async 关键字

    • 将一个普通函数转换为异步函数。
    • 异步函数返回一个 Promise
    • 允许在异步函数内部使用 await 关键字等待 Promise 完成。
  • await 关键字

    • 只能在 async 函数内部使用
    • 它用于等待一个 Promise 对象的解决(resolved)或拒绝(rejected)。当 await 关键字等待的 Promise 被解决时,它会暂停函数的执行,并返回 Promise 的值。如果 Promise 被拒绝,它会抛出一个异常。

默认情况下,nextTick 接受一个普通的回调函数,在下一个 DOM 更新循环之后执行该回调函数。这时候的回调函数本身不是异步函数。

1
2
3
4
5
import { nextTick } from 'vue';

nextTick(() => {
console.log('This is a normal callback function.');
});

如果希望在 nextTick 的回调函数中执行异步操作,可以将回调函数定义为异步函数。这允许你在回调函数内部使用 await

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
import { nextTick } from 'vue';

export default {
data() {
return {
message: 'Hello'
};
},
methods: {
async fetchData() {
// 模拟异步数据获取
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data fetched');
}, 1000);
});
},
updateMessage() {
this.message = 'Hello, Vue!';

nextTick(async () => {
console.log('DOM has been updated');

// 这里可以安全地操作更新后的 DOM

const data = await this.fetchData();
console.log(data); // 输出 'Data fetched',表明异步操作已完成
});
}
}
};

在这个示例中:

  1. nextTick 的回调函数被定义为 async 函数(异步函数)。
  2. 在回调函数内部,可以使用 await 来等待 fetchData 方法完成。
  3. 这样可以确保在 DOM 更新之后再执行异步操作,并处理其结果。

2.2.4 reactive

还有另一种声明响应式状态的方式,即使用 reactive() API。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:

1
2
3
import { reactive } from 'vue'

const state = reactive({ count: 0 })

在模板中使用:

1
2
3
<button @click="state.count++">
{{ state.count }}
</button>

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<button @click="increment">Count is: {{ state.count }}</button>
<p>{{ state.message }}</p>
</template>

<script>
import { reactive } from 'vue';

export default {
setup() {
const state = reactive({
count: 0,
message: 'Hello Vue!'
});
const increment = () => state.count++;
return { state, increment };
}
}
</script>

2.9 生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

最常用的是 onMountedonUpdatedonUnmounted

生命周期钩子的执行顺序如下:

  1. 创建(Creation)阶段:(setup
    • beforeCreate
    • created
  2. 挂载(Mounting)阶段
    • onBeforeMount:在组件实例被挂载到 DOM 之前调用。
    • onMounted:在组件实例被挂载到 DOM 后调用。可以操作DOM。
  3. 更新(Updating)阶段
    • onBeforeUpdate:在组件数据更新之前调用,适合在 DOM 更新之前执行一些逻辑。
    • onUpdated:在组件数据更新并且 DOM 更新完成后调用。
  4. 卸载(Unmounting)阶段
    • onBeforeUnmount:在组件实例卸载之前调用。
    • onUnmounted:在组件实例卸载之后调用。

组件生命周期图示

2.9.1 onMounted

注册一个回调函数,在组件挂载完成后执行。

1
function onMounted(callback: () => void): void

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
import { ref, onMounted } from 'vue'

const el = ref()

onMounted(() => {
console.log(el.value) // <div>
})
</script>

<template>
<div ref="el"></div>
</template>

2.9.2 onUpdated

注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

1
function onUpdated(callback: () => void): void

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
import { ref, onUpdated } from 'vue'

const count = ref(0)

// 当count更新时调用
onUpdated(() => {
// 文本内容应该与当前的 `count.value` 一致
console.log(document.getElementById('count').textContent)
})
</script>

<template>
<button id="count" @click="count++">{{ count }}</button>
</template>

2.9.3 onUnmounted

注册一个回调函数,在组件实例被卸载之后调用。

1
function onUnmounted(callback: () => void): void

2.11 模板引用

虽然 Vue 的声明性渲染模型为你抽象了大部分对 DOM 的直接操作,但在某些情况下,我们仍然需要直接访问底层 DOM 元素。要实现这一点,我们可以使用特殊的 ref

1
<input ref="input">

它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。这可能很有用,比如说在组件挂载时将焦点设置到一个 input 元素上,或在一个元素上初始化一个第三方库。

2.11.1 访问模板引用

为了通过组合式 API 获得该模板引用,我们需要声明一个匹配模板 ref 属性值的 ref:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup>
import { ref, onMounted } from 'vue'

// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const aaa = ref(null)

onMounted(() => {
aaa.value.focus() // 获得焦点
})
</script>

<template>
<input ref="aaa" />
</template>

注意,只可以在组件挂载后才能访问模板引用。

2.11.2 组件上的ref

模板引用也可以被用在一个子组件上。这种情况下引用中获得的值是组件实例。

示例

  • 父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- ParentComponent.vue -->
<template>
<div>
<ChildComponent ref="childComponent" />
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childComponent = ref(null); // 组件上的引用

const callChildMethod = () => {
if (childComponent.value) { // childComponent.value是子组件的实例
childComponent.value.someMethod();
}
};
</script>
  • 子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- ChildComponent.vue -->
<template>
<div>Child Component</div>
</template>

<script setup>
import { defineExpose } from 'vue';

const someMethod = () => {
console.log('Child method called!');
};

defineExpose({
someMethod
});
</script>

3 组件

3.1 组件基础

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:

组件树

3.1.1 定义组件

当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的.vue文件中,这被叫做单文件组件 (简称 SFC):

1
2
3
4
5
6
7
8
9
10
<!-- ButtonCounter.vue -->
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>

3.1.2 使用组件

要使用一个子组件,我们需要在父组件中导入它。假设我们把计数器组件放在了一个叫做 ButtonCounter.vue 的文件中,这个组件将会以默认导出的形式被暴露给外部。

1
2
3
4
5
6
7
8
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>

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

3.2 props

props(properties)是组件用于接收来自父组件的数据的机制,由子组件声明,子组件接收父组件的数据。它们使得父组件能够将数据传递给子组件,并使子组件能够灵活地复用和处理这些数据。props 可以包含不同类型的数据,例如字符串、数字、数组、对象、函数等。

defineProps详见2.2.2.②

3.2.1 基本用法

1
2
3
4
5
6
7
8
<!-- ParentComponent.vue -->
<template>
<ChildComponent :message="Hello, Vue 3!" :count="5" />
</template>

<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- ChildComponent.vue -->
<template>
<div>
<p>{{ message }}</p>
<p>Count: {{ count }}</p>
</div>
</template>

<script setup>
const props = defineProps({
message: String,
count: Number
});
</script>

3.2.2 要点

  • 通过声明 props 的类型,可以确保父组件传递的数据类型正确。

  • 如果父组件没有传递 props,可以为 props 设置默认值。

  • 可以为 props 声明多种可能的类型。
  • 通过设置 required 属性,可以指定某个 prop 是必需的。
1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
const props = defineProps({
message: {
type: String, // 声明类型
required: true // 必需
},
count: {
type: Number,
default: 0 // 默认值
},
value: [String, Number] // value可以为String或Number
});
</script>

3.2.3 单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。

另外,每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告:

1
2
3
4
const props = defineProps(['foo'])

// ❌ 警告!prop 是只读的!
props.foo = 'bar'

3.3 事件

略。defineEmits详见2.2.2.②节。

这里给出父组件包含一个子组件,并监听子组件发出的事件的示例。

  • 子组件ChildComponent.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<button @click="handleUpdateClick">Update</button>
<button @click="handleSaveClick">Save</button>
</div>
</template>

<script setup lang="ts">
const emit = defineEmits<{
(event: 'update'): void;
(event: 'save', data: { id: number; value: string }): void;
}>();

const handleUpdateClick = () => {
emit('update');
};

const handleSaveClick = () => {
const data = { id: 1, value: 'Hello, Vue 3!' };
emit('save', data);
};
</script>

在这个子组件中,自定义了两个事件:

  1. 'update' 事件不接受任何参数。
  2. 'save' 事件接受一个对象作为参数,包含 idvalue 属性。
  • 父组件ParentComponent.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<h1>Parent Component</h1>
<ChildComponent @update="handleUpdate" @save="handleSave" />
</div>
</template>

<script setup lang="ts">
import ChildComponent from './ChildComponent.vue';

const handleUpdate = () => {
console.log('Update event received from ChildComponent');
};

const handleSave = (data: { id: number; value: string }) => {
console.log('Save event received from ChildComponent with data:', data);
};
</script>

在这个父组件中,我们引入了子组件 ChildComponent,并为其定义的事件添加了监听处理函数:

  1. @update 事件触发时,调用 handleUpdate 方法。
  2. @save 事件触发时,调用 handleSave 方法,并接收传递的 data 参数。

名称对应关系:

image-20240731142409575