03_组件、事件、插槽、发布订阅
什么是组件:
vue组件可以将一个复杂的应用程序(或者是页面)拆分成多个小的模块,每个模块可以独立的进行开发和测试。在Vue当中组件是通过虚拟DOM来实现的,可以将复杂的页面拆分多个小的DOM节点,每个节点对应一个组件,然后这些组件组合成一个完整的页面。
1.定义一个组件
当使用构建步骤时,我们一般会将 Vue 组件定义在一个单独的 .vue 文件中,这被叫做单文件组件 (简称 SFC):
ButtonCounter.vue
<!-- 写js代码,或者vue代码 --> <script setup> import { ref } from 'vue' // 定义组件可以接收的参数 // 参数为单向数据流 // ROOT <- Button <- Submit // ROOT -> Button -> Submit // step: 只能在父组件进行修改,不能在子组件进行修改 const props = defineProps({ // 定义参数 // 参数名: 参数数据类型 // step: Number step: { type: Number, // String, Array, Object // 必须要传递参数 // required: true, // 设置默认值 default: 8, // 自定义校验规则 validator: function(value) { console.log("传递的参数是:", value) // true: 校验通过 // false: 校验失败 // 传递的值必须要小于10 if (value<10) { return true } else { return false } } } }) const count = ref(0) const plus = ()=>{ count.value = count.value + props.step } </script> <!-- 写html文档 --> <template> <div> <p>你传递的参数是: {{ props.step }}</p> <button class="button" @click="plus">You clicked me {{ count }} times.</button> </div> </template> <!-- 写组件的样式,scoped表示只对当前页有效 --> <style scoped> .button{ font-size: 20px; background-color: aqua; } </style>
要使用一个子组件,我们需要在父组件中导入它。
<script setup> import ButtonCounter from './ButtonCounter.vue' </script> <template> <h1>Here is a child component!</h1> <ButtonCounter :step="2" /> </template>
2.触发与监听事件
Message.vue
<!-- 写js代码,或者vue代码 --> <script setup> import { ref } from 'vue' // 定义组件可以接收的参数 // 参数为单向数据流 const props = defineProps({ msg: { type: String, // String, Array, Object } }) const emit = defineEmits(['changeValue']) // 子组件内不能修改父组件的值 const modifyMsg = ()=>{ // 直接改不可以的 // props.msg = "这是通过子组件直接进行修改的值" // 通过事件让父组件进行修改,changValue对应App.vue中的@change-value emit('changeValue',"这里是传到父组件的参数") } </script> <!-- 写html文档 --> <template> <div> {{ msg }} <button placeholder="请输入值" type="button" @click="modifyMsg">修改</button> </div> </template> <!-- 写组件的样式 --> <style scoped> .button{ font-size: 20px; background-color: aqua; } </style>
父组件App.vue
import Message from './components/Message.vue'; import { ref } from 'vue' const msg = ref("这是父组件传递的参数") const modify = (value) => { // 由父组件直接更改 // msg.value = "这是一个新值,由子组件触发" // 接收参数进行修改 msg.value = value } <template> <Message :msg="msg" @change-value="modify"></Message> </template>
3.插槽
SlotDemo.vue
<script setup> import { onBeforeMount, onMounted,onBeforeUpdate,onUpdated, onBeforeUnmount ,onUnmounted } from 'vue'; // 插槽: 插槽是vue中一种特殊的机制,他可以让我们在组件中定义可插入区域、 // 也就是可以在不同的地方插入不同的内容 // 可以让一个通用的组件,展示不同的内容 // beforeUpdate --> onBeforeUpdate onBeforeUpdate( () => { console.log("钩子函数: onBeforeUpdate") } ) // updated --> onUpdated onUpdated( () => { console.log("钩子函数: onUpdated") } ) // beforeUnmount --> onBeforeUnmount , vue2: beforeDestory onBeforeUnmount( () => { console.log("钩子函数: onBeforeUnmount") } ) // unmounted --> onUnmounted ,vue2: destroyed onUnmounted( () => { console.log("钩子函数: onUnmounted") } ) </script> <template> <div> <!-- 定义一个插槽 --> <!-- name=default --> <!-- 具名插槽,命名插槽 --> <slot name="header"> <p>这是插槽的默认值</p> </slot> <slot name="main"> <p>这是插槽的默认值</p> </slot> <slot name="footer"> <p>这是插槽的默认值</p> </slot> </div> </template> <style> </style>
父组件App.vue
import SlotDemo from './components/SlotDemo.vue'; <template> <!-- 使用插槽这个组件 --> <SlotDemo> <!-- 第一种写法 --> <template v-slot:header> <p>这是header头定义</p> </template> <template #main> <div style="background-color: aqua; width: 200px; height: 200px;"> <p>这是main中内容</p> </div> </template> <template #footer> <p>尾部的定义</p> </template> </SlotDemo> </template>
4.发布与订阅
父组件ProvideInject.vue
<script setup> import MessageProvide from './MessageProvide.vue' import { provide, readonly, ref } from 'vue'; // 订阅和发布数据 // provide:用来发布数据 // inject:用来订阅数据 let defaultMsg = ref("这是父组件发布的数据") // 发布一个数据 // provide语法: provide("发布的命令","你要发布谁") // 发布数据 // provide('message', defaultMsg) // 发布只读数据 provide('message', readonly(defaultMsg)) // 发布一个用于修改数据的函数 const modifyHandler = (newValue) => { defaultMsg.value = newValue } provide('modifyHandler',modifyHandler) </script> <template> <MessageProvide></MessageProvide> </template>
子组件MessageProvide.vue
<script setup> import { inject,ref, onBeforeMount, onMounted,onBeforeUpdate,onUpdated, onBeforeUnmount ,onUnmounted } from 'vue'; // 订阅父组件发布的数据 // inject语法: inject('数据的名字') let injectMsg = inject("message") // 订阅父组件发布的修改数据的函数 const changeValueHandler = inject("modifyHandler") let newMsg = ref('') const submit = ()=> { console.log("提交数据") // 也是可以直接通过子组件修改数据的 // injectMsg.value = newMsg.value // 不推荐 changeValueHandler(newMsg.value) } // beforeUpdate --> onBeforeUpdate onBeforeUpdate( () => { console.log("钩子函数: onBeforeUpdate") } ) // updated --> onUpdated onUpdated( () => { console.log("钩子函数: onUpdated") } ) // beforeUnmount --> onBeforeUnmount , vue2: beforeDestroy onBeforeUnmount( () => { console.log("钩子函数: onBeforeUnmount") } ) // unmounted --> onUnmounted ,vue2: destroyed onUnmounted( () => { console.log("钩子函数: onUnmounted") } ) </script> <template> <div> <p style="color: red;">{{ injectMsg }}</p> <input placeholder="请输入新值" v-model="newMsg" /> <button type="button" style="background-color: aqua;" @click="submit">修改</button> </div> </template>
5.生命周期钩子