Vue3封裝全局函數(shù)式組件方法總結(jié)
前言
相信大家在 Vue 中考慮復(fù)用邏輯的時(shí)候經(jīng)常使用組件化開發(fā),也肯定使用過函數(shù)式組件,就是那種在 js 中也能夠?qū)胝{(diào)用的組件。那么如何去封裝這么一個(gè)函數(shù)式組件呢,這篇文章將采用Toast組件簡單介紹一下封裝的方法,封裝之后就能大大提高我們開發(fā)的效率了。
一、函數(shù)式組件是什么?
簡單介紹一下聲明式組件與函數(shù)式組件,大多數(shù)時(shí)候我們引入組件都采用聲明式的的方式,這里以 Vant 組件庫為例,類似 Button 按鈕這種就是聲明式組件:
<van-button type="primary">主要按鈕</van-button>
還有類似 <TheWelcome />
這種自定義名稱且在 .vue 文件里引用其他 .vue 文件的就是聲明式組件
<template> <main> <TheWelcome /> </main> </template> <script setup lang="ts"> import TheWelcome from '../components/TheWelcome.vue'; </script>
而函數(shù)式組件則是通過調(diào)用 API 的方式快速喚起全局的組件,還是以 Vant 組件庫為例,比如使用 Toast 組件,調(diào)用函數(shù)后會(huì)直接在頁面中渲染對應(yīng)的輕提示:
import { showToast } from 'vant'; showToast('提示內(nèi)容');
通常我們使用函數(shù)式組件是在某個(gè)交互完成時(shí)觸發(fā),又或者是在非.vue文件
里喚起全局的組件,例如封裝axios,在axios.js中使用Toast組件顯示報(bào)錯(cuò)信息:
showToast('服務(wù)器響應(yīng)超時(shí),請刷新當(dāng)前頁');
二、創(chuàng)建一個(gè)函數(shù)式組件
下面將創(chuàng)建一個(gè)自己定義的toast組件,由于這個(gè)toast組件默認(rèn)是顯示成功的,所以稱之為“okToast”,先展示一下調(diào)用后的效果:
1. 封裝toast組件
與創(chuàng)建聲明式組件一致,在.vue文件里定義好組件接收的參數(shù)還有組件的樣式。代碼如下(示例):
<template> <!-- 加一點(diǎn)動(dòng)畫效果 --> <transition name="toast" @after-leave="onAfterLeave"> <div class="toast" v-if="isShow" :style="{ width: toastWidth }"> <!-- 手動(dòng)點(diǎn)擊隱藏彈窗 --> <div v-if="time < 0" class="cancel" @click="hidden"></div> <img v-if="type === 'success' || type === 'icon'" class="img" src="../../assets/images/7vip_web_toast_finish_icon_40x40@2x.png" alt="success" /> <img v-if="type === 'warn'" class="img" src="../../assets/images/7vip_web_toast_warn.png" alt="warn" /> <div v-if="content && type !== 'icon'" class="content" :style="{ textAlign }">{{ content }}</div> </div> </transition> </template> <script setup> import { ref, computed } from "vue"; const props = defineProps({ //文案內(nèi)容,默認(rèn)success content: { type: String, default: "success", }, //顯示時(shí)間,默認(rèn)2s,傳小于0的值不自動(dòng)消失,需要手動(dòng)關(guān)閉 time: { type: Number, default: 2000, }, //寬度,默認(rèn)310px,這里考慮傳入的寬度可以用字符串也可以用數(shù)值,所以沒有定義類型 width: { default: 310, }, //彈窗文案文本對齊方式,默認(rèn)center textAlign: { type: String, default: "center", }, //類型,默認(rèn)圖標(biāo)(√),傳'warn'顯示(?。瑐髌渌祫t不顯示icon,傳'icon'不顯示文本 type: { type: String, default: "success", }, //接收的函數(shù)方法 hide: { type: Function, default: () => {}, }, }); // 彈窗顯隱控制 const isShow = ref(false); // 寬度控制,由于設(shè)計(jì)稿寬度是750px的寬度,這里通過計(jì)算屬性,根據(jù)設(shè)備屏幕寬度自適應(yīng)顯示彈窗的寬度 const toastWidth = computed(() => (parseInt(props.width.toString()) / 750) * document.documentElement.clientWidth + "px"); // 顯示彈窗方法 const show = () => { isShow.value = true; if (props.time >= 0) { setTimeout(() => { isShow.value = false; }, props.time); } }; // 隱藏彈窗方法 const hidden = () => { isShow.value = false; }; // 彈窗關(guān)閉后等動(dòng)畫結(jié)束再調(diào)用卸載邏輯 const onAfterLeave = () => { props.hide(); }; // 將顯示彈窗方法暴露出去 defineExpose({ show, }); </script> <style lang="scss" scoped> .toast-enter-active, .toast-leave-active { transition: opacity 0.3s ease-out; } .toast-enter-from, .toast-leave-to { opacity: 0; } .toast { position: fixed; top: 45%; left: 50%; transform: translate(-50%, -50%); z-index: 99; background: #333333; border-radius: 20px; padding: 40px; text-align: center; .cancel { background: url("../../assets/images/quxiao@2x.png") no-repeat center / contain; position: absolute; top: 10px; right: 10px; width: 40px; height: 40px; &::before { content: ""; position: absolute; top: -10px; right: -10px; bottom: -10px; left: -10px; } } .img { width: 80px; height: 80px; } .content { margin-top: 20px; font-size: 32px; color: #ffcc99; line-height: 30px; text-align: initial; } } </style>
2. 創(chuàng)建應(yīng)用實(shí)例
這是最關(guān)鍵的步驟,在 Vue2 的時(shí)候封裝函數(shù)式組件使用的是 Vue.extend
,利用這個(gè)基礎(chǔ)的 Vue 構(gòu)造器,能創(chuàng)建Vue子類實(shí)例,然而在 Vue3 官方刪除了這個(gè)方法,但是也提供了新的api: createApp
給我們使用,利用 createApp 就能創(chuàng)建 Vue 應(yīng)用實(shí)例了。代碼如下(示例):
import { createApp } from "vue"; import OkToast from "./okToast.vue"; const okToast = options => { // 創(chuàng)建元素節(jié)點(diǎn) const rootNode = document.createElement("div"); // 在body標(biāo)簽內(nèi)部插入此元素 document.body.appendChild(rootNode); // 創(chuàng)建應(yīng)用實(shí)例(第一個(gè)參數(shù)是根組件。第二個(gè)參數(shù)可選,它是要傳遞給根組件的 props) const app = createApp(OkToast, { ...options, hide() { // 卸載已掛載的應(yīng)用實(shí)例 app.unmount(); // 刪除rootNode節(jié)點(diǎn) document.body.removeChild(rootNode); }, }); // 將應(yīng)用實(shí)例掛載到創(chuàng)建的 DOM 元素上 return app.mount(rootNode); }; // 注冊插件app.use()會(huì)自動(dòng)執(zhí)行install函數(shù) okToast.install = app => { // 注冊全局屬性,類似于 Vue2 的 Vue.prototype app.config.globalProperties.$okToast = options => okToast(options).show(); }; // 定義show方法用于直接調(diào)用 okToast.show = options => okToast(options).show(); export default okToast;
3. 注冊插件(可省略)
代碼如下(示例):
// main.js import okToast from './plugins/okToast/index'; app.use(okToast);
Q&A: 補(bǔ)充一些注釋
①:為什么采用調(diào)用函數(shù)方法的方式去控制顯隱
答:目的是為了那個(gè)顯示與消失的動(dòng)畫效果,當(dāng)組件創(chuàng)建后需要組件內(nèi) ”isShow“ 產(chǎn)生變化才能觸發(fā)<Transition>
的動(dòng)畫效果,所以這里寫了show函數(shù)方法。
②:函數(shù)式組件的這兩個(gè)文件之間的聯(lián)系
答:簡單來說,js文件傳參數(shù)及函數(shù)給vue文件,均可在 createApp
的第二個(gè)參數(shù)中傳遞,vue文件相當(dāng)于子組件,使用props的方式接收;vue文件傳值及函數(shù)給js文件,可以通過 defineExpose
方法暴露出去,js文件中在應(yīng)用實(shí)例創(chuàng)建完成后,就能拿到暴露出來的屬性及方法。
三、調(diào)用
1. 注冊插件后在.vue文件內(nèi)獲取全局方法
<script setup> import { getCurrentInstance } from 'vue'; // 獲取當(dāng)前實(shí)例,在當(dāng)前實(shí)例相當(dāng)于 vue2 中的 this const { proxy }: any = getCurrentInstance(); // 最簡單的調(diào)用方式,即可出來開頭所展示的效果 proxy.$okToast(); // 傳遞自定義參數(shù),與okToast.vue文件接收的參數(shù)對應(yīng) setTimeout(() => { proxy.$okToast({ content: 'Hello World' }); }, 2000); </script>
2. 可不注冊插件,在.vue或.js文件內(nèi)直接調(diào)用方法
import $okToast from "./plugs/okToast"; $okToast.show({ type: "warn", content: "Network error,try again later", });
四、優(yōu)化改進(jìn)
上面封裝的Toast組件在創(chuàng)建多個(gè)實(shí)例的時(shí)候,它們之間是互不干擾的,不會(huì)存在組件參數(shù)異常的情況。那么實(shí)際觀察 DOM 元素我們會(huì)發(fā)現(xiàn)其在 DOM 上是存在多個(gè)的,只不過當(dāng)多次調(diào)用的時(shí)候,后面的會(huì)把前面還沒消失的Toast覆蓋了,這樣效果可能不那么友好。那么就存在兩個(gè)優(yōu)化方向:一是當(dāng)后續(xù)出現(xiàn)Toast的時(shí)候結(jié)束掉前面出現(xiàn)的Toast,二是調(diào)整后續(xù)Toast出現(xiàn)的位置。
1、單例模式(推薦)
先上代碼(示例):
let rootNode = null; let app = null; const okToast = options => { const dom = document.body.querySelector('.my-ok-toast'); if (!dom) { rootNode = document.createElement('div'); // 給創(chuàng)建的元素設(shè)置 class 屬性值 rootNode.className = `my-ok-toast`; document.body.appendChild(rootNode); } else { // If you want to mount another app on the same host container, you need to unmount the previous app by calling `app.unmount()` first. app.unmount(); } app = createApp(OkToast, { ...options, hide() { // 卸載已掛載的應(yīng)用實(shí)例 if (app) { app.unmount(); app = null; } // 刪除rootNode節(jié)點(diǎn) if (rootNode) { document.body.removeChild(rootNode); rootNode = null; } } }); return app.mount(rootNode); };
效果展示:
怎么去結(jié)束前面出現(xiàn)的Toast呢,我們只需要確保全局只渲染一個(gè)Toast彈窗就行,所以可以使用單例模式,單例模式即一個(gè)類只能有一個(gè)實(shí)例。類似Vant的Toast組件,其默認(rèn)采用了單例模式,即同一時(shí)間只會(huì)存在一個(gè),這種做法應(yīng)該是普遍的彈窗做法。
2、多個(gè)提示彈窗
先上代碼(示例):
// 創(chuàng)建臨時(shí)變量保存高度值 let top = 0; const okToast = options => { const rootNode = document.createElement('div'); // 給創(chuàng)建的元素設(shè)置 class 屬性值 rootNode.className = `my-ok-toast`; document.body.appendChild(rootNode); const dom = document.body.querySelector('.my-ok-toast'); // 若DOM中存在該元素則將新元素高度往下移動(dòng) if (dom) { top += 120; rootNode.style.top = 80 + top + 'px'; } const app = createApp(OkToast, { ...options, hide() { app.unmount(); document.body.removeChild(rootNode); } }); return app.mount(rootNode); };
再將css樣式添加到全局上
.my-ok-toast { position: fixed; z-index: 99; top: 80px; left: 50%; transform: translateX(-50%); }
效果展示:
這里的做法提供給大家一種思路,實(shí)際的動(dòng)畫效果還有待優(yōu)化,由于本文篇幅有限所以就不展開了,以后遇到這種需求再深入探索吧。
總結(jié)
以上就是全部內(nèi)容,本文簡單介紹了 Vue3 函數(shù)式組件的封裝方法,將其以插件的方式使用app.use() 方法安裝在 Vue 上,使其作為全局功能的工具,這就是 Vue3 中邏輯復(fù)用的插件 (Plugins) 寫法。
如果此篇文章對您有幫助,歡迎您【點(diǎn)贊】、【收藏】!也歡迎您【評論】留下寶貴意見,共同探討一起學(xué)習(xí)~
擴(kuò)展閱讀
到此這篇關(guān)于Vue3封裝全局函數(shù)式組件方法的文章就介紹到這了,更多相關(guān)Vue3封裝全局函數(shù)式組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3+vite動(dòng)態(tài)加載路由,本地路由和線上路由匹配方式
這篇文章主要介紹了vue3+vite動(dòng)態(tài)加載路由,本地路由和線上路由匹配方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06vue ajax 攔截原理與實(shí)現(xiàn)方法示例
這篇文章主要介紹了vue ajax 攔截原理與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了vue.js基于ajax攔截實(shí)現(xiàn)無刷新登錄的相關(guān)原理與操作技巧,需要的朋友可以參考下2019-11-11Vue3中watch監(jiān)聽器及源碼學(xué)習(xí)
本文主要介紹了Vue3中watch監(jiān)聽器及源碼學(xué)習(xí),Watch偵聽器在Vue3中特性進(jìn)行了一些改變和優(yōu)化,下面來詳解的介紹一下基本使用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01Vue3異步數(shù)據(jù)加載組件suspense的使用方法
前端開發(fā)中異步請求是非常常見的事情,比如遠(yuǎn)程讀取圖片,調(diào)用后端接口等等,這篇文章主要給大家介紹了關(guān)于Vue3異步數(shù)據(jù)加載組件suspense的使用方法,suspense中文含義是懸念的意思,需要的朋友可以參考下2021-08-08