最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【Vue3 基礎(chǔ)】05.組件化

2023-07-26 08:08 作者:ChatGPT云炬學(xué)長  | 我要投稿

這是 Vue3 + Vite + Pinia +TS + Element-Plus 實戰(zhàn)系列文檔。最近比較忙沒什么時間寫文章,爭取早日把這個系列完結(jié)吧~

生命周期和模板引用

在本章之前,我們通過響應(yīng)式 api 和聲明式渲染,處理了 DOM 的更新,但光是這些,對于一些復(fù)雜的需要手動操作 DOM 的情況,之前介紹的就無法滿足了。

生命周期

每個 Vue 組件在創(chuàng)建時經(jīng)歷的一系列初始化步驟的階段,我們需要在這些階段做額外操作的話,需要調(diào)用對應(yīng)階段的鉤子。

這些階段包括:設(shè)置好數(shù)據(jù)偵聽,編譯模板,掛載實例到 DOM,以及在數(shù)據(jù)改變時更新 DOM 等。Vue 官方給出了圖示,可以幫助我們更好的理解生命周期:

  1. setup:紅色的所有生命周期 API 都在組件的 setup() 階段被同步調(diào)用。

  2. 紅色方框:不同階段代表的生命周期,后續(xù)我們寫的 生命周期鉤子 會在此階段執(zhí)行。

  3. 主軸上的:代表組件從初始化到卸載的主要事件。

這里我們先簡單介紹,在介紹完生命周期鉤子之后,相信你會更理解這張圖。

生命周期鉤子

了解了上述的生命周期,我們想在對應(yīng)的周期做一些事情的話,在 Vue3 中我們使用 onXxx 的生命周期鉤子,例如:

<script setup>

import { onMounted, onBeforeUnmount } from 'vue'

onMounted(() => {

})

onBeforeUnmount(()=>{

})

</script>

如果你使用過 Vue2 的話,你會發(fā)現(xiàn)差別:

<script>

export default {

created() {

},

mounted() {

},

beforeDestroy() {

}

}

</script>

使用方法就如前面寫的,在 setup 中,在生命周期 API 中注入回調(diào)就可以了。這里我們就不去做 Vue2 和 Vue3 的對比了,全當(dāng)新學(xué)的,按照生命周期的順序:

  1. setup:beforeCreate 和 created 被 setup 方法代替。

  2. onBeforeMount():在組件被掛載之前執(zhí)行回調(diào)。組件已經(jīng)完成了其響應(yīng)式狀態(tài)的設(shè)置,但還沒有創(chuàng)建 DOM 節(jié)點。

  3. onMounted():在組件掛載完成后執(zhí)行回調(diào)。通常用于執(zhí)行需要訪問組件所渲染的 DOM 樹相關(guān)的副作用。

  4. onBeforeUpdate():在組件即將因為響應(yīng)式狀態(tài)變更而更新其 DOM 樹之前執(zhí)行回調(diào)。通常用來在 Vue 更新 DOM 之前訪問 DOM 狀態(tài)。

  5. onUpdated():在組件因為響應(yīng)式狀態(tài)變更而更新其 DOM 樹之后執(zhí)行回調(diào)。會在組件的任意 DOM 更新后被調(diào)用,一般用來訪問更新后的 DOM,不能在此做更新 DOM 的操作,可能導(dǎo)致循環(huán)。

  6. onBeforeUnmount():在組件實例被卸載之前執(zhí)行回調(diào)。

  7. onUnmounted():在組件實例被卸載后執(zhí)行回調(diào)。通常用于手動清理一些副作用,例如計時器、DOM 事件監(jiān)聽器或者與服務(wù)器的連接。

  8. onActivated():當(dāng)作為 keep-alive 組件被激活時執(zhí)行回調(diào)。

  9. onDeactivated():當(dāng)作為 keep-alive 組件被取消激活時執(zhí)行回調(diào)。

  10. onErrorCaptured():在捕獲了后代組件傳遞的錯誤時執(zhí)行回調(diào)。通常用來更改組件狀態(tài)來為用戶顯示一個錯誤狀態(tài)。

  11. onRenderTracked()?僅開發(fā)模式使用 ?:當(dāng)組件渲染過程中追蹤到響應(yīng)式依賴時執(zhí)行回調(diào)。通常用于追蹤依賴的調(diào)試。

  12. onRenderTriggered()??僅開發(fā)模式使用 ?:當(dāng)響應(yīng)式依賴的變更觸發(fā)了組件渲染時執(zhí)行回調(diào)。通常用于觸發(fā)更新的調(diào)試。

以上就是所有的生命周期以及其可被調(diào)用的生命周期鉤子,我們在上述鉤子中傳遞回調(diào),Vue 會在其所在的生命周期觸發(fā)。

