03_元件、事件、插槽、釋出訂閱

野码發表於2024-06-23

文件:元件基礎 | 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)

相關文章