Vue3 開發之 components 組件
前言
開發過程中我們會經常遇到一些複雜的頁面,而這些頁面大部分由一個個小部分組合起來的,而且不同頁面中可能有些部分是一樣的,所以我們通常會將這些部分封裝成組件。在 Vue 中,我們可以使用 components 組件 (模板) 來實現。
實現一個組件
一個組件其實就是一個 vue 文件,簡單示例(header.vue)如下:
<script setup></script>
<template>
<div></div>
</template>
<style scoped lang="less">
.header {
position: absolute;
width: 100%;
height: 80px;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0.6) 0%,
rgba(0, 0, 0, 0) 100%
);
}
</style>
註冊使用
<script setup>
import Header from "./Header.vue";
</script>
<template>
<div>
<Header></Header>
</div>
</template>
<style scoped lang="less">
.container {
width: 100%;
height: 100%;
}
</style>
數據
<script setup>
const props = defineProps({
title: String,
userInfo: Object
});
function doSomething(){
if(props.userInfo.isVip){
...
}
}
</script>
<template>
<div>{{props.title}}</div>
</template>
<style scoped lang="less">
...
</style>
這裏定義了一個 title 屬性,是一個字符串;一個 userInfo 屬性,是一個對象,然後在組件中就可以通過props.xxx
來使用這些屬性。
那麼如何將數據傳遞給這些屬性呢,直接通過 v-bind 綁定數據即可,如下:
<script setup>
import Header from "./Header.vue";
let userInfo;
let title;
</script>
<template>
<div>
<Header :title="title" :userInfo="userInfo"></Header>
</div>
</template>
<style scoped lang="less">
...
</style>
這裏使用的是 v-bind 的縮寫。v-bind 綁定後面雙引號中是表達式,所以如果類型是:
-
數值:
:count="3"
-
布爾值:
:isVip="true"
-
數組:
:array="[1,2,3]"
-
對象:
:info="{name:'名字',isVip:true}"
其他就不一一列舉了。
Props 是支持類型如下:
-
String
-
Number
-
Boolean
-
Array
-
Object
-
Date
-
Function
-
Symbol。
Props 支持類型檢查,同時支持默認值,如下:
props: {
// 基礎的類型檢查 (`null` 和 `undefined` 值會通過任何類型驗證)
propA: Number,
// 多個可能的類型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 帶有默認值的數字
propD: {
type: Number,
default: 100
},
// 帶有默認值的對象
propE: {
type: Object,
// 對象或數組的默認值必須從一個工廠函數返回
default() {
return { message: 'hello' }
}
},
// 具有默認值的函數
propG: {
type: Function,
// 與對象或數組的默認值不同,這不是一個工廠函數——這是一個用作默認值的函數
default() {
return 'Default function'
}
}
}
Props 是單向數據流,這樣可以防止子組件意外變更父組件的狀態,每當父組件發生變更,子組件所有 Props 都會刷新到最新值。所以子組件中不能更改 Props 的屬性,否則會在控制檯警告。
事件
數據傳遞清楚了,那麼如何響應組件的一些自定義事件呢?通過 emits 來定義事件,如下:
<script setup>
const emit = defineEmits(["onSelected"]);
function selectItem(item){
emit("onSelected", item);
}
</script>
<template>
<div >
...
</div>
</template>
<style scoped lang="less">
...
</style>
這裏定義了一個 onSelected 事件,當組件中觸發 selectItem 函數的時候就會執行這個事件。
在父組件中則通過 v-on 來綁定事件,如下:
<script setup>
import Header from "./Header.vue";
function headerSelected(item){
}
</script>
<template>
<div>
<Header @onSelected="headerSelected"></Header>
</div>
</template>
<style scoped lang="less">
...
</style>
這裏同樣是 v-on 的縮寫形式,這樣就綁定了事件。事件同樣可以驗證,這裏就不細說了。
v-model
v-model 是雙向數據綁定,默認情況下,組件上的 v-model 使用 modelValue 作爲 prop 和 update:modelValue 作爲事件。比如有一個 title 屬性:
<my-component v-model:title="bookTitle"></my-component>
那麼在子組件中就可以這樣做:
<script setup>
const props = defineProps({
title: String
});
const emit = defineEmits(["update:title"]);
</script>
<template>
<div >
...
</div>
</template>
<style scoped lang="less">
...
</style>
這樣子組件中可以通過update:title
來同步 title 數據。
插槽
如果子組件中部分區域是不定的,需要父組件來實現,那麼怎麼辦?這就需要用到插槽 slot,插槽使用很簡單,如下:
<script setup>
...
</script>
<template>
<div >
<slot>default</slot>
</div>
</template>
<style scoped lang="less">
...
</style>
這裏定義了一個插槽,並且指定了一個默認內容,在父組件中:
<script setup>
import Header from "./Header.vue";
</script>
<template>
<div>
<Header>newtitle</Header>
</div>
</template>
<style scoped lang="less">
...
</style>
這樣插槽就被 “newtitle” 這個字符串替代,如果這裏沒有任何內容<Header></Header>
則顯示默認內容,即 "default"。
插槽中不僅是字符串,可以是 html 或其他組件,比如:
<Header>
<button>submit</botton>
</Header>
當然默認內容也可以是 html。
具名插槽
一個組件裏可以有多個插槽,比如左邊欄右邊欄等,這樣就需要具名插槽來區分,如下:
<script setup>
...
</script>
<template>
<div >
<div >
<slot>left</slot>
</div>
<div >
<slot>right</slot>
</div>
</div>
</template>
<style scoped lang="less">
...
</style>
使用的時候需要用 v-slot 指令,並且一定是<template>
元素,因爲 v-slot 只能添加在<template>
上,如下:
<script setup>
import Header from "./Header.vue";
</script>
<template>
<div>
<Header>
<template v-slot:leftbar >
<button>left</button>
</template>
<template v-slot:rightbar >
<button>right</button>
</template>
</Header>
</div>
</template>
<style scoped lang="less">
...
</style>
插槽名也可以是動態的<template v-slot:[dynamicSlotName] >
。v-slot 也可以縮寫成 “#”,如<template #leftbar >
。
調用子組件方法
Expose
首先子組件的方法需要暴露出去,如下:
<script setup>
defineExpose({
onEvent
});
function onEvent(event) {
console.log(`header: ${event}`);
}
</script>
<template>
<div >
...
</div>
</template>
<style scoped lang="less">
...
</style>
然後在父組件中爲子組件添加 ref 屬性,然後通過 ref() 函數獲取對象執行方法即可,如下:
<script setup>
import Header from "./Header.vue";
const headerRef = ref();
function headerEvent(event){
headerRef.value.onEvent(event);
}
</script>
<template>
<div>
<Header ref="headerRef">
...
</Header>
</div>
</template>
<style scoped lang="less">
...
</style>
上面 header 的 ref 屬性是 “headerRef”,所以變量名也保持一致,即const headerRef = ref();
,然後就可以通過headerRef.value
執行暴露出來的方法了。
如果有多個子組件,都設置了 ref 屬性,則定義多個變量即可,如下:
const headerRef = ref();
const footerRef = ref();
EventBus
更簡單的方法是使用 EventBus 即可完成組件間通信,使用也非常簡單,參考官方文檔即可 https://www.npmjs.com/package/events
我們可以簡單封裝一下以便使用,如下:
import EventEmitter from "events";
const eventEmitter = new EventEmitter();
export const WsEvent = {
emit: data => {
eventEmitter.emit("wsevent", data);
},
on: callback => {
eventEmitter.on("wsevent", callback);
}
};
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ekpj3HbWuIxLoB68fTcqoA