props defineProps
-
父组件使用
v-bind或缩写:动态绑定的props,子组件通过props接收父组件传递的数据 -
Props是一种特别的
attributes(属性),你可以在组件上声明注册,这里要用到**defineProps** 宏 -
defineProps是一个仅<script setup>中可用的编译 宏命令,并不需要显示地导入。声明的props会自动暴露给模板。defineProps会返回一个对象,其中包含了可以传递给组件的所有props:<script setup> // defineProps(['title']) const props = defineProps(['title']) console.log(props.title) </script> <template> <h4>{{ title }}</h4> </template> -
如果没有使用
<script setup>,props必须以props选项的方式声明,props对象会作为setup()函数的第一个参数被传入:export default { props: ['title'], setup(props) { console.log(props.title) } } -
父子通信例子:
父组件Parent.vue
<script setup>
// 引入 ref 函数,用于定义响应式数据
import { ref } from 'vue';
// 引入子组件 Child.vue
import Child from './Child.vue';
// 使用 ref 函数创建了一个响应式的变量 count,初始值为 0,该变量将用于传递给子组件
let count = ref(0)
</script>
<template>
<div id="parent">
<!-- 将 count 变量传递给子组件 Child -->
<Child :count="count"/>
</div>
</template>
子组件Child.vue
<script setup>
// 使用 defineProps 函数定义Props的类型和默认值 不需要引入 直接使用即可
const props = defineProps({
// 变量 count 是通过父组件传递过来的
count: {
type: Number,
default: 0
}
})
</script>
<template>
<div id="child">
<!-- 在模板中可以直接使用该值 -->
<h1>count: {{ count }}</h1>
</div>
</template>
- 在
defineProps函数的参数中,我们可以传入一个对象,其每一个属性代表一个props - 在以上代码中,定义了一个名为count的props,类型为Number,默认值为0
- 在template中,可以直接使用count变量,它是由父组件传递过来的
$emit defineEmits
-
子组件的模板表达式中,可以直接使用
$emit方法触发自定义事件(例如:在v-on(简写为@)的处理函数中),还可以提供额外的参数父组件可以通过
v-on(简写为@)来监听事件,可以简单写一个内联的箭头函数作为监听器;也可以用一个组件方法来作为事件处理函数:// 子组件 MyComponent 中 <button @click="$emit('increaseBy', 1)">click me</button> // 父组件使用 <MyButton @increase-by="(n) => count += n" /> <MyButton @increase-by="increaseCount" /> function increaseCount(n) { count.value += n }
TIP:和原生DOM事件不一样,组件触发的事件没有冒泡机制,你只能监听直接子组件触发的事件。平级组件或是跨越多层嵌套的组件间通信,应使用一个外部的事件总线,或是使用一个全局状态管理方案
-
组件可以显示地通过
defineEmits()宏来声明它要触发的事件,$emit方法不能在<script setup>中使用,但defineEmits()会返回一个相同作用的函数供我们使用:<script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() { emit('submit') } </script>defineEmits()宏不能在子函数中使用。如上所示,它必须直接放置在
<script setup>的顶级作用域下 -
如果显示地使用了
setup函数而不是<script setup>,则事件需要通过emits选项来定义,emit函数也被暴露在setup()的上下文对象上:export default { emits: ['inFocus', 'submit'], setup(props, ctx) { ctx.emit('submit') } } // 与setup()上下文对象中的其他属性一样,emit可以安全地被解构 export default { emits: ['inFocus', 'submit'], setup(props, { emit }) { emit('submit') } } -
父子通信例子:
子组件Child.vue
<script setup>
const props = defineProps({
count: {
type: Number,
default: 0
}
})
// 使用 defineEmit函数定义了一个名
const emit = defineEmit(['onChildChange'])
const changeParentCount= () => {
emit('onChildChange', 5)
}
</script>
<template>
<div id="child">
<h1>count: {{ count }}</h1>
<button @click="changeParentCount">更新父组件的count</button>
</div>
</template>
- 在 template 的 button 中使用 @click="changeParentCount" 添加点击事件监听器,当按钮被点击时,将调用 changeParentCount 方法,触发父组件中的自定义事件
- 然后使用 defineEmits 函数定义了一个名为 changeParentCount 的自定义事件。然后通过 emit 方法触发名为 changeParentCount 的自定义事件,并将参数 5 传递给父组件
父组件Parent.vue
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
let count = ref(0)
// 这个方法用于处理子组件中触发的自定义事件 changeParentCount,并更新父组件中的 count 变量的值。
const parentChanged= (params) => {
count.value += params
}
</script>
<template>
<div id="parent">
<!-- 监听子组件自定义事件 changeParentCount -->
<Child :count="count" @onChildChange="parentChanged"/>
</div>
</template>
- 在template中,通过v-on(@)监听子组件自定义事件changeParentCount,并在父组件中执行名为parentChanged的方法,它接收一个名为params的参数,然后更新父组件中的count变量的值
ref和defineExpose
- 在Vue3中,
ref函数除了可以用于定义一个响应式的变量或引用之外,还可以获取DOM组件实例 defineExpose是用于将组件内部的属性和方法暴露给父组件或其他组件使用。通过这种方法,可以定义哪些部分可以从组件的外部访问和调用- 例子如下:
子组件Child.vue
<script setup>
import { ref } from 'vue';
const msg = ref('我是子组件中的数据');
const childMethod = () => {
console.log('我是子组件中的方法');
}
// defineExpose 对外暴露组件内部的属性和方法,不需要引入,直接使用
// 将属性 msg 和方法 childMethod 暴露给父组件
defineExpose({
msg,
childMethod
})
</script>
父组件Parent.vue
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
// 获取子组件DOM实例
const childRef = ref();
// 该方法用于获取子组件对外暴露的属性和方法
const getChildPropertyAndMethod = () => {
// 获取子组件对外暴露的属性
console.log(childRef.value.msg);
// 调用子组件对外暴露的方法
childRef.value.childMethod();
}
</script>
<template>
<div id="parent">
<Child ref="childRef"/>
<button @click="getChildPropertyAndMethod">获取子组件对外暴露的属性和方法</button>
</div>
</template>
provide和inject
- ’prop逐级透传‘:组件不关心这些props,但是为了使能访问到它们,仍然需要定义并向下传递,如果组件链路非常长,可能会影响到更多这条路上的组件
provide和inject可以帮助我们解决这一问题,一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级多深,都可以注入由父组件提供给整条链路的依赖
Provide(提供)
provied()函数接收两个参数:
- 注入名(可以是一个字符串或是一个Symbol),后代组件会用注入名来查找期望注入的值,一个组件可以多次调用该函数,使用不同的注入名,注入不同的依赖值
- 值(可以是响应式对象、响应式的ref、reactive对象、函数等)
<script setup>
import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>
Inject(注入)
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
// 如果注入名没有任何组件提供,应该声明一个默认值
// `value` 会是 "这是默认值"
const value = inject('message', '这是默认值')
- 父子通信例子:
父组件Parent.vue
<script setup>
// 引入 provide,用于提供数据给所有子组件
import { ref, provide } from 'vue';
// 引入子组件1和子组件2
import Child1 from './Child1.vue';
import Child2 from './Child2.vue';
const message = ref('我是父组件的数据')
// 使用 provide 将数据 message 提供给所有子组件
provide('message', message)
</script>
<template>
<div id="parent">
<Child1 />
<Child2 />
</div>
</template>
组件Child1.vue
<script setup>
import { inject } from 'vue';
// 使用 inject 获取来自父组件的数据 message
const parentMessage = inject('message');
</script>
<template>
<div id="child">
<p>子组件1: {{ parentMessage }}</p>
</div>
</template>
组件Child2.vue
<script setup>
import { inject } from 'vue';
// 使用 inject 获取来自父组件的数据 message
const parentMessage = inject('message');
</script>
<template>
<div id="child">
<p>子组件2: {{ parentMessage }}</p>
</div>
</template>
vueX
pinia
mitt 全局bus总线
在Vue3中,可以使用第三方库mitt实现组件之间的通信
mitt是一个简单且强大的事件总线库(类似Vue2中的EventBus),提供了一种方便的方式来在不同组件之间传递事件和数据
如何实现通信
- 安装mitt.js
yarn add mitt
- 创建一个 event bus
// mitt/index.js
import mitt from 'mitt';
const bus = mitt();
export default bus;
- 在需要通信的组件中,导入event bus对象并进行事件的监听和触发
// 组件 First.vue
<script setup>
import mitt from '../mitt';
const emitEvent = () => {
mitt.emit('updateName', 36);
}
</script>
<template>
<div id="first">
<button @click="emitEvent">更新name和age</button>
</div>
</template>
// 组件 Second.vue
<script setup>
import mitt from '../mitt';
import { ref } from 'vue';
let name = ref('Echo');
let age = ref(26);
mitt.on('updateName', (data) => {
name.value = 'Steven';
age.value = data;
});
</script>
<template>
<div id="second">
<p>name: {{ name }}</p>
<p>age: {{ age }}</p>
</div>
</template>
参考文章:
vue官方文档: 组件基础 | Vue.js