前言:
- 一、父传子 defineProps
- 二、子传父 defineEmits
- 三、子组件暴露属性和方法给父组件 defineExpose
- 四、依赖注入Provide / Inject
在 <script setup>
中必须使用 defineProps
和 defineEmits
API 来声明 props
和 emits
,它们具备完整的类型推断并且在 <script setup>
中是直接可用的。
<template> <!-- 在模板中直接使用 props 中声明的变量 --> <h1>{{ msg }}</h1> <div>{{ title }}</div>
</template><script setup>
const props = defineProps({ msg: String, title: { type: String, default: '我是标题' }, list: { type: Array, default: () => [] }
});
console.log(props.msg); //使用props中的属性const emits = defineEmits(['change', 'delete']);
</script>
1. defineProps 和 defineEmits 、defineExpose 都是只能在 <script setup> 中才能使用,他们不需要被导入即可使用,并且会在编译 <script setup> 语法块时一同被编译。
2. defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的。
defineProps 是Vue3的写法并且是一个仅在 <script setup> 中可使用的编译命令,并不需要显式地导入;在Vue3的非语法糖setup和在Vue2中的写法是 props 。
注意:defineProps() 中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。 defineProps 只能在 setup 中使用,且只能在 setup 的顶层使用,不能在局部作用域使用。 和 vue2 一样,defineProps 里的属性只读,不能修改。
在子组件中可以使用defineProps声明需要接收父组件的哪些props,它需要定义一个包含props字段的对象,每个字段定义默认值和类型等信息。当父组件的props发生变化时,子组件也会随之响应。
defineProps支持的主要类型有:
- String
- Number
- Boolean
- Object
- Array
- Function
同时也支持许多高级类型,比如,枚举类型,对象类型,联合类型等等。
我们可以对props进行验证,确保传入的值符合我们期望的值。
- type:定义数据的类型
- required:是否必须
- default:默认值
- validator:自定义验证
const props = defineProps({message: {type: String,default: ''},count: {type: Number,required: true,default: 0,validator: (value) => {return value >= 0 && value <= 10}}type:{type: String,validator: (value) => {return ['success', 'warning', 'danger', 'info'].includes(value)}}, data:{type: [Array, Object],default: () => {return { name: 'jack', age: 20 }}}})
实例:父组件传值给子组件
1、定义子组件
<template>我是子组件<p>子组件得到的name:{{ props.name }}</p><p>子组件得到的age:{{ props.age }}</p><!--可以省略前面的props直接写属性名---><p>子组件得到的name:{{ name }}</p><p>子组件得到的age:{{ age }}</p>
</template><script setup>
//方式1: 以对象的形式去接收
const props = defineProps({name: {type: String,default: "张三",},age: {type: Number,default: 22}
});// props.age = 18 // 报错,注意 defineProps 中的属性只读不能修改//方式2: 以数组的方式去接收
//const childProps = defineProps(['name', 'age']);
</script>
2、定义父组件
<template>name: {{ name }} <br />age: {{ age }} <br /><Child :name="name" :age="age" />
</template><script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'const name = ref('LiuQing');
const age = ref(18);
</script>
defineEmits 和 defineProps 一样也是Vue3的写法并且仅用于 <script setup> 中,并且不需要导入;在Vue3的非语法糖setup和在Vue2中的写法是 emits 。
defineEmits 的不同点在于,组件要触发的事件可以显式地通过 defineEmits() 来声明。
defineEmits 用于子组件向父组件传递消息,在父组件中,只需要监听子组件的自定义事件,然后执行相应的逻辑即可。
注意:如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。
实例:子组件向父组件传值
1、定义子组件
// 子组件 child.vue
<template><button @click="handelClick">传递给父级</button><button @click="add">加</button><button @click="decrease">减</button>
</template><script setup>
const emits = defineEmits(['clickFn', 'add', 'decrease'])// 定义一个或多个自定义事件
// 触发emits事件
const handelClick = () => {emits('clickFn', { name: '张三', age: 18, id: 1 }) // 第一个参数为自定义事件名 第二个参数为要传递的数据
}
const add = () => {emits('add', 10) // 第一个参数为自定义事件名 第二个参数为要传递的数据
}
const decrease = () => {emits('decrease', 3) // 第一个参数为自定义事件名 第二个参数为要传递的数据
}
</script>
2、定义父组件
// 父组件 parent.vue
<template><h3>年龄:{{ age }}</h3><child :name="name" @clickFn="updateInfo" />
</template><script setup>
import { ref } from 'vue'
import child from './components/child.vue'const name = ref('李四');
const age = ref(10);const updateInfo = (obj) => {console.log(obj) // { name: '张三', age: 18, id: 1 }name.value = obj.name;age.value = obj.age;
}
</script>
defineExpose 是Vue3中的一个新API,它允许子组件暴露其内部属性或方法给父组件访问。可以通过将属性或方法添加到defineExpose
函数中来实现。
获取用setup语法糖创建的子组件实例时,获取的实例是没有子组件自定义的属性和方法的,此时我们需要通过defineExpose
来暴露子组件的属性和方法。
在父组件中,我们使用ref
属性引用了子组件。需要注意的是,defineExpose
函数必须在setup
函数中调用,否则会报错。
1、定义子组件
<template><p>子组件test</p>
</template><script setup>
import { ref } from 'vue'const msg = ref('Hello Vue3')const a = () => {console.log(1)
}const handleChangeMsg = (v) => { msg.value = v
}defineExpose({msg, a, handleChangeMsg
})
</script>
2、定义父组件
<template><Test ref="testInstanceRef" /><button @click="handleChangeMsg">handleChangeMsg</button>
</template><script setup>
import { ref, onMounted } from "vue";
import Test from "./components/Test.vue";const testInstanceRef = ref();onMounted(() => {const testInstance = testInstanceRef.value;console.log(testInstance.$el) // p标签console.log(testInstance.msg) // msg属性console.log(testInstance.a) // a方法,如果不使用defineExpose暴露是拿不到的
})const handleChangeMsg = () => { testInstanceRef.value.handleChangeMsg('Hello TS')
}
</script>