如何建立一個通用的組件
⚙️

如何建立一個通用的組件

Tags
Component Design
Author
Published
Published October 26, 2022
某天,你在寫的專案,需求說想要之前有做的一個小功能,你做出來但被打槍說跟以前的有點不一樣,以前的比較好,於是你想要使用複製貼上大法(我就爛),但你發現,你不知道從何處開始複製起…

我對通用組件的想像

可複用

都把它拆成組件了,不外乎就是想要他在未來的某一天,成為拯救你專案時程的完美配方。
如何讓組件在不同的地方都可以正常運行,並且在各種情形下都可以掌握他的運行方式。

可維護

需求天天變,程式天天改,讓工程師可以放心跟 PM 說出 沒問題,這改很快,具備可快速更改的程式碼我相信是各位夢寐以求的項目吧。
藉由props、slot去控制該組件本身的功能及呈現,在統一的規格上套上了可客製化的項目。

低耦合

我想要的只是一張顯卡,而不是要買到一整組的電腦RRRR。
通用型的組件一大特點,幾乎沒有業務邏輯,在使用時也不會影響到業務流程,可以做為祖傳組件一路流傳下去。

職責單一

通用組件能夠控制的只有他自己的東西,不管是資料的儲存,或是狀態的切換,基本上都只能改變自己,不屬於自己的東西,就必須傳遞出去讓其他人(e.g. 含有業務邏輯的組件) 做處理。
 
這些特點看下來,其實通用型組件的成形不會太大,也因為沒有業務邏輯,所以不會是流程中的一部份,沒有他不會怎樣,但有了他,使用者體驗會更好,以下會以常見的複製功能來做示範。

複製功能通用組件

此為用 vue3 所製作

所要達成的功能

  1. 複製項目 (主要功能)
  1. 複製網址 (主要功能)
  1. 可以更換複製的圖示或文字 (客製化)
  1. 已經複製成功顯示 (增加使用者體驗)
 

複製組件的畫面

要讓使用者知道這邊可以複製,促使使用者可以點擊他,另一方面,不同地方提醒使用者可以複製的畫面不盡相同,有的是有圖示,有的是一串文字本身就可以點擊,於是這邊就需要用slot去讓外層可以自行設計他的畫面,組件本身只負責點擊,以及實作複製時所需要的input。
<div id="copied" @click.stop="handleCopy"> <slot></slot><!-- 提供給父層客製化 --> <input class="hidden" ref="copiedInput" readonly /> </div>

複製時所需要的資料

組件需要知道要複製得資料,以及想要客製化複製成功時的文字敘述等。
const props = defineProps({ // 要複製的項目 target: { type: String, default: '', }, // 複製項目的名稱 targetName: { type: String, default: '網址', }, // 複製修飾文字 text: { type: String, default: '已複製', }, })

點擊事件

最重要的功能,在使用者點擊下去時要做的動作,流程大概是
  1. 將 props 的 target 資料放到 input,target 沒有資料就放入該頁的網址
  1. 運用 document 的複製功能將其資料複製
  1. 顯示客製化的複製成功提示告知使用者複製成功
const copiedInput = ref(null) const handleCopy = () => { copyUrl() } const copyUrl = () => { const _input = copiedInput.value _input.value = props.target || window.location.href _input.select() _input.setSelectionRange(0, 99999) document.execCommand('copy') showToast() } const showToast = () => { alert(`${props.targetName}${props.text}`) }

最後隱藏HTML的Input

因為 html 中的 input 只有給JS做複製使用,使用者並不需要知道他的存在,畫面也不需要給他位置,於是需要寫一個隱藏的css把它藏起來。
#copied { position: relative; .hidden { opacity: 0; right: 200vw; position: absolute; pointer-events: none; } }

結語

通用組件其實都只是小元件,用途大多是提升使用者體驗,像是複製、圖片顯示、scroll bar客製化等,既可以讓網站變得更好看,又不會破壞整體的統一性,更重要的,可以重複使用,節省大量工時,讓工程師可以專注在其他困難的業務邏輯上,這麼划算的事情,何樂而不為呢?

源碼

<script setup lang="ts"> import { ref } from 'vue' const props = defineProps({ // 要複製的項目 target: { type: String, default: '', }, // 複製項目的名稱 targetName: { type: String, default: '網址', }, // 複製修飾文字 text: { type: String, default: '已複製', }, }) const copiedInput = ref(null) const handleCopy = () => { copyUrl() } const copyUrl = () => { const _input = copiedInput.value _input.value = props.target || window.location.href _input.select() _input.setSelectionRange(0, 99999) document.execCommand('copy') showToast() } const showToast = () => { alert(`${props.targetName}${props.text}`) } </script> <template> <div id="copied" @click.stop="handleCopy"> <slot></slot> <input class="hidden" ref="copiedInput" readonly /> </div> </template> <style lang="scss" scoped> #copied { position: relative; .hidden { opacity: 0; right: 200vw; position: absolute; pointer-events: none; } } </style>

參考資料