1.Props
父传子:直接传递需要获取的属性
子传父:需要借助函数,也就是方法,通过传递函数,子接着入参给函数,父调用函数即可获取到参数。
父:
<template><div class="father"><h3>父组件</h3><h4>汽车:{{ car }}</h4><h4 v-show="toy">儿子给的玩具:{{ toy }}</h4><Child :car="car" :sendToy="getToy" /></div>
</template><script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from 'vue';let car = ref('奔驰')
let toy = ref('')function getToy(value: string) {console.log(value)toy.value = value
}
</script>
子:
<template><div class="child"><h3>子组件</h3><h4>玩具:{{ toy }}</h4><h4>父亲给的汽车:{{ car }}</h4><button @click="sendToy(toy)">给父亲玩具</button></div>
</template><script setup lang="ts" name="Child">import { ref } from 'vue';let toy = ref('奥特曼')defineProps(['car', 'sendToy'])
</script>
2.自定义事件
使用@,声明一个自定义事件,并绑定一个函数,传递给子子组件通过接收该自定义事件,
<!-- 自定义事件 --><Child @send-toy="saveToy" />
子接收自定义时间,并绑定点击事件,通过入参,实现数据的传递
let emit = defineEmits(['send-toy'])
<button @click="emit('send-toy',toy)">传递玩具给父亲</button>
3.mitt(绑定-订阅接收)任意组件消息传递(类似于RabbitMQ)
import mitt from 'mitt'
import { ref } from 'vue'let emitter = mitt()let num = ref(0)// 绑定事件
emitter.on('add', () => {console.log('add被调用了', num)
})
// 每隔1秒执行事件
setInterval(() => {emitter.emit('add')
}, 1000)setTimeout(() => {// 解绑事件emitter.off('add')
}, 5000)export default emitter
接收数据,需要绑定事件,也就是订阅。避免内存溢出,组件卸载之后,解绑事件
let food = ref('')
emitter.on('send-food', (value: string) => {food.value = value
})onUnmounted(() => {emitter.off('send-food')
})
发送数据,需要执行事件
<button @click="emitter.emit('send-food', food)">分享食物给弟弟</button>
4.v-model,一般用于UI库的底层实现
例如el input 输入框,底层编写了输入框组件,并且通过v-model实现数据的双向绑定
5.$attrs
父组件向子组件传递数据,attrs存储的是被defineProps接收的值,可以通过$attrs获取
<Child :car="car" :sendToy="getToy" @send-toy="saveToy" :a="a" :b="b" :updateA="updateA"/>
defineProps(['car', 'sendToy'])
<h4>attrs:a:{{ $attrs.a }}</h4><h4>attrs:b:{{ $attrs.b }}</h4><button @click="$attrs.updateA(6)">修改a</button>
6.$refs,$parent
都需要集合defineExpose,将需要传递的数暴露出去
$refs :子传父
<button @click="addBook($refs)">给孩子买书</button><Child ref="c1" />let c1 = ref()
function updateA(value: number) {a.value += value
}
defineExpose({book})
$parent:父传子
defineExpose({ house })
<button @click="minusHouse($parent)">得到父亲的一套房子</button>function minusHouse(parent:any){console.log(parent)parent.house -=1
}
7.provide(提供数据)、inject(接收数据)
let money = ref(100)
provide('moneyContext',{money,updateMoney})
<h4>父亲的钱:{{ money }}</h4><button @click="updateMoney(6)">花父亲的钱</button>let { money, updateMoney } = inject('moneyContext', { money: 55, updateMoney: (value: number) => { } })
8.插槽slot
8.1 默认插槽
引入子组件,组间标签内编写内容
在子组件,使用<slot>j进行占位
<template><div class="father"><h2>父组件</h2><div class="content"><Child title="今日游戏列表"><ul><li v-for=" (item, index) in games" :key="index">{{ item }}</li></ul></Child><Child title="今日美食推荐"><img :src="imgUrl"></Child><Child title="今日影视推荐"><video :src="videoUrl" controls /></Child></div></div></template><script>
export default {name: 'Father'
}</script>
<script setup>
import Child from './Child.vue';
import { ref, reactive } from 'vue';let games = reactive(['王者荣耀','绝地求生','天天酷跑'
])let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script><style scoped>
.father {background-color: antiquewhite;box-shadow: 0 0 10px;border-radius: 10px;padding: 10px;
}.content {display: flex;justify-content: space-evenly;
}img,
video {width: 90%;
}
</style>
<template><div class="child"><h2>{{ title }}</h2><slot></slot></div>
</template><script>
export default {name: 'Child'
}
</script><script setup>
defineProps(['title'])
</script><style scoped>
.child {background-color: aquamarine;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;width: 200px;height: 300px;
}h2 {background-color: yellow;text-align: center;}
</style>
8.2 具名插槽:使用多个插槽,填充不同的内容
数据在父组件,结构也在父组件,填充的位置由子组件定义。
<template><div class="father"><h2>父组件</h2><div class="content"><Child><template v-slot:s1><h2>今日游戏列表</h2></template><template v-slot:s2><ul><li v-for=" (item, index) in games" :key="index">{{ item }}</li></ul></template></Child><Child><template v-slot:s1><h2>今日美食推荐</h2></template><template v-slot:s2><img :src="imgUrl"></template></Child><Child><template #s1><h2>今日影视推荐</h2></template><template #s2><video :src="videoUrl" controls /></template></Child></div></div></template><script>
export default {name: 'Father'
}</script>
<script setup>
import Child from './Child.vue';
import { ref, reactive } from 'vue';let games = reactive(['王者荣耀','绝地求生','天天酷跑'
])let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script><style scoped>
h2 {background-color: yellow;text-align: center;}.father {background-color: antiquewhite;box-shadow: 0 0 10px;border-radius: 10px;padding: 10px;
}.content {display: flex;justify-content: space-evenly;
}img,
video {width: 90%;
}
</style>
<template><div class="child"><slot name="s1">默认内容1</slot><slot name="s2">默认内容2</slot></div>
</template><script>
export default {name: 'Child'
}
</script><script setup>
</script><style scoped>
.child {background-color: aquamarine;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;width: 200px;height: 300px;
}
</style>
8.3 作用域插槽
数据在子组件,结构在父组件,需要传递数据给父组件
<template><div class="father"><h2>父组件</h2><div class="content"><Child1><template v-slot:s1><h2>今日游戏列表</h2></template><template v-slot="params"><span>{{ params }}</span><ul><li v-for="item in params.youxi" :key="item.id">{{ item.name }}</li></ul></template></Child1><Child1><template v-slot:s1><h2>今日游戏列表</h2></template><template #default="params"><span>{{ params }}</span><ol><li v-for="item in params.youxi" :key="item.id">{{ item.name }}</li></ol></template></Child1></div></div></template><script>
export default {name: 'Father'
}</script>
<script setup>
import { ref, reactive } from 'vue';
import Child1 from './Child1.vue';let games = reactive(['王者荣耀','绝地求生','天天酷跑'
])let imgUrl = ref('http://47.113.201.132:9001/api/v1/download-shared-object/aHR0cDovLzEyNy4wLjAuMTo5MDAwL215bGlmZS8yMDI0LzA4LzE2L2NkNjY3YWY2YzAxMzRiNmZhZGZhMTMwZjk4NGQ0MGUxLmpwZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPVNVTEdOWTY3RVpFMFoyVVZNVTFUJTJGMjAyNDA4MzAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwODMwVDAxNTEzNVomWC1BbXotRXhwaXJlcz00MzE5NiZYLUFtei1TZWN1cml0eS1Ub2tlbj1leUpoYkdjaU9pSklVelV4TWlJc0luUjVjQ0k2SWtwWFZDSjkuZXlKaFkyTmxjM05MWlhraU9pSlRWVXhIVGxrMk4wVmFSVEJhTWxWV1RWVXhWQ0lzSW1WNGNDSTZNVGN5TlRBeU5UZ3lOQ3dpY0dGeVpXNTBJam9pY205dmRDSjkuOElYdU9CR2w3V2p5QWR5LUpoSVRMQml5dmJTRmhHM3lLNmZpUWE3RS1aaHQzV3o3TkdsMzdGUjlTal90aXdLNk4ySmxtcUhRNmtSQmxtODVzajNUa2cmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnZlcnNpb25JZD1udWxsJlgtQW16LVNpZ25hdHVyZT0xNjE5NmViYzcwZTM2MDkxNDQ4Y2ZmMmMzYTc3ODY4NzZmZTllNDhmYTZkMGQ5Y2JkMmU4NzE2NmM3MDQ4YWQw')let videoUrl = ref('http://vjs.zencdn.net/v/oceans.mp4')
</script><style scoped>
h2 {background-color: yellow;text-align: center;}.father {background-color: antiquewhite;box-shadow: 0 0 10px;border-radius: 10px;padding: 10px;
}.content {display: flex;justify-content: space-evenly;
}img,
video {width: 90%;
}
</style>
<template><div class="child1"><slot name="s1"></slot><slot :youxi="games"></slot></div></template><script lang="ts" setup name="Child1">
import { reactive } from 'vue';let games = reactive([{id: 111,name: "王者荣耀"},{id: 2222,name: "QQ飞车"},{id: 333,name: "斗地主"}
])
</script><style scoped>
.child1 {background-color: aquamarine;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;width: 200px;height: 300px;
}
</style>