vue3-组件传值1(Props/defineProps和defineEmits )

301 阅读4分钟

一、接受参数

1、接受父组件参数:Props

  • 子组件使用来接受父组件传递参数
export default {
  props: ['foo'],
  setup(props) {
    // setup() 接收 props 作为第一个参数
    console.log(props.foo)
  }
}

2、接受父组件参数:defineProps 宏

  • 在使用<script setup>的单文件组件中,props 可以使用 defineProps() 宏来声明
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
  • 如果一个 prop 的名字很长,应使用 camelCase 形式命名,但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式
//父
<MyComponent greeting-message="hello" />
//子
defineProps({
  greetingMessage: String
})
  • 所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。
  • 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。若你这么做了,Vue 会在控制台上向你抛出警告:
  • 你想要更改一个 prop 的需求通常来源于以下两种场景
  • 1、prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
const props = defineProps(['initialCounter'])

// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
  • 2、需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:
const props = defineProps(['size'])

// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
  • 3、更改对象 / 数组类型的 props
  • 当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,对 Vue 来说,阻止这种更改需要付出的代价异常昂贵。你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合。
  • 4、校验
defineProps({
  // 基础类型检查
  // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
  propA: Number,
  // 多种可能的类型
  propB: [String, Number],
  // 必传,且为 String 类型
  propC: {
    type: String,
    required: true
  },
  // 必传但可为 null 的字符串
  propD: {
    type: [String, null],
    required: true
  },
  // Number 类型的默认值
  propE: {
    type: Number,
    default: 100
  },
  // 对象类型的默认值
  propF: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 自定义类型校验函数
  // 在 3.4+ 中完整的 props 作为第二个参数传入
  propG: {
    validator(value, props) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 函数类型的默认值
  propH: {
    type: Function,
    // 不像对象或数组的默认,这不是一个
    // 工厂函数。这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})

二、接受事件和触发事件

1、接受父组件事件:defineEmits 宏

  • defineEmits 仅可用于<script setup> 之中,我们可以通过 defineEmits 宏来声明需要抛出的事件
  • 方式1
<template>
  <div class="blog-post">
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>
<script setup>
defineEmits(['enlarge-text'])
</script>
  • defineEmits并且不需要导入,它返回一个等同于 $emit 方法的 emit 函数
  • 方式2
<script setup>
const emit = defineEmits(['enlarge-text','enlarges'])
emit('enlarge-text');//直接主动触发
</script>

  • 如果你没有在使用 <script setup>,你可以通过 emits 选项定义组件会抛出的事件。你可以从 setup() 函数的第二个参数,即 setup 上下文对象上访问到 emit 函数
  • 方式3
<script>
export default {
  emits: ['enlarge-text'],
  setup(props, ctx) {
    ctx.emit('enlarge-text')
  }
}
</script>

2、接受父组件事件:emits

  • 如果你显式地使用了 setup 函数,则事件需要通过 emits选项来定义,emit 函数也被暴露在 setup() 的上下文对象上
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  }
}

3、触发事件:emit

  • 上面接受的案例

暴露属性和函数给父组件

1、defineExpose

  • 原理:使用 <script setup> 的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。
  • 作用:用于组件通信中父级组件调用操作子组建方法和响应式属性参数能力,即子组件将值和方法传给父组件,父组件通过ref属性获取子组件暴露的。
  • ref会和在普通实例中一样被自动解包,父组件无需使用.value

2、子组件

<script setup>
    import {ref} from "vue";
    function childFn() { }
    let msg = 'Hello World';
    //暴露想要传递的值或方法
    defineExpose({ 
        msg,
        childFn,
    });
</script>

3、父组件

<Index ref="childeRef"></Index>
<script setup>
    import Index from "./index.vue";
    const childeRef = ref();
	
    onMounted(() => {
      childeRef.value.childFn() // 调用子组件函数
      childeRef.value.msg = 'Hello 1234'() // 修改子组件定义的变量
    })
 
</script>