目录
一、环境
二、为什么要组件开发
三、正式开发
【初步封装】child.vue
【初步封装】App.vue
【父组件向子组件传参数/方法】child.vue
【父组件向子组件传参数/方法】App.vue
【子组件向父组件暴露参数/方法】child.vue
【子组件向父组件暴露参数/方法】App.vue
【子组件向父组件传递参数/方法】child.vue
【子组件向父组件传递参数/方法】App.vue
总结
四、defineProps、defineEmits、defineExpose方法介绍
defineProps、defineEmits
defineExpose
子组件
父组件
一、环境
本文组件开发使用环境如下:
- Vue3
- setup语法糖
二、为什么要组件开发
Vue3的一个很大特性在于“组件”,有了组件,我们可以将原本需要几千几万行的一个前端文件(html、js、css)分成若干个小的部分,根据需要再将小的部分拼装起来,同时体现组件的复用性
三、正式开发
首先在component文件夹下创建一个“child.vue”文件,作为组件,默认的App.vue作为父组件使用child.vue组件
假设我们的子组件需要封装一个<input>输入框,父组件只需充当子组件的容器,假设在这个阶段,我们不需要与子组件进行信息交互。
【初步封装】child.vue
在这里我们封装了一个输入框,该输入框的高度和宽度取决于父组件,并且该输入框的值依赖于组件本身的变量“inputValue”
注意:此时父组件无法获取到子组件的值,因为我们没有设置外部方法和外部参数。
<script setup>
import { ref } from 'vue'
const inputValue = ref('')
</script><template><div class="container"><div>测试:</div><input type="text" v-model="inputValue" class="input"></div>
</template><style scoped>.container {width: 100%;height: 100%;
}
.input {width: 100%;height: auto;line-height: 100%;font-size: 18pxpx;
}
</style>
【初步封装】App.vue
<script setup>
import myInput from './components/child.vue'
</script><template><div class="container"><myInput /></div>
</template><style scoped>
.container {width: 300px;margin: 20px auto;
}
</style>
效果:
此时组件可以正常运行,但是此时我们的业务需求增加一条:“可以由父组件传入输入框的标签,即label”
【父组件向子组件传参数/方法】child.vue
子组件使用defineProps预编译宏,该函数可以用来指定父组件传入的参数,并且可以指定传入参数的类型和默认值。
注意:在新版本Vue3中,defineProps不需要导入,在旧版本中仍然需要使用import导入
<script setup>
import { ref } from 'vue'const props = defineProps({labelName:{type: String,default: '测试'}
})const inputValue = ref('')
</script><template><div class="container"><div>{{ props.labelName }}</div><input type="text" v-model="inputValue" class="input"></div>
</template><style scoped>.container {width: 100%;height: 100%;
}
.input {width: 100%;height: auto;line-height: 100%;font-size: 18px;
}
</style>
【父组件向子组件传参数/方法】App.vue
<script setup>
import { ref } from 'vue'
import myInput from './components/child.vue'
const name = ref('用户名')
</script><template><div class="container"><myInput labelName="用户名"/><!-- 或者 --><myInput :labelName="name"/></div>
</template><style scoped>
.container {width: 300px;height: auto;margin: 20px auto;
}
</style>
效果:
看起来,我们成功向父组件传入了参数,但是此时业务需求再次增加:“父组件创建一个按钮,点击按钮后,输入框会清空”
【子组件向父组件暴露参数/方法】child.vue
这里,我们使用defineExpose方法,暴露组件本身的“inputValue”变量,顾名思义,暴露的变量,父组件可以控制改组件的变量。
<script setup>
import { ref } from 'vue'const props = defineProps({labelName:{type: String,default: '测试'}
})const inputValue = ref('')//定义暴露的参数
defineExpose({inputValue
})
</script><template><div class="container"><div>{{ props.labelName }}</div><input type="text" v-model="inputValue" class="input"></div>
</template><style scoped>.container {width: 100%;height: 100%;
}
.input {width: 100%;height: auto;line-height: 100%;font-size: 18px;
}
</style>
【子组件向父组件暴露参数/方法】App.vue
父组件给子组件传递一个“ref”,后续使用ref的value方法获取组件暴露的变量
<script setup>
import { ref } from 'vue'
import myInput from './components/child.vue'
const name = ref('用户名')
// 使用ref获取子组件的DOM节点
const input = ref(null)function clearInput() {input.value.inputValue = ''
}
</script><template><div class="container"><myInput labelName="用户名"ref="input"/><button @click="clearInput">清空</button></div>
</template><style scoped>
.container {width: 300px;height: auto;margin: 20px auto;
}
</style>
点击后,输入框中的内容成功被清除。
此时,我们又多了一个需求:“输入框中值为子字符串’abcd‘时,向父组件发出信号(执行父组件的方法)”
在这里为方便起见,当输入框出现abcd时,父组件接收子组件的值,并且弹窗提醒。
秉持着,父组件尽量不应破坏子组件的原则,检测字符串的任务应交由“子组件child.vue”完成,弹窗提醒交由“父组件App.vue”
弹窗提醒交给父组件是为了保证:当出现abcd时,父组件可以根据场景灵活的选择处理方案。
【子组件向父组件传递参数/方法】child.vue
这里使用defineEmits方法(需要import导入最新版本已不需要导入),用来向主动的向父组件传递参数
<script setup>
import { ref,defineEmits } from 'vue'// 定义外部参数
const props = defineProps({labelName:{type: String,default: '测试'}
})const inputValue = ref('')//定义暴露的参数
defineExpose({inputValue
})// 定义事件
const emit = defineEmits(['customInput'])function watchValue() {if (inputValue.value == 'abcd'){emit('customInput', inputValue.value);}
}
</script><template><div class="container"><div>{{ props.labelName }}</div><input type="text" v-model="inputValue" class="input" @input="watchValue"></div>
</template><style scoped>.container {width: 100%;height: 100%;
}
.input {width: 100%;height: auto;line-height: 100%;font-size: 18px;
}
</style>
【子组件向父组件传递参数/方法】App.vue
<script setup>
import { ref } from 'vue'
import myInput from './components/child.vue'
const name = ref('用户名')
// 使用ref获取子组件的DOM节点
const input = ref(null)function clearInput() {input.value.inputValue = ''
}
function Pop(value){console.log(value);alert(value);
}
</script><template><div class="container"><myInput labelName="用户名"ref="input"@customInput="Pop"/><button @click="clearInput">清空</button></div>
</template><style scoped>
.container {width: 300px;height: auto;margin: 20px auto;
}
</style>
效果:
总结
- 子组件主动的向父组件传递消息:使用defineEmits
- 子组件被动的接收父组件传递消息:使用defineExpose
- 子组件初始化的接收父组件传递消息:使用defineProps
四、defineProps、defineEmits、defineExpose方法介绍
defineProps、defineEmits
- defineProps和defineEmits只能在<script setup>中使用,不需要被导入
- defineProps和defineEmits可以对传入值进行类型约束
const props = defineProps({test:{type: String,default: '测试',required: true,validator:(value) => {if (value.length > 5){return false}else {return true}}}
})
- type:指定prop的类型,例如String、Number、Boolean、Object、Array、Func
- default:指定prop默认值
- required:表明该prop是否为必需项
- validator:自定义验证函数,用于对传入的prop值进行验证,返回true表示验证通过,返回false表示验证失败
响应式Props结构:
在Vue3.5及以上版本中,从defineProps返回值解构出的变量是响应式的。
const { foo } = defineProps(['foo'])
// 使用数组形式定义事件
const emit = defineEmits(['messageSent']);// 或者使用对象形式定义事件并进行参数验证
// const emit = defineEmits({
// messageSent: (payload) => {
// if (typeof payload === 'string') {
// return true;
// } else {
// console.warn('messageSent 事件的参数必须是字符串');
// return false;
// }
// }
// });
- 使用对象形式定义事件时,以键值对的形式,对参数进行验证
defineExpose
子组件
<template><div><p>子组件内容</p></div>
</template><script setup>
import { ref } from 'vue';// 定义子组件内部的响应式数据
const innerCount = ref(0);// 定义子组件内部的方法
const increment = () => {innerCount.value++;
};// 使用 defineExpose 暴露属性和方法
defineExpose({innerCount,increment
});
</script><style scoped>
/* 子组件样式 */
</style>
父组件
<template><div><ChildComponent ref="childRef" /><button @click="callChildMethod">调用子组件方法</button><p>子组件的计数: {{ childRef?.innerCount }}</p></div>
</template><script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';// 创建一个 ref 来引用子组件
const childRef = ref(null);// 调用子组件暴露的方法
const callChildMethod = () => {if (childRef.value) {childRef.value.increment();}
};
</script><style scoped>
/* 父组件样式 */
</style>