Father.vue
<template>
<Child
title="用户信息"
:index="1"
:uid="userInfo.id"
:user-name="userInfo.name"
/>
</template>
<script>
import { defineComponent } from 'vue'
import Child from '@cp/Child.vue'
interface Member {
id: number,
name: string
};
export default defineComponent({
// 需要启用子组件作为模板
components: {
Child
},
// 定义一些数据并return给template用
setup () {
const userInfo: Member = {
id: 1,
name: 'Petter'
}
// 不要忘记return,否则template拿不到数据
return {
userInfo
}
}
})
</script>
Child.vue
export default defineComponent({
props: [
'title',
'index',
'userName',
'uid'
]
})
加上类型限制:
export default defineComponent({
props: {
title: String,
index: Number,
userName: String,
uid: Number
}
})
如果你需要对某个 prop 允许多类型,比如这个 uid 字段,它可能是数值,也可能是字符串,那么可以在类型这里,使用一个数组,把允许的类型都加进去。
export default defineComponent({
props: {
// 单类型
title: String,
index: Number,
userName: String,
// 这里使用了多种类型
uid: [ Number, String ]
}
})
对部分字段设置为可选,并提供默认值:
export default defineComponent({
props: {
// 可选,并提供默认值
title: {
type: String,
required: false,
default: '默认标题'
},
// 默认可选,单类型
index: Number,
// 添加一些自定义校验
userName: {
type: String,
// 在这里校验用户名必须至少3个字
validator: v => v.length >= 3
},
// 默认可选,但允许多种类型
uid: [ Number, String ]
}
})
template 部分
<template>
<p>标题:{{ title }}</p>
<p>索引:{{ index }}</p>
<p>用户id:{{ uid }}</p>
<p>用户名:{{ userName }}</p>
</template>
script 部分
export default defineComponent({
props: {
title: String,
index: Number,
userName: String,
uid: Number
},
// 在这里需要添加一个入参
setup (props) {
// 该入参包含了我们定义的所有props
console.log(props);
}
})
在 Child.vue 里,可以通过 setup 的第二个参数 context 里的 attrs 来获取到这些属性。
export default defineComponent({
setup (props, { attrs }) {
// attrs 是个对象,每个 Attribute 都是它的 key
console.log(attrs.class);
// 如果传下来的 Attribute 带有短横线,需要通过这种方式获取
console.log(attrs['data-hash']);
}
})
Child.vue 绑定一个更新用户年龄的方法,那么在 Father.vue 里需要这么处理:
<template>
<Child
@update-age="updateAge"
/>
</template>
<script>
import { defineComponent, reactive } from 'vue'
import Child from '@cp/Child.vue'
interface Member {
id: number,
name: string,
age: number
};
export default defineComponent({
components: {
Child
},
setup () {
const userInfo: Member = reactive({
id: 1,
name: 'Petter',
age: 0
})
// 定义一个更新年龄的方法
const updateAge = (age: number): void => {
userInfo.age = age;
}
return {
userInfo,
// return给template用
updateAge
}
}
})
</script>
动态绑定 props 是用 :,绑定 emit 是用 @
Child.vue
export default defineComponent({
emits: [
'update-age'
]
})
接收 emits 时做一些校验
比如上面的更新年龄,只允许达到成年人的年龄才会去更新父组件的数据:
export default defineComponent({
emits: {
// 需要校验
'update-age': (age: number) => {
// 写一些条件拦截,记得返回false
if ( age < 18 ) {
console.log('未成年人不允许参与');
return false;
}
// 通过则返回true
return true;
},
// 一些无需校验的,设置为null即可
'update-name': null
}
})
调用 emits
export default defineComponent({
emits: [
'update-age'
],
setup (props, { emit }) {
// 2s 后更新年龄
setTimeout( () => {
emit('update-age', 22);
}, 2000);
}
})
Father.vue
<template>
<Child
v-model:user-name="userInfo.name"
/>
</template>
如果你要绑定多个数据,写多个 v-model 即可
<template>
<Child
v-model:user-name="userInfo.name"
v-model:uid="userInfo.id"
/>
</template>
看到这里应该能明白了,一个 v-model 其实就是一个 prop,它支持的数据类型,和 prop 是一样的。
所以,子组件在接收数据的时候,完全按照 props 去定义就可以了。
export default defineComponent({
props: [
'title',
'index',
'userName',
'uid'
]
})
Child.vue
虽然 v-model 的配置和 prop 相似,但是为什么出这么两个相似的东西?自然是为了简化一些开发上的操作。
使用 props / emits,如果要更新父组件的数据,还需要在父组件定义好方法,然后 return 给 template 去绑定事件给子组件,才能够更新。
而使用 v-model / emits ,无需如此,可以在 Child.vue 直接通过 “update:属性名” 的格式,直接定义一个更新事件:
export default defineComponent({
props: {
userName: String,
uid: Number
},
emits: [
'update:userName',
'update:uid'
]
})
调用自身的 emits
在 Child.vue 配置好 emits 之后,就可以在 setup 里直接操作数据的更新了:
export default defineComponent({
// ...
setup (props, { emit }) {
// 2s 后更新用户名
setTimeout(() => {
emit('update:userName', 'Tom')
}, 2000);
}
})
在学习 响应式 API 之 ref 的时候,我们了解到 ref 是可以用在 DOM 元素与子组件 上面。
所以,父组件也可以直接通过对子组件绑定 ref 属性,然后通过 ref 变量去操作子组件的数据或者调用里面的方法。
比如导入了一个 Child.vue 作为子组件,需要在 template 处给子组件标签绑定 ref:
<template>
<Child ref="child" />
</template>
然后在 script 部分定义好对应的变量名称(记得要 return 出来):
import { defineComponent, onMounted, ref } from 'vue'
import Child from '@cp/Child.vue'
export default defineComponent({
components: {
Child
},
setup () {
// 给子组件定义一个ref变量
const child = ref<HTMLElement>(null);
// 请保证视图渲染完毕后再执行操作
onMounted( () => {
// 执行子组件里面的ajax函数
child.value.getList();
// 打开子组件里面的弹窗
child.value.isShowDialog = true;
});
// 必须return出去才可以给到template使用
return {
child
}
}
})
子组件如果想主动向父组件通讯,也需要使用 emit