模板引用 ref

ref 用于注冊元素或者子組件的引用。

模板引用將存儲在與名字匹配的 ref 中,例如想在數(shù)據(jù)加載完之后,更改文字信息或描述信息:

<script setup>

import { ref, onMounted } from "vue";

const h2 = ref(null);

const img = ref(null);

onMounted(() => {

setTimeout(() => {

h2.value.textContent = "數(shù)據(jù)加載完成";

img.value.src = "/src/assets/logo.svg";

}, 3000);

});

</script>

<template>

<h2 ref="h2">數(shù)據(jù)加載中...</h2>

<img ref="img" src="./assets/load.svg" alt="" />

</template>

當(dāng)然我們也完全可以讓上述的代碼中 h2 變成響應(yīng)式的,并在h2中使用 {{}} ?模板語法實現(xiàn)。

組件引用ref

ref 也可以使用在子組件上,相對來說也是較為常見的用法。

首先解釋什么是組件:

在此之前我們都是使用的一個單文件App.vue,如果一個項目將代碼全寫在這一個文件,那將非常難維護(hù),于是我們把可復(fù)用等的頁面組件化,頁面和邏輯抽離,通過導(dǎo)入組件被其它頁面引用。

<script setup>

import Child from './Child.vue'

</script>

<template>

<Child ref="child" />

</template>

使用 ref,父組件能獲取子組件示例:

<script setup>

import { ref, onMounted } from 'vue'

import Child from './Child.vue'

const child = ref(null)

onMounted(() => {

})

</script>

<template>

<Child ref="child" />

</template>

需要注意的是,子組件沒有使用 <script setup> ?,被引用的組件實例和該子組件的 this 完全一致,父組件擁有對子組件的每個屬性和方法的訪問權(quán)。

如果使用 <script setup> ?那么子組件默認(rèn)私有,除非使用 defineExpose ?顯式暴露。

當(dāng)然在大部分情況下,使用 props 和 emit 就能實現(xiàn)父子組件的交互,而無需使用 ref。

組件傳值 props

組件之間傳值的方式主要可以概括為這三類:父子組件傳值、兄弟組件傳值和遠(yuǎn)親組件傳值。

Vue 提供給我們組件傳值的api有兩種:props、emit。其中我們可以通過 props ?進(jìn)行父=>子組件傳值。

在開發(fā)過程中,我們需要通過 defineProps() 明確子組件的 props。父組件可以像聲明 HTML 參數(shù)一樣傳值,也可以使用 : ?(v-bind 簡寫) 動態(tài)傳值:

<!-- 父組件 -->

<script setup>

import { ref } from "vue";

import Children from "./components/Children.vue";

const hello = ref("Hello");

</script>

<template>

<input v-model="hello" />

<Children msg="hhh" :activeMsg="hello" />

</template>

<!-- 子組件 -->

<script setup>

import { onMounted } from "vue";

const props = defineProps({

msg: String,

activeMsg: String

});

onMounted(() => {

console.log("props", props);

});

</script>

<template>

<span>msg: {{ msg }}</span>

<span>activeMsg: {{ activeMsg }}</span>

</template>

defineProps() 聲明之后,其中的數(shù)據(jù)就可以在子組件模板中使用。在 JavaScript 中訪問則需要通過 ?defineProps() ? 返回的對象訪問。

注意:

  1. props 是只讀的,遵循單項數(shù)據(jù)流,當(dāng)嘗試修改 props 會警告 prop 只讀。

  2. js 中定義數(shù)據(jù)與 props 中重名時,使用的是 js 中定義的。

const activeMsg = "hl";

<span>activeMsg: {{ activeMsg }}</span>

  1. props 提供校驗選項,保證項目沒有使用 TypeScript 進(jìn)行類型檢測,也可以確定一定的數(shù)據(jù)類型,避免不不滿足類型要求的數(shù)據(jù)傳入。(現(xiàn)在 Vue3 支持 TypeScript 可以說這個用處不大了)

interface DataProps {

msg: String;

activeMsg: String;

}

const props = defineProps<DataProps>();

組件監(jiān)聽事件 emit

子組件使用 emit() ?向父組件傳遞數(shù)據(jù)。第一個參數(shù)是事件名稱,其它額外參數(shù)都會被直接傳向父組件的監(jiān)聽器函數(shù)。

父組件使用 @ (v-on)監(jiān)聽子組件時間,并且可以接收子組件傳遞的參數(shù)。

<!-- 父組件 -->

<template>

<Children @response="(msg) => (hello = msg)" />

{{ hello }}// 點擊子組件按鈕后變?yōu)?hello from child

</template>

<script setup>

import { ref } from "vue";

import Children from "./components/Children.vue";

