vue3學(xué)習(xí)筆記之自定義組件舉例
自定義組件,舉個(gè)??:
1、封裝自定義組件 CustomList.vue
<!-- src/components/CustomList.vue -->
<script lang="ts">
import type { IDataItem } from "../type/customlist";
// IProps 不能直接從外部導(dǎo)入,當(dāng)前會(huì)報(bào)錯(cuò),以后可能會(huì)支持(有人提了issue)
export interface IProps {
title?: string;
data: Array<IDataItem>;
}
</script>
<script setup lang="ts">
import { onMounted } from "vue";
// 聲明一些屬性,給父組件傳值進(jìn)來(lái),并且給部分設(shè)置了默認(rèn)值
const props = withDefaults(defineProps<IProps>(), {
title: "CustomList",
});
// 拋出事件,供父組件使用
// const emit = defineEmits(["click-item"]);
const emit = defineEmits<{
(e: "click-item", item: IDataItem, $event: Event): void;
}>();
const clickItem = function (item: IDataItem, e: Event) {
// 可以在函數(shù)里執(zhí)行完一些操作
console.log("自定義組件點(diǎn)擊了組件里面的item...", item, e);
// 再拋出宏定義的事件
emit("click-item", item, e);
};
onMounted(() => {
console.log("CustomList onMounted...", props);
});
</script>
<template>
<h1>{{ title }}</h1>
<ul @click="$emit('click-ul', $event, 1)">
<li @click="clickItem(item, $event)" v-for="item in data" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
<style lang="less" scoped></style>src 底下 type 文件夾中聲明的 interface 接口文件
// src/type/customlist.d.ts
export interface IDataItem {
name: string;
id: number;
}2、在 App.vue 中使用自定義組件 CustomList.vue
<!-- src/App.vue -->
<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeMount } from "vue";
import CustomList from "./components/CustomList.vue";
import type { IDataItem } from "./type/customlist";
// 聲明一個(gè)data,傳到自定義組件CustomList
const data = reactive([] as Array<IDataItem>);
const customListRef = ref<HTMLElement | null>(null);
// 點(diǎn)擊了自定義組件的item,執(zhí)行了自定義組件拋出宏定義事件并接收其攜帶過(guò)來(lái)的參數(shù)
const clickItem = function (item: string, e: Event) {
console.log("clickItem...", item, e);
};
// 點(diǎn)擊了自定義組件的ul在templete里面直接拋出的事件并接收其攜帶過(guò)來(lái)的參數(shù)
const clickUl = function (e: Event, value: number) {
console.log("clickUl...", e, value);
};
onBeforeMount(() => {
const list: Array<IDataItem> = [
{
name: "aaa",
id: 1,
},
{
name: "bbb",
id: 2,
},
];
data.push(...list);
});
onMounted(() => {
console.log("onMounted...", customListRef.value);
});
</script>
<template>
<div class="content">
<!-- 使用自定義組件CustomList -->
<CustomList
@click-ul="clickUl"
@click-item="clickItem"
:data="data"
ref="customListRef"
/>
</div>
</template>
<style scoped lang="less">
.content {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
color: var(--colorTextNormal);
padding: 50px;
}
</style>一、組件注冊(cè)
組件注冊(cè):https://cn.vuejs.org/guide/components/registration.html
1、全局注冊(cè)
在 main.ts 中使用 app.component('MyComponent', MyComponent) 全局注冊(cè)一個(gè)組件,可以在app內(nèi)的任何地方使用。
缺點(diǎn):
無(wú)法在生產(chǎn)打包時(shí)被自動(dòng)移除 (也叫
tree-shaking),即使它并沒(méi)有被實(shí)際使用,它仍然會(huì)出現(xiàn)在打包后的JS文件中。依賴(lài)關(guān)系不明顯,出問(wèn)題不易定位,用多了難維護(hù)
2、局部注冊(cè)
在用到組件的地方 import 導(dǎo)入
<script setup> import ComponentA from './ComponentA.vue' </script> <template> <ComponentA /> </template>
3、總結(jié):非必要不實(shí)用全局注冊(cè)
二、屬性
父組件通過(guò)給子組件傳遞不同的屬性數(shù)據(jù)控制子組件最終展示狀態(tài)。
1、定義一個(gè) props
通過(guò) defineProps 宏定義一個(gè)屬性
const props = defineProps(['title']) console.log(props.title)
2、給 props添加類(lèi)型并給定默認(rèn)值
defineProps<IProps>() 可以給 props 設(shè)置類(lèi)型,IProps 為 props 類(lèi)型 為組件的 props 標(biāo)注類(lèi)型
withDefaults 的第二個(gè)參數(shù)支持給 props 設(shè)置默認(rèn)值 使用類(lèi)型聲明時(shí)的默認(rèn) props 值
import type { IDataItem } from "../type/customlist";
// IProps 不能直接從外部導(dǎo)入,當(dāng)前會(huì)報(bào)錯(cuò),以后可能會(huì)支持(有人提了issue)
export interface IProps {
title?: string;
data: Array<IDataItem>;
}
const props = withDefaults(defineProps<IProps>(), {
title: "CustomList",
});3、 props只讀,不可修改
組件內(nèi)的 props 都是只讀的,不能對(duì)其進(jìn)行修改 單向數(shù)據(jù)流
<script setup lang="ts">
import { onMounted } from "vue";
// 聲明一些屬性,給父組件傳值進(jìn)來(lái),并且給部分設(shè)置了默認(rèn)值
const props = withDefaults(defineProps<IProps>(), {
title: "CustomList",
});
onMounted(() => {
// ? 不能這么干,單向數(shù)據(jù)流 props 不可修改
props.title = "改變了CustomList的title"
});
</script>三、事件
子組件拋出內(nèi)部事件并傳遞參數(shù)供父組件使用。
1、template 內(nèi)使用 $emit 直接拋出一個(gè)事件
在組件的模板表達(dá)式中,可以直接使用 $emit 方法觸發(fā)自定義事件并拋出相關(guān)參數(shù),$emit('拋出的事件名', 需要傳到父組件的參數(shù)一, 參數(shù)二...)。拋出事件名要用可以使用 camelCase 和 kebab-case 形式(建議 kebab-case )。
<!-- MyComponent -->
<button @click="$emit('someEvent', $event, 1)">click me</button>2、defineEmits() 宏來(lái)聲明要觸發(fā)的事件
在 <template> 中使用的 $emit 方法不能在組件的 <script setup> 部分中使用,相當(dāng)于你不能對(duì)這個(gè)事件先做出一些處理然后再拋出,使用 defineEmits() 宏聲明要觸發(fā)的事件可以解決這個(gè)問(wèn)題:
<script>
// 拋出事件,供父組件使用
const emit = defineEmits<{
// (e: "拋出的事件名", 拋出的參數(shù)一, 參數(shù)二...)
(e: "callback", $event: Event): void;
}>();
const callback = function (e: Event) {
emit("callback", e);
};
</script>
<template>
<button @click="callback($event, 111)">click me</button>
</template>3、使用組件并監(jiān)聽(tīng)子組件拋出的事件及參數(shù)
<script>
import MyComponent from "./components/MyComponent.vue";
const callback = function (e: Event, value) {
console.log("callback...", e, value);
}
</script>
<template>
<MyComponent @some-event="callback" />
</template>四、插槽
可以在自定義組件內(nèi)部指定的部位插入自定義內(nèi)容,讓組件更加靈活。
1、匿名插槽
組件內(nèi)插入 <slot> 標(biāo)簽不指定 name 屬性,默認(rèn)只有一個(gè),可以給插槽內(nèi)添加默認(rèn)值,父組件在使用組件時(shí)不傳值的時(shí)候會(huì)展示默認(rèn)內(nèi)容,傳值則會(huì)替換掉默認(rèn)內(nèi)容。
<!-- 子組件 SubmitButton --> <button type="submit"> <slot>這里是默認(rèn)內(nèi)容文本</slot> </button> <!-- 使用 SubmitButton (不傳值) ,按鈕內(nèi)部顯示【這里是默認(rèn)內(nèi)容文本】--> <SubmitButton /> <!-- 使用 SubmitButton (傳值) ,按鈕內(nèi)部顯示【Save】--> <SubmitButton>Save</SubmitButton>
2、具名插槽
組件內(nèi)插入 <slot> 標(biāo)簽時(shí)加上一個(gè) name 屬性,區(qū)分不同的插槽,可以有多個(gè)。
<slot name="header"></slot>
3、具名作用域插槽
感覺(jué)跟具名插槽就是同一個(gè)東西,就是可以從子組件帶參數(shù)過(guò)來(lái),只能在指定的插槽里面用。
<!-- 子組件 MyComponent -->
<template>
<slot name="header" message="hello header"></slot>
<slot message="hello default"></slot>
<slot name="footer" message="hello footer"></slot>
</template>
<!-- 父組件 -->
<!-- headerProps, defaultProps, footerProps 是一個(gè)對(duì)象 ,返回以對(duì)應(yīng) slot 上 { 屬性: 屬性值 } 形式的數(shù)據(jù) -->
<MyComponent>
<template #header="headerProps">
{{ headerProps.message }}
</template>
<template #default="defaultProps">
{{ defaultProps.message }}
</template>
<template #footer="footerProps">
{{ footerProps.message }}
</template>
<!-- 解構(gòu)取值的 ?? -->
<template #footer="{ message }">
{{ message }}
</template>
</MyComponent>五、封裝一個(gè)組件注意
1、組件編碼規(guī)范
使用
PascalCase形式的組件名稱(chēng),提高了模板的可讀性,方便區(qū)分vue component和原生HTML DOM組件名格式給事件命名可以使用
camelCase和kebab-case(短橫線(xiàn)連字符) 形式,使用時(shí)用kebab-case(短橫線(xiàn)連字符) 形式,使用camelCase并沒(méi)有太多優(yōu)勢(shì),推薦更貼近HTML的kebab-case書(shū)寫(xiě)風(fēng)格。
2、可復(fù)用性
- 組件內(nèi)部樣式可覆蓋,盡可能不要寫(xiě)死,提供參數(shù)去修改
- 組件內(nèi)部頁(yè)面展示更加靈活,在合適的地方提供插槽,讓組件內(nèi)布局可控、更加靈活
總結(jié)
到此這篇關(guān)于vue3學(xué)習(xí)筆記之自定義組件的文章就介紹到這了,更多相關(guān)vue3自定義組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli+iview項(xiàng)目打包上線(xiàn)之后圖標(biāo)不顯示問(wèn)題及解決方法
這篇文章主要介紹了解決vue-cli+iview項(xiàng)目打包上線(xiàn)之后圖標(biāo)不顯示問(wèn)題,本文通過(guò)兩種方法給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
Vue 實(shí)現(xiàn)可視化拖拽頁(yè)面編輯器
這篇文章主要介紹了Vue 實(shí)現(xiàn)可視化拖拽頁(yè)面編輯器的方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下2021-02-02
vue2+elementui進(jìn)行hover提示的使用
本文主要介紹了vue2+elementui進(jìn)行hover提示的使用,主要分為外部和內(nèi)部,具有一定的參考價(jià)值,感興趣的可以了解一下2021-11-11
vue-router實(shí)現(xiàn)嵌套路由的講解
今天小編就為大家分享一篇關(guān)于vue-router實(shí)現(xiàn)嵌套路由的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
詳解使用Vue.Js結(jié)合Jquery Ajax加載數(shù)據(jù)的兩種方式
本篇文章主要介紹了詳解使用Vue.Js結(jié)合Jquery Ajax加載數(shù)據(jù)的兩種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
純JS如何實(shí)現(xiàn)vue.js下的雙向綁定功能
對(duì)于vue下的雙向綁定功能,個(gè)人理解為在處理邏輯的過(guò)程中緩存了大量的node對(duì)象,node對(duì)象可以是html標(biāo)簽、文本內(nèi)容。既然選擇了緩存這些對(duì)象,那么在用的過(guò)程中哪里需要改變就把node拿出來(lái),進(jìn)行標(biāo)簽屬性的變更或者文本內(nèi)容的修改。本文主要講了如何實(shí)現(xiàn)雙向綁定2021-06-06
基于vue監(jiān)聽(tīng)滾動(dòng)事件實(shí)現(xiàn)錨點(diǎn)鏈接平滑滾動(dòng)的方法
本篇文章主要介紹了基于vue監(jiān)聽(tīng)滾動(dòng)事件實(shí)現(xiàn)錨點(diǎn)鏈接平滑滾動(dòng)的方法,非常具有實(shí)用價(jià)值,有興趣的可以了解一下2018-01-01

