03_组件、事件、插槽、发布订阅

文档:组件基础 | Vue.js (vuejs.org)

什么是组件:

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.生命周期钩子

生命周期钩子 | Vue.js (vuejs.org)

 

posted @ 2024-06-23 11:19  野码  阅读(12)  评论(0编辑  收藏  举报