Vue3父子组件传属性和方法调用Demo
- 说明
- 目录
- 父组件给子组件传值和方法
- 父组件给子组件传值-使用defineProps接受父组件属性值
- 父组件给子组件传值-使用defineModel接受父组件v-model值
- 当子组件只需要接收父组件一个v-model值时,写法1如下:
- 子组件接收单个v-model写法2如下:
- 当子组件需要接收父组件多个v-model值时,写法如下:
- 父组件给子组件传方法
- 子组件调用父组件方法-使用defineEmits调用父组件方法
- 子组件暴露属性和方法给父组件调用-使用defineExpose暴露子组件属性和方法
说明
这里记录下自己学习Vue3父子组件怎么传值和方法怎么互相调用,防止后面继续踩坑且方便以后直接使用。这里承接自己的博客Vue3+vite优化基础架构(3)— 优化vue-i18n国际化配置这篇博客,在该博客项目的基础上学习Vue3父子组件传值及使用。
官方文档:https://cn.vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
目录
这里父组件是test-management1文件夹下的index.vue,子组件是test-management1文件夹下的components文件夹的test-child.vue。
父组件给子组件传值和方法
父组件给子组件传属性值,有2种方式:
- 第一种方式是在子组件里面使用defineProps来接受父组件那边传过来的属性值。
- 第二种方式是在子组件里面使用defineModel来接受父组件那边传过来的v-model值。
父组件给子组件传值-使用defineProps接受父组件属性值
父组件代码如下:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--父组件向子组件传参,参数为name,age,isOpenEmail,content,score这5个-->
<TestChild
:name="testName"
:age="testAge"
:isOpenEmail="testIsOpenEmail"
:content="testContent"
:score="testScore"
/>
</div>
</template>
<script setup name="test-management1">
import {ref} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//定义name值
const testName=ref('张三')
//定义age值
const testAge=ref(18)
//定义isOpenEmail开通邮箱值
const testIsOpenEmail=ref(false)
//定义content值
const testContent=ref(['测试值1','测试值2','测试值3'])
//定义score值
const testScore=ref({
curriculum:"语文",
score:60
})
</script>
<style scoped>
</style>
子组件代码如下:
<template>
<!--显示父组件传过来的参数值-->
<div>
<!--在页面模版里面如果要使用父组件里面穿过来的参数,有2种写法,都能在页面显示出来:-->
<!--第一种全写:{{props.name}}-->
<div>name名字为:{{props.name}}</div>
<!--第二种简写:{{name}},这种简写有个问题,如果你在js中定义了一个相同名的参数,它会优先读取定义的那个参数,不会去读父组件传过来的参数-->
<div>name名字为:{{name}}</div>
<div>age年龄为:{{props.age}}</div>
<div>isOpenEmail是否开通邮箱为:{{props.isOpenEmail}}</div>
<div>content内容为:{{props.content[0]}}</div>
<div>score分数为:{{props.score.score}}</div>
</div>
</template>
<script setup>
import {onMounted} from 'vue'
//const name="haha"
//页面初始化时执行
onMounted(() => {
console.info("从父组件那边传过来的值为:")
console.info("name:",props.name)
console.info("age:",props.age)
console.info("isOpenEmail:",props.isOpenEmail)
console.info("content:",props.content)
console.info("score:",props.score)
})
//使用defineProps接收父组件穿传过来的参数
const props = defineProps({
//从父组件那边接收一个String类型的参数,参数名叫name(名字)
name: {
required:false,//是否必须传该name参数,不写默认为false,true为必须传该参数,false为可以不传该name参数
type: String,//参数类型为String字符串
default: ''//默认值为空值
},
//从父组件那边接收一个Number类型的参数,参数名叫age(年龄)
age: {
type: Number,//参数类型为Number整型
default: 0//默认值为0
},
//从父组件那边接收一个Boolean类型的参数,参数名叫isOpenEmail(是否开通邮箱)
isOpenEmail: {
type: Boolean,//参数类型为Boolean布尔类型
default: false//默认值为false
},
//从父组件那边接收一个content类型的参数,参数名叫content(内容)
content: {
type: Array,//参数类型为Array数组类型
default: []//默认值为空数组
},
//从父组件那边接收一个Object类型的参数,参数名叫score(分数)
score: {
type: Object,//参数类型为Object对象类型
default: {}//默认值为空对象
}
})
</script>
<style scoped>
</style>
谷歌浏览器效果显示
注意事项
1.在页面模版中如果直接使用简写{{name}}这种写法,如果在js中又重新定义了一个name属性,那么会优先使用定义的这个name值,不会使用父组件那边传值过来的name属性值。如下:
2.如果子组件接收的参数有required属性而且为true,如果父组件不传该属性的话,那么控制台会出现警告。如下:
浏览器结果如下:
父组件给子组件传值-使用defineModel接受父组件v-model值
当子组件只需要接收父组件一个v-model值时,写法1如下:
父组件代码:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--父组件向子组件传参,使用v-model形式进行双向绑定-->
<TestChild
v-model="testName"
/>
</div>
</template>
<script setup name="test-management1">
import {ref} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//定义name值
const testName=ref('张三')
</script>
<style scoped>
</style>
子组件代码:
<template>
<!--显示父组件传过来的参数值-->
<div>
<el-input v-model="modelValue"></el-input>
</div>
</template>
<script setup>
import {onMounted} from 'vue'
//页面初始化时执行
onMounted(() => {
})
//直接使用解构写法来接收父组件那边通过v-model传过来的值,必须使用modelValue来接收属性值
const [modelValue]=defineModel()
console.info("子组件接收父组件的name值为:",modelValue.value)
</script>
<style scoped>
</style>
谷歌浏览器结果如下:
子组件接收单个v-model写法2如下:
<template>
<!--显示父组件传过来的参数值-->
<div>
<el-input v-model="name"></el-input>
</div>
</template>
<script setup>
import {onMounted} from 'vue'
//页面初始化时执行
onMounted(() => {
console.info("子组件接收父组件的v-model值为:",name.value)
})
//直接使用defineModel接受父组件通过v-model传过来的值
//子组件这边定义一个name属性来接收父组件那边传过来的v-model的值,简写形式如下
const name=defineModel()
//完整写法
/*const name = defineModel({
type: String,//字段类型
default: ''//默认空值
})*/
//等同于上面的default: ''写法
/*const name = defineModel({
type: String,//字段类型
default: () => {//扩展:将默认属性作为一个方法使用,可以执行一段自定义逻辑,这里直接返回了一个空值,等同于上面写法
return ''
}
})*/
</script>
<style scoped>
</style>
浏览器结果如下:
当子组件需要接收父组件多个v-model值时,写法如下:
父组件代码:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--父组件向子组件传参,使用v-model形式进行双向绑定-->
<!--绑定多个v-model时,v-model冒号后面为参数名称,后面为参数值-->
<!--向子组件传参属性为testName,testAge,testIsOpenEmail,testContent,testScore这5个参数-->
<TestChild
v-model:testName="name"
v-model:testAge="age"
v-model:testIsOpenEmail="isOpenEmail"
v-model:testContent="content"
v-model:testScore="score"
/>
</div>
</template>
<script setup name="test-management1">
import {ref} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//定义name值
const name=ref('张三')
//定义age值
const age=ref(18)
//定义isOpenEmail开通邮箱值
const isOpenEmail=ref(false)
//定义content值
const content=ref(['测试值1','测试值2','测试值3'])
//定义score值
const score=ref({
curriculum:"语文",
score:60
})
</script>
<style scoped>
</style>
子组件代码:
<template>
<!--显示父组件传过来的参数值-->
<div>
<!--使用父组件那边传过来的name值-->
<el-input v-model="name"></el-input>
</div>
</template>
<script setup>
import {onMounted} from 'vue'
//页面初始化时执行
onMounted(() => {
console.info("子组件接收父组件的v-model值为:")
console.info("name:",name.value)
console.info("age:",age.value)
console.info("isOpenEmail:",isOpenEmail.value)
console.info("content:",content.value)
console.info("score:",score.value)
})
//直接使用defineModel接受父组件通过v-model传过来的值
//子组件这边自定义一个name属性来接收父组件那边传过来的v-model(testName)的值,简写形式如下
const name=defineModel('testName')
//完整写法
/*const name = defineModel('testName',{
type: String,//字符串类型
default: ''//默认值为空
})*/
//子组件这边自定义一个age属性来接收父组件那边传过来的v-model(testAge)的值
const age = defineModel('testAge',{
type: Number,//整型类型
default: 0//默认值为0
})
//子组件这边自定义一个isOpenEmail属性来接收父组件那边传过来的v-model(testIsOpenEmail)的值
const isOpenEmail = defineModel('testIsOpenEmail',{
type: Boolean,//布尔类型
default: false//默认值为false
})
//子组件这边自定义一个content属性来接收父组件那边传过来的v-model(testContent)的值
const content = defineModel('testContent',{
type: Array,//数组类型
default: []//默认值为空数组
})
//子组件这边自定义一个score属性来接收父组件那边传过来的v-model(testScore)的值
const score = defineModel('testScore',{
type: Object,//对象类型
default: {}//默认值为空对象
})
</script>
<style scoped>
</style>
浏览器结果如下:
父组件给子组件传方法
父组件代码:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--父组件向子组件传递一个方法,方法名为print-->
<TestChild
:print="testPrint"
/>
</div>
</template>
<script setup name="test-management1">
import {ref} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//定义print方法
const testPrint = () => {
console.info("我是父组件的testPrint方法。")
}
</script>
<style scoped>
</style>
子组件代码:
<template>
<!--显示父组件传过来的参数值-->
<div>
子组件
</div>
</template>
<script setup>
import {onMounted} from 'vue'
//页面初始化时执行
onMounted(() => {
//调用父组件那边传过来的方法
props.print()
})
//使用defineProps接受父组件穿传过来的参数
const props = defineProps({
//从父组件那边接收一个方法,参数名叫print
print: {
type: Function,//参数类型为Function方法类型
default: () =>{}//默认一个空方法
}
})
</script>
<style scoped>
</style>
谷歌浏览器结果如下:
子组件调用父组件方法-使用defineEmits调用父组件方法
父组件代码:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--自定义一个testChange方法-->
<TestChild
@testChange="change"
/>
</div>
</template>
<script setup name="test-management1">
import {ref} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//父组件定义一个change方法
const change = (val) => {
console.info("我是父组件的change方法,参数值为:",val)
}
</script>
<style scoped>
</style>
子组件代码:
<template>
<!--子组件-->
<div>
我是子组件
<!--不使用js的话,模版页面直接调用父方法并传递参数写法如下:-->
<!-- <el-button @click="$emit('testChange','2222')"></el-button>-->
</div>
</template>
<script setup>
//使用defineEmits接收父组件自定义的testChange方法
const emit=defineEmits(['testChange'])
//子组件调用父组件中的testChange方法,并给该方法传递了一个参数值为2222
emit('testChange', '2222')
</script>
<style scoped>
</style>
浏览器结果如下:
子组件暴露属性和方法给父组件调用-使用defineExpose暴露子组件属性和方法
子组件代码:
<template>
<!--子组件-->
<div>
<div>name名字为:{{name}}</div>
<div>age年龄为:{{age}}</div>
<div>isOpenEmail是否开通邮箱为:{{isOpenEmail}}</div>
<div>content内容为:{{content}}</div>
<div>score分数为:{{score}}</div>
</div>
</template>
<script setup>
import {ref} from "vue"
//定义name值
const name=ref('张三')
//定义age值
const age=ref(18)
//定义isOpenEmail开通邮箱值
const isOpenEmail=ref(false)
//定义content值
const content=ref(['测试值1','测试值2','测试值3'])
//定义score值
const score=ref({
curriculum:"语文",
score:60
})
//测试方法
const test = (val) => {
console.info("我是子组件的test方法,参数值是=",val)
}
//使用defineExpose暴露属性和方法,暴露了name,age,isOpenEmail,content,score这5个属性值和1个test方法给父组件调用
defineExpose({
name,
age,
isOpenEmail,
content,
score,
test
})
</script>
<style scoped>
</style>
父组件代码:
<template>
<div>测试管理1页面</div>
<div>
<!--在父组件里面使用子组件TestChild-->
<!--如果子组件里面暴露了属性和方法,子组件必须要加ref才能在父组件中调用子组件里面的属性和方法-->
<TestChild ref="testChildRef"/>
</div>
</template>
<script setup name="test-management1">
import {ref,onMounted} from "vue"
//引入子组件
import TestChild from './components/test-child.vue'
//页面初始化加载
onMounted(() => {
//调用子组件里面的暴露的属性和方法,要用ref去调用
console.info("父组件中调用子组件中属性值:")
console.info("name值:",testChildRef.value.name)
console.info("age值:",testChildRef.value.age)
console.info("isOpenEmail值:",testChildRef.value.isOpenEmail)
console.info("content值:",testChildRef.value.content)
console.info("score值:",testChildRef.value.score)
console.info("父组件中调用子组件中方法:")
testChildRef.value.test('111')
})
//子组件ref名称
const testChildRef=ref()
</script>
<style scoped>
</style>
浏览器结果如下:
注意事项
如果父组件中调用了子组件未暴露的属性或者方法或者不存在的属性和方法,那么浏览器控制台会报错,如下:
父子组件的传值和方法互相调用就学习到这里了,后面要是遇到新的方式在扩展。