const hello = ref("Hello");

</script>

<!-- 子組件 -->

<template>

<button @click="emit('response', 'hello from child')">emit</button>

</template>

<script setup>

const emit = defineEmits(["response"]);

</script>

注意:

  1. 在模板中可以使用 $emit 的語法,js中只能使用 defineEmits 返回對象 emit ?觸發(fā)事件。

  2. 可以使用類型標(biāo)注觸發(fā)事件,對觸發(fā)的事件有更精準(zhǔn)的控制。

const emit = defineEmits<{

(e: 'response', msg: string): void

}>()

傳遞模板-插槽 slot

除了傳遞數(shù)據(jù)外,父組件還可以通過插槽 slot ?的方式將模板傳遞給子組件。

<!-- 父組件 -->

<template>

<Children>slot button content</Children>

</template>

<script setup>

import Children from "./components/Children.vue";

</script>

<!-- 子組件 -->

<template>

<button><slot /></button> ?

</template>

<script setup></script>

默認(rèn)內(nèi)容

如果想設(shè)置默認(rèn)內(nèi)容的話,比如在父組件不向子組件傳遞模板字符,而使用子組件按鈕內(nèi)容有默認(rèn) content:

<!-- 父組件 -->

<template>

<Children></Children>

</template>

<script setup>

import Children from "./components/Children.vue";

</script>

<!-- 子組件 -->

<template>

<button>

<slot>

content

</slot>

</button>

</template>

<script setup></script>

具名插槽

如果組件包含多個插槽出口,則需要使用具名插槽,用來給插槽一個唯一 ID,以確定不同出口要渲染的內(nèi)容。

<!-- 父組件 -->

<template>

<Children>

<template v-slot:header> Header </template>

<template v-slot:button> slot button content </template>

</Children>

</template>

<script setup>

import Children from "./components/Children.vue";

</script>

<template>

<div><slot name="header" /></div>

<button><slot name="button" /></button>

</template>

<script setup></script>

v-slot 可以簡寫為 # ,v-slot:header => #header。并且v-slot 也可以接受動態(tài)參數(shù)(動態(tài)插槽名): #[dynamicSlotName] 。

作用域插槽

上述的幾種插槽,無法訪問到子組件的狀態(tài),在某些場景中我們想要子組件傳遞數(shù)據(jù)給插槽,作用域插槽就可以滿足這個需求。

<!-- 父組件 -->

<template>

<Children>

<template v-slot:header="slotProps"> {{ slotProps.msg }}</template>

</Children>

</template>

<script setup>

import Children from "./components/Children.vue";

</script>

<!-- 子組件 -->

<template>

<input type="text" v-model="msg" />

<div><slot name="header" :msg="msg" /></div>

</template>

<script setup>

import { ref } from "vue";

const msg = ref("");

</script>

準(zhǔn)確來說,上述例子是具名作用域插槽。如果是普通的作用域插槽,即改變 template 的具名插槽為普通插槽就可以了。

實戰(zhàn)

前幾節(jié)講到了組件的各生命周期鉤子、組件傳值的 props、觸發(fā)事件的 emit 以及模板插槽 slot,用的例子比較簡單,我們通過實戰(zhàn)體驗一下在實戰(zhàn)開發(fā)中的運用,幫助我們更好的理解和運用。

博客的列表展示功能,提供分類功能。

  1. 分類的數(shù)據(jù)來源于父組件,其選擇的類型通過回調(diào)告知父組件。

  2. 列表部分父組件做元素內(nèi)容和樣式的控制,子組件做列表的基礎(chǔ)循環(huán)等的通用操作。

示例盡可能的包含到我們這章所學(xué)知識,如下述代碼部分看不懂,代表你那部分知識還沒理解清楚。

本節(jié)中的例子里包含部分ts的類型確認(rèn)

父組件?App.vue:

<script setup>

import { reactive, ref, onMounted } from "vue";

import Children from "./components/Children.vue";

import ClassifyHeader from "./components/ClassifyHeader.vue";

const tags = reactive({

list: ["vue", "react"],

checked: [],

});

function checkedTags(checked) {

tags.checked = checked;

getData();

}

onMounted(() => {

getData();

});

const listRef = ref();

function getData() {

const params = { tags: tags.checked, page: 1 };

console.log("請求參數(shù):", params);

setTimeout(() => {

const data = [

{ title: "JavaScript 入門到精通", username: "Chocolate 1999", date: "2023-02-11" },

{ title: "Vue3 實戰(zhàn)", username: "HearLing", date: "2023-03-09" },

];

listRef.value.loadData(data);

}, 1000);

}

</script>

<template>

<ClassifyHeader :tags="tags.list" @select="checkedTags" />

<Children ref="listRef">

