1. 父组件给子组件传值
父组件App.vue
<template>
<div>
父级
</div>
<waterFallVue :title="name"></waterFallVue>
</template>
<script setup lang="ts">
import waterFallVue from './components/waterFall.vue'
let name = 'uzi'
</script>
<style lang="scss" scoped></style>
子组件waterFall.vue
<template>
<div>
子级
</div>
<div>
接收值:{{ title }}
</div>
<div>
接收数组:{{ arr }}
</div>
</template>
<script setup lang="ts">
// 1.仅接收
// const props = defineProps(['title'])
// 2.接收+限制类型(+指定默认值)
//接受父组件传过来的值,没有传入则使用默认值
// const props = defineProps({
// // key 是 prop 的名称, value 是该 prop 预期类型的构造函数
// title: {
// type: String,
// default: '默认值'
// }
// })
// // console.log(title); 直接使用会报错
// console.log(props.title);//使用props接受才可以使用传入的值
// 3.接收+限制类型+指定默认值
//接受父组件传过来的值,没有传入则使用默认值
//ts特有定义默认值用 withDefaults函数,接收definProps和默认值对象,当然不定义默认值可以直接使用defineProps
const props = withDefaults(defineProps<{
title: string,
arr: number[]
}>(),{
// 定义复杂数据类型,需要使用一个函数返回默认值的拷贝,每个实例都会拥有自己的数组引用,以防止多个实例之间共享同一个引用。
// 如果直接将默认值设置为 [666],那么如果有多个组件实例化时,它们将共享同一个 [666] 数组的引用。这意味着,当一个实例修改了这个数组时,其他实例中的该 prop 的值也会被修改。
arr:()=> [666]
})
</script>
<style lang="scss" scoped></style>
2. 子组件给父组件传值
父组件App.vue
<template>
<div>
父级
</div>
<waterFallVue @on-click="getName" :title="name"></waterFallVue>
</template>
<script setup lang="ts">
import waterFallVue from './components/waterFall.vue'
let name = 'zitai'
const getName = (name:string) => {
console.log(name, '==> 子组件传值给父组件');
}
// 总结
/**
* 1.子组件通过defineEmits定义方法数组
* 2.通过函数调用传递该方法和参数
* 3.父组件直接使用该方法并接收参数
*/
</script>
<style lang="scss" scoped></style>
子组件waterFall.vue
<template>
<div>
子级
</div>
<button @click="send">给父组件传值</button>
</template>
<script setup lang="ts">
//不用ts
//给父组件传值 defineEmits
// const emit = defineEmits(['on-click'])
// const send = () => {
// emit('on-click', 'uzi')
// }
//用ts
//给父组件传值 defineEmits,只是改变了一下 defineEmits 的写法
const emit = defineEmits<{
(e: "on-click", name: string): void
}>()
const send = () => {
emit('on-click', 'uzi')
}
</script>
<style lang="scss" scoped></style>
3. 子组件给父组件暴露方法或者属性
父组件App.vue
<template>
<div>
父级
</div>
<waterFallVue ref="waterFall"></waterFallVue>
</template>
<script setup lang="ts">
import { ref,onMounted } from 'vue';
import waterFallVue from './components/waterFall.vue'
const waterFall = ref<InstanceType<typeof waterFallVue>>()
// setup 函数在组件生命周期中是在组件实例创建之前执行的,因此为了避免name和open出现undefined,需将他们挂载到onMounted中
onMounted(() => {
console.log(waterFall.value?.name);
const openFunc = waterFall.value?.open
openFunc()
})
</script>
<style lang="scss" scoped></style>
子组件waterFall.vue
<template>
<div>
子级
</div>
</template>
<script setup lang="ts">
defineExpose({
name:'xiaohu',
open:()=>console.log('暴露方法')
})
</script>
<style lang="scss" scoped></style>
使用场景:element-plus 中 Form 表单组件库中验证表单的方法就是使用了暴露方法
4. 小案例(封装瀑布流组件)
父组件App.vue
<template>
<waterFallVue :list="list"></waterFallVue>
</template>
<script setup lang='ts'>
import waterFallVue from './components/waterFall.vue';
const list = [
{
height: 300,
background: 'red'
},
{
height: 400,
background: 'pink'
},
{
height: 500,
background: 'blue'
},
{
height: 200,
background: 'green'
},
{
height: 300,
background: 'gray'
},
{
height: 400,
background: '#CC00FF'
},
{
height: 200,
background: 'black'
},
{
height: 100,
background: '#996666'
},
{
height: 500,
background: 'skyblue'
},
{
height: 300,
background: '#993366'
},
{
height: 100,
background: '#33FF33'
},
{
height: 400,
background: 'skyblue'
},
{
height: 200,
background: '#6633CC'
},
{
height: 300,
background: '#666699'
},
{
height: 300,
background: '#66CCFF'
},
{
height: 300,
background: 'skyblue'
},
{
height: 200,
background: '#CC3366'
},
{
height: 200,
background: '#CC9966'
},
{
height: 200,
background: '#FF00FF'
},
{
height: 500,
background: '#990000'
},
{
height: 400,
background: 'red'
},
{
height: 100,
background: '#999966'
},
{
height: 200,
background: '#CCCC66'
},
{
height: 300,
background: '#FF33FF'
},
{
height: 400,
background: '#FFFF66'
},
{
height: 200,
background: 'red'
},
{
height: 100,
background: 'skyblue'
},
{
height: 200,
background: '#33CC00'
},
{
height: 300,
background: '#330033'
},
{
height: 100,
background: '#0066CC'
},
{
height: 200,
background: 'skyblue'
},
{
height: 100,
background: '#006666'
},
{
height: 200,
background: 'yellow'
},
{
height: 300,
background: 'yellow'
},
{
height: 100,
background: '#33CCFF'
},
{
height: 400,
background: 'yellow'
},
{
height: 400,
background: 'yellow'
},
{
height: 200,
background: '#33FF00'
},
{
height: 300,
background: 'yellow'
},
{
height: 100,
background: 'green'
}
]
</script>
<style lang='less'>
#app,
html,
body {
height: 100%;
}
* {
padding: 0;
margin: 0;
}
</style>
子组件waterFall.vue
<template>
<div class="wraps">
<div :style="{ height: item.height + 'px', background: item.background, top: item.top + 'px', left: item.left + 'px' }"
v-for="item in waterList" class="items"></div>
</div>
</template>
<script setup lang='ts'>
// 整体思路:1,确定列数,并排满第一列,同时每一列的当前记录到heightList中,此时heightList的长度已经固定,只需要每次都更改高度就可以了;2,第一排排满后,先找出哪一列最短,找到后,根据heightList的下标设置位置,然后再更新此列的高度
import { reactive, onMounted } from 'vue'
const props = defineProps<{
list: any[]
}>()
const waterList = reactive<any[]>([])
const init = () => {
const heightList: any[] = []
const width = 130;
//获取可视区域的距离
const x = document.body.clientWidth
//获取可视区域可以摆放的列数
const column = Math.floor(x / width)
for (let i = 0; i < props.list.length; i++) {
if (i < column) {
props.list[i].top = 10;
props.list[i].left = i * width;
heightList.push(props.list[i].height + 10)
waterList.push(props.list[i])
} else {
let current = heightList[0]
let index = 0;
heightList.forEach((h, inx) => {
if (current > h) {
current = h;
index = inx;
}
})
console.log(current, 'c')
props.list[i].top = (current + 20);
console.log(props.list[i].top, 'top', i)
props.list[i].left = index * width;
heightList[index] = (heightList[index] + props.list[i].height + 20);
waterList.push(props.list[i])
}
}
console.log(props.list)
}
onMounted(() => {
window.onresize = () => init()
init()
})
</script>
<style scoped lang='less'>
.wraps {
position: relative;
height: 100%;
.items {
position: absolute;
width: 120px;
}
}
</style>