【Vue3】父子组件传参(defineEmits,defineProps,defineExpose)

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>

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小秀_heo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值