<template #item="{ title, username, date }">

<div>

<p>{{ title }}</p>

<p>作者:{{ username }} | 時間:{{ date }}</p>

</div>

</template>

</Children>

</template>

<style scoped></style>

父組件涉及知識點:

  1. 與 ClassifyHeader ?分類組件的傳值。props 和 emit。

  2. 生命周期 onMounted。在組件掛載后請求數(shù)據(jù)。

  3. 組件引用 ref 。使用子組件的方法。

  4. 具名作用域插槽。

父組件和分類組件

在父組件中,初始的?tags.list?值通過 props 傳遞給子組件,同時監(jiān)聽了 select 事件。select 觸發(fā)會執(zhí)行 checkedTags 函數(shù),父組件得到 checked 值,并做出重新請求列表數(shù)據(jù)的操作。

components/ClassifyHeader.vue?分類組件:

<!-- 分類 -->

<template>

<div v-for="item in props.tags">

<input type="checkbox" :value="item" @click="select(item)" />

{{ item }}

</div>

</template>

<script setup>

const props = defineProps(["tags"]);

const emit = defineEmits<{

(e: "select", checked: string[]): void;

}>();

const checked: string[] = [];

function select(item) {

const index = checked.indexOf(item);

if (index !== -1) {

checked.splice(index, 1);

} else {

checked.push(item);

}

emit("select", checked);

}

</script>

<style scoped></style>

ClassifyHeader ?分類組件主要就是記錄所選類別,并通過 emit 傳遞數(shù)據(jù)給父組件。

父組件與列表組件

父組件通過 ref 獲取到子組件示例,并且使用子組件暴露的 loadData 方法加載數(shù)據(jù)。子組件通過作用域插槽,傳遞數(shù)據(jù)給父組件,父組件控制內(nèi)容布局。

components/Children.vue?列表組件:

<!-- 子組件 -->

<script setup>

import { ref } from "vue";

interface Item {

title: string;

username: string;

date: string;

}

const items = ref<Item[]>([]);

const loadData = (data) => {

items.value = data;

};

defineExpose({

loadData,

});

</script>

<template>

<ul>

<li v-if="!items.length">Loading...</li>

<li v-for="item in items">

<slot name="item" v-bind="item" />

</li>

</ul>

</template>

<style scoped></style>

子組件,提供具名插槽 slot 以及用 defineExpose 拋出 loadData 方法供父組件通過組件示例拿到。

上述例子,我們就把大部分的知識都重新實戰(zhàn)復(fù)習(xí)了一遍,建議可以把這幾個代碼自己寫一遍,加深印象。

其它傳值方式

除了上述的 props、emit 父子組件傳值之外。還可以使用依賴注入 API :provide、inject。

  1. provide():提供一個值,可以被后代組件注入。使用方式:provide(/* 注入名?/ 'message', /?值 */ 'hello!') 。

  2. inject():注入上層組件提供的數(shù)據(jù)。使用方式:const message = inject('message') 。

兄弟組件,可以可以通過父組件控制數(shù)據(jù)傳值。

對于跨組件通信,我們可以使用狀態(tài)管理工具,比如我們后面要學(xué)的 Pinia,就是一個狀態(tài)管理框架。

總結(jié)

本章中,我們首先結(jié)合 Vue 的生命周期的流程圖,例舉了各個生命周期鉤子的觸發(fā)時機,以及部分鉤子的使用場景。然后講到了 ref 的作用,最后講完并實戰(zhàn)了組件通信相關(guān)的 api。

至此 Vue3 的基礎(chǔ)知識到這里已經(jīng)結(jié)束了,還剩下一小部分,留在我們實戰(zhàn)課程中探索。

如果未學(xué)習(xí)過TypeScript的話,我也準(zhǔn)備了 TypeScript 相關(guān)的入門基礎(chǔ)知識,課程不會很長,帶大家了解 TypeScript 常用的一些知識,為實戰(zhàn)做準(zhǔn)備。


【Vue3 基礎(chǔ)】05.組件化的評論 (共 條)

分享到微博請遵守國家法律
崇明县| 旬邑县| 眉山市| 临沭县| 韶关市| 镇巴县| 揭阳市| 漳平市| 伊金霍洛旗| 牙克石市| 台中市| 渭源县| 彭泽县| 镶黄旗| 富顺县| 泸定县| 玉山县| 新巴尔虎左旗| 贵州省| 永寿县| 沾益县| 舒兰市| 武胜县| 平和县| 桓仁| 栖霞市| 昌平区| 廉江市| 长阳| 武穴市| 南和县| 昌乐县| 平安县| 巢湖市| 昭通市| 安达市| 襄樊市| 城口县| 蒲城县| 汾西县| 吴忠市|