<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Dialog 對話方塊元件幾乎是每個前端專案必不可少的元件,通常是在保留當前頁面狀態並遮蔽其他使用者輸入的情況下,與使用者互動並承載相關操作。
在 BOM 的方法中,alert
、prompt
都是以前用來做類似功能的方法,但是這些瀏覽器內建方法會完全停止網頁程式碼執行,對於貧乏的前端執行緒資源來說實在是過於僵硬。於是便產生了各種各樣的 Js 彈窗。
今天就來談談在前端框架 Vue 2 版本中的彈窗元件的相關實現以及我個人認為的最佳實踐。
對於 Vue2 的 UI 框架,坊間比較火的有 element-ui
antd-vue
vant-ui
等等,不過他們在彈窗的使用方法上幾乎都是一致的。下面我們以 element-ui
中的元件為例講講這些使用方式的缺點。
這種方式不用多講,使用起來肯定是最麻煩的一種,這意味著你每次想使用這個彈窗元件都要寫一遍負責顯隱的狀態以及方法,儘量只在唯一場景裡使用,不具備複用條件。
即是通過往 Vue.prototype
中注入彈窗的方法,使你可以在 vue 上下文中使用 this.$confirm
諸如此類的方法來使用他們的彈窗功能。
這種呼叫方式曾經幾乎是所有 vue2 全域性 api 的解決方法,一時間各種外掛都有了各種各樣的全域性 api。例如 vuex 的 this.$store
、 EventBus 的 this.$bus
等等。
這種方式在使用上雖然非常方便,但也有如下一些缺點:
上下文汙染,一個 vue 的全域性 this
裡面,什麼東西都可以有,不論是全域性方法、還是全域性變數,都可以放在 Vue.prototype
裡面,例如前一個例子,如果複雜的彈窗需要在各種其他前端檔案內開啟,大概率也是用這種方式將彈窗開啟和關閉方法都注入到全域性 this
中使用。
型別丟失,無論你是全域性狀態管理還是事件匯流排,在使用的時候都是各種黑盒,全域性方法來自於什麼外掛、需要傳什麼引數、返回什麼東西,全然無感,只能想辦法尋找蛛絲馬跡去溯源。
只能在 vue 上下文中使用,在 vue 檔案外就無法使用了。就好比 React 無法在 React 上下文之外 setState
一樣難受。
這種方式和第二種幾乎一樣,通過頂層元件 provider
出對應的函數方法,之後在子元件中 inject
進來。 一樣存在著 provider 的上下文汙染,也丟失了型別,並且依舊只能在 vue 上下文中使用,靈活性一樣差。
除了上述所說的問題之外
綜合以上的缺點,我認為一個彈窗最佳的使用方式在 Vue2 中使用方式是這樣:
<template> <button @click="open">開啟彈窗</button> </template> <script> import openDialog from 'my-dialog' export default { methods: { open() { openDialog() .then(() => {}) .catch(() => {}) .finally(() => {}) } } } </script>
也可以在其他檔案中,例如:
// api.ts import openDialog from 'my-dialog' const getUserInfo = () => { return fetch('/xxx/api').then(() => { openDialog('success') }) }
廢話不多說,直接上核心程式碼
// dialog.ts import Vue, { ComponentInstance } from "vue"; import ConfirmDialog from "./confirm-dialog.vue"; export let index = 1000; export const cache = new Set<string>(); export function openDialog(component: ComponentInstance) { const div = document.createElement("div"); const el = document.createElement("div"); const id = 'dialog-' + Math.random(); div.appendChild(el); document.body.appendChild(div); const ComponentConstructor = Vue.extend(component); return (propsData = {}, parent = undefined) => { let instance = new ComponentConstructor({ propsData, parent, }).$mount(el); const destroyDialog = () => { if (cache.has(id)) return; if (instance && div.parentNode) { cache.add(id); (instance as any).visible = false; // 延時是為了在關閉動畫執行完畢後解除安裝元件 setTimeout(() => { cache.delete(id); instance.$destroy(); // @ts-ignore instance = null; div.parentNode && div.parentNode.removeChild(div); }, 1000); } }; // visible控制 if ((instance as any).visible !== undefined) { // 支援sync/v-model instance.$watch("visible", (val) => { !val && destroyDialog(); }); Vue.nextTick(() => ((instance as any).visible = true)); } return new Promise((resolve, reject) => { // emit 一個 done 事件關閉 instance.$once("done", (data: any) => { destroyDialog(); resolve(data); }); // emit 一個 cancel 事件取消 instance.$once("cancel", (data: any) => { destroyDialog(); reject(data); }); }); }; } export function confirmDialog(content: string, editable?: boolean) { return openDialog(ConfirmDialog as any)({ content, editable }); }
使用方式和結果可以通過下面這個例子進行檢視
在例子中,我們可以將任意 vue 元件包裝進 openDialog
Api 中,只需要在元件的 data 裡寫入一個 visible
屬性,而後續在元件方法中呼叫 this.$emits('done')
或者 this.$emits('cancel')
就可以對應 openDialog
方法返回 Promise 的 then
回撥和 catch
回撥。
可以自定義傳參、自定義內容、自定義事件,自定義返回,靈活性直接拉滿。
而且,方法不限於任何上下文,在任何檔案內都可以使用,實現了真正的函數式呼叫 Vue2 的彈窗元件。
但是這個方法確實也有那麼一點點不方便的地方,如果有掘友看得出來,可以在評論下方說說。
這篇文章其實沒多少東西,甚至程式碼都是我一年多前就寫的,在受到 React hooks 的啟發後,我就覺得函數語言程式設計非常的爽,就嘗試將專案中的全域性變數都剝離 vue 上下文,包括 dialog
、message
元件,之後摒棄 vuex 和 pinia 這種強綁上下文的狀態管理庫,改用 Vue.observable
,就可以很方便的將業務與 UI 完全抽離。所有變數和方法都可以通過 import 追溯,更重要的是這種模式更契合 typescript
,型別提示也很輕易跟上來了。
以上就是本篇文章的所有內容了,後續我會封裝 v2 和 v3 的函數式彈窗元件,並行布到 npm 上,到時候再更新連結到文章裡,更多關於Vue2 Dialog彈窗函數式呼叫的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45