父传子(Props)
// Parent.vue
<template>
<!-- 使用子组件 -->
<Child :msg="message" />
</template>
<script setup>
import Child from './components/Child.vue' // 引入子组件
let message = '雷猴'
</script>
// Child.vue
<template>
<div>
{{ msg }}
</div>
</template>
<script setup>
const props = defineProps({
msg: {
type: String,
default: ''
}
})
//另一种写法
// const props = defineProps<{
// msg:String
// }>();
//这一步很关键哦,为了页面响应式,需要toRefs一下
const {msg} = toRefs(props)
</script>
子传父(emits)
子组件通知父组件触发一个事件,并且可以传值给父组件。
// Parent.vue
<template>
<div>父组件:{{ message }}</div>
<!-- 自定义 changeMsg 事件 -->
<Child @changeMsg="changeMessage" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'
let message = ref('雷猴')
// 更改 message 的值,data是从子组件传过来的
function changeMessage(data) {
message.value = data
}
</script>
// Child.vue
<template>
<div>
子组件:<button @click="handleClick">子组件的按钮</button>
</div>
</template>
<script setup>
// 注册一个自定义事件名,向上传递时告诉父组件要触发的事件。
const emit = defineEmits(['changeMsg'])
function handleClick() {
// 参数1:事件名
// 参数2:传给父组件的值
emit('changeMsg', '鲨鱼辣椒')
}
</script>
子传父(expose / ref)
子组件可以通过 expose 暴露自身的方法和数据。
父组件通过 ref 获取到子组件并调用其方法或访问数据。
// Parent.vue
<template>
<div>父组件:拿到子组件的message数据:{{ msg }}</div>
<button @click="callChildFn">调用子组件的方法</button>
<hr>
<Child ref="com" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Child from './components/Child.vue'
const com = ref(null) // 通过 模板ref 绑定子组件
const msg = ref('')
onMounted(() => {
// 在加载完成后,将子组件的 message 赋值给 msg
msg.value = com.value.message
})
function callChildFn() {
// 调用子组件的 changeMessage 方法
com.value.changeMessage('蒜头王八')
// 重新将 子组件的message 赋值给 msg
msg.value = com.value.message
}
</script>
// Child.vue
<template>
<div>子组件:{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('蟑螂恶霸')
function changeMessage(data) {
message.value = data
}
使用 defineExpose 向外暴露指定的数据和方法
defineExpose({
message,
changeMessage
})
</script>
祖孙多层组件通信(provide/inject)
// Parent.vue
<template>
<Child></Child>
</template>
<script setup>
import { ref, provide, readonly } from 'vue'
import Child from './components/Child.vue'
const name = ref('猛虎下山')
const msg = ref('雷猴')
// 使用readonly可以让子组件无法直接修改,需要调用provide往下传的方法来修改
provide('name', readonly(name))
provide('msg', msg)
provide('changeName', (value) => {
name.value = value
})
</script>
// Child.vue
<template>
<div>
<div>msg: {{ msg }}</div>
<div>name: {{name}}</div>
<button @click="handleClick">修改</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
const name = inject('name', 'hello') // 看看有没有值,没值的话就适用默认值(这里默认值是hello)
const msg = inject('msg')
const changeName = inject('changeName')
function handleClick() {
// 这样写不合适,因为vue里推荐使用单向数据流,当父级使用readonly后,这行代码是不会生效的。没使用之前才会生效。
// name.value = '雷猴'
// 正确的方式
changeName('虎躯一震')
// 因为 msg 没被 readonly 过,所以可以直接修改值
msg.value = '世界'
}
</script>