《精通react/vue組件設計》之快速實現一個可定製的進度條組件

2020-12-23 酷扯兒

本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫

前言

這篇文章是筆者寫組件設計的第四篇文章,之所以會寫組件設計相關的文章,是因為作為一名前端優秀的前端工程師,面對各種繁瑣而重複的工作,我們不應該按部就班的去"辛勤勞動",而是要根據已有前端的開發經驗,總結出一套自己的高效開發的方法.作為數據驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘代碼, 一切皆組件的思想深得人心.所以, 為了讓工程師們有更多的時間去考慮業務和產品迭代,我們不得不掌握高質量組件設計的思路和方法.所以筆者將花時間去總結各種業務場景下的組件的設計思路和方法,並用原生框架的語法去實現各種常用組件的開發,希望等讓前端新手或者有一定工作經驗的朋友能有所收穫.

今天要來實現一個高可定製的進度條組件,在介紹組件設計之前,我們先牢記以下幾個原則.

每日一學: 組件設計三原則

高內聚, 低耦合(尤其是vue/react組件中, 降低組件之間的耦合尤為重要)組件邊界劃分清晰(每一個組件都有自己清晰的邊界劃分)單一職責(每一個組件只負責某一特定的表現或者功能)

正文

在開始組件設計之前希望大家對css3和js有一定的基礎.我們先看看實現後的組件效果:

上圖可以知道封裝後的進度條組件通過對外暴露的接口(react/vue裡面可以看做props屬性)可以很快的實現多個不同的表現和重用.我將會使用react帶大家實現這個進度條組件, 大家不用擔心技術棧不一樣,因為react實現的組件可以很快套用於vue項目中, 所以說底層原理非常重要.

1. 組件原理和設計思路

由於組件設計的前提還是基於需求, 所以我們第一步是要確認需求. 一個進度條組件一般都會有如下需求點:

通過進度控制進度條長度進度條總長度可以由用戶來控制隨時修改精度條的額顏色(來自於設計師或產品經理獨特而百變的審美)當進度為100%時進度條可以自動消失(可能的需求)進度提示文本(用戶想知道當前長度下的具體進度, 比如體溫計)對於不同的進度節點,需要有不同的進度條顏色(比如遊戲人物裡的血, 快沒血的時候為紅色, 血滿的時候為藍色)需求收集好之後,作為一個有追求的程式設計師, 會得出如下線框圖:

這也是一個健壯的react/vue組件應有的思考角度.對於react選手來說,如果沒用typescript,我建議大家都用PropTypes, 它是react內置的類型檢測工具,我們可以直接在項目中導入. vue有自帶的屬性檢測方式,筆者在這一點上認為vue還是很貼心的.

上面的思維導圖我們也知道了, 進度條組件的實現原理就是通過對外暴露一定的屬性,使用css先畫一個進度條, 最後通過屬性和樣式之間的調度來實現我們需求滿滿的進度條.至於如何畫進度條,下面會詳細介紹.

2. 基於react實現一個可定製的進度條組件

2.1. 實現進度條的靜態樣式

首先我們會有一個容器來包裹我們的進度條,進度條和進度提示文字分開(為了更靈活的配置), 這樣我們會得到一個如下的html結構:

<div className={styles.progressWrap}>

<div className={styles.progressBar}>

<div className={styles.progressInnerBar}></div>

</div>

<span className={styles.progressText}>{percent + '%'}</span>

}

</div>

.progressBar用來做進度條背景, .progressInnerBar用來做實際的進度條, .progressText為進度條文本.我們通過控制.progressInnerBar的寬度就能實現進度條的變化了, css代碼如下:

.progressWrap {

margin: 6px 3px;

display: inline-flex;

align-items: center;

.progressBar {

position: relative;

display: inline-block;

height: 10px;

background-color: #f0f0f0;

border-radius: 5px;

overflow: hidden;

.progressInnerBar {

position: absolute;

height: 100%;

}

}

.progressText {

margin-left: 6px;

margin-top: -2px;

font-size: 14px;

}

}

沒錯,css代碼就這麼簡單, 我們用了css3比較流行的額彈性布局flex, css部分由於都比較簡單,這裡我只提一點就是.progressInnerBar的css,使用絕對定位, 因為這個部分未來可能會做動畫,所以我們把它做成離屏dom, 因為它只做展示,它的寬度完全由js控制,後面我們會將會看到.

2.2 實現組件外殼

我們根據我們收集到的需求, 可以對外暴露7個自定義屬性(props),所以我們的react組件一定是這樣的:

/**

* 進度條組件

* @param {themeColor} string 進度條的顏色

* @param {percent} number 進度值百分比

* @param {autoHidden} boolean 是否進度到100%時自動消失

* @param {hiddenText} boolean 是否影藏進度條文本

* @param {width} string|number 進度條的寬度

* @param {textColor} string 進度文本顏色

* @param {statusScope} array 狀態閾值,分別設置不同進度範圍的進度條顏色,最大允許設置3個值, 為一個二維數組

*/

function Progress(props) {

let {

themeColor = '#06f',

percent = 0,

autoHidden = false,

hiddenText = false,

width = 320,

textColor = '#666',

statusScope

} = props

return

<div className={styles.progressWrap}>

<div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}>

<div

className={styles.progressInnerBar}

style={{

width: `${percent}%`

}}

>

</div>

</div>

{

!hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span>

}

</div>

}

根據我們收集到的額需求我們很快可以知道react組件需要暴露哪些屬性,而不會造成多餘的屬性,這一點是非常好的設計方法, 核心思想就是基於需求設計.所以我們當確定需求之後,其實組件已經實現了.這一經驗一致應用於筆者很多實際項目中,也清晰的指引著我組件的最終實現.剩幾個關鍵點如下:

設置進度區間進度為100%時進度條自動消失3. react組件細節和最終實現

react組件中,一個屬性不一定要顯性的賦值才能正常工作,比如上面代碼中的hiddenText屬性, 如果我們不設置false或者true, 那麼react會默認為false, 如果只寫了hiddenText屬性而不賦值, react會自動認為它的值為true.這是react的一個設計細節,希望大家能了解掌握. 設置進度區間這個需求是組件唯一比較複雜的地方(相對來說,實際項目中有更複雜的案例),對應的屬性為statusScope, 它的值為一個數組,之所以為數組是為了開發人員更容易理解和使用,它的值可能如下:

let scope = [[30, 'red'], [60, 'orange'], [80, 'blue']]

最大閾值為3,意思就是用戶可以設置4種不同的進度狀態.每一個狀態用不同的顏色代替.由於用戶可以不是按照從小到大的順序寫數組的,所以為了組件的可靠性和容錯性, 筆者專門寫了排序方法對用戶傳來的額二維數組進行排序.具體代碼邏輯如下:

// 升序排序

let sortArr = arr => arr.sort((a,b) => a[0] - b[0])

// 檢測值所對應的進度條顏色狀態

function checkStatus(scope, val, defaultColor) {

val = +val

// 從小到大排序

sortArr(scope)

if(scope.length === 1) {

return val < scope[0][0] ? scope[0][1] : defaultColor

}else if(scope.length === 2) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: defaultColor

}else if(scope.length === 3) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: scope[1][0] < val && val < scope[2][0] ? scope[2][1]

: defaultColor

}

}

筆者不認為checkStatus是最優的計算閾值顏色的方法, 大家可以用更優雅的方法實現它.該方法的作用就是通過傳入用戶配置的區間和當前的進度值,來得到當前進度條的顏色.

進度為100%時進度條自動消失的邏輯也很簡單,就是判斷有這個屬性,並且進度為100時將組件卸載就好了,所以相對完整的代碼如下:

import styles from './index.less'

// 升序排序

let sortArr = arr => arr.sort((a,b) => a[0] - b[0])

// 檢測值所對應的進度條顏色狀態

function checkStatus(scope, val, defaultColor) {

val = +val

// 從小到大排序

sortArr(scope)

if(scope.length === 1) {

return val < scope[0][0] ? scope[0][1] : defaultColor

}else if(scope.length === 2) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: defaultColor

}else if(scope.length === 3) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: scope[1][0] < val && val < scope[2][0] ? scope[2][1]

: defaultColor

}

}

/**

* 進度條組件

* @param {themeColor} string 進度條的顏色

* @param {percent} number 進度值百分比

* @param {autoHidden} boolean 是否進度到100%時自動消失

* @param {hiddenText} boolean 是否影藏進度條文本

* @param {width} string|number 進度條的寬度

* @param {textColor} string 進度文本顏色

* @param {statusScope} array 狀態閾值,分別設置不同進度範圍的進度條顏色,最大允許設置3個值, 為一個二維數組

*/

function Progress(props) {

let {

themeColor = '#06f',

percent = 0,

autoHidden = false,

hiddenText = false,

width = 320,

textColor = '#666',

statusScope

} = props

return +percent === 100 && autoHidden ?

null :

<div className={styles.progressWrap}>

<div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}>

<div

className={styles.progressInnerBar}

style={{

width: `${percent}%`,

backgroundColor: statusScope && statusScope.length ? checkStatus(statusScope, percent, themeColor) : themeColor

}}

>

</div>

</div>

{

!hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span>

}

</div>

}

大家也許覺得到這裡我們的組件就做好了.其實為了我們組件能夠健壯的執行,我們用propType來對屬性進行檢測.關於react的propTypes的用法,我們可以去react官網自行學習,用法也很簡單, 一下代碼我也會做完善的額注釋. 下面看看我們完整的效果演示:

完整代碼如下:

import PropTypes from 'prop-types'

import styles from './index.less'

// 升序排序

let sortArr = arr => arr.sort((a,b) => a[0] - b[0])

// 檢測值所對應的進度條顏色狀態

function checkStatus(scope, val, defaultColor) {

val = +val

// 從小到大排序

sortArr(scope)

if(scope.length === 1) {

return val < scope[0][0] ? scope[0][1] : defaultColor

}else if(scope.length === 2) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: defaultColor

}else if(scope.length === 3) {

return val < scope[0][0] ? scope[0][1]

: scope[0][0] < val && val < scope[1][0] ? scope[1][1]

: scope[1][0] < val && val < scope[2][0] ? scope[2][1]

: defaultColor

}

}

/**

* 進度條組件

* @param {themeColor} string 進度條的顏色

* @param {percent} number 進度值百分比

* @param {autoHidden} boolean 是否進度到100%時自動消失

* @param {hiddenText} boolean 是否影藏進度條文本

* @param {width} string|number 進度條的寬度

* @param {textColor} string 進度文本顏色

* @param {statusScope} array 狀態閾值,分別設置不同進度範圍的進度條顏色,最大允許設置3個值, 為一個二維數組

*/

function Progress(props) {

let {

themeColor = '#06f',

percent = 0,

autoHidden = false,

hiddenText = false,

width = 320,

textColor = '#666',

statusScope

} = props

return +percent === 100 && autoHidden ?

null :

<div className={styles.progressWrap}>

<div className={styles.progressBar} style={{ width: typeof width === 'number' ? width + 'px' : width }}>

<div

className={styles.progressInnerBar}

style={{

width: `${percent}%`,

backgroundColor: statusScope && statusScope.length ? checkStatus(statusScope, percent, themeColor) : themeColor

}}

>

</div>

</div>

{

!hiddenText && <span className={styles.progressText} style={{ color: textColor }}>{percent + '%'}</span>

}

</div>

}

Progress.propTypes = {

themeColor: PropTypes.string,

percent: PropTypes.number,

autoHidden: PropTypes.bool,

textAlign: PropTypes.string,

hiddenText: PropTypes.bool,

width: PropTypes.oneOfType([

PropTypes.string,

PropTypes.number

]),

statusScope: PropTypes.array

}

export default Progress

關於如何使用,我就不做過多說明了,這裡舉2個例子:

<Progress

percent={percent}

width={240}

autoHidden

/>

<Progress

percent={10}

themeColor="#6699FF"

statusScope={[[18, 'red'], [40, 'orange']]}

/>

相關焦點

  • 精通react/vue組件設計之配合React Portals實現一個(Drawer)組件
    通過組件的設計過程,大家會接觸到一個完成健壯的組件設計思路和方法,也能在實現組件的過程逐漸對react/vue的高級知識和技巧有更深的理解和掌握,並且在企業實際工作做遊刃有餘.作為數據驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘代碼, 一切皆組件的思想深得人心.
  • 《精通react/vue組件設計》之實現一個健壯的警告提示(Alert)組件
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文是筆者寫組件設計的第七篇文章, 今天帶大家實現一個自帶主題且可關閉的Alert組件, 該組件在諸如Antd或者elementUI等第三方組件庫中都會出現,主要用來提供系統的用戶反饋
  • 精通react/vue組件設計之實現一個Tag(標籤)和Empty(空狀態)組件
    "辛勤勞動",而是要根據已有前端的開發經驗,總結出一套自己的高效開發的方法.作為數據驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘代碼, 一切皆組件的思想深得人心.所以, 為了讓工程師們有更多的時間去考慮業務和產品迭代,我們不得不掌握高質量組件設計的思路和方法.所以筆者將花時間去總結各種業務場景下的組件的設計思路和方法,並用原生框架的語法去實現各種常用組件的開發,希望等讓前端新手或者有一定工作經驗的朋友能有所收穫
  • 精通React/Vue系列之帶你實現一個功能強大的通知提醒框
    本文將會使用React來開發該組件,也會使用到Javascript中常用的一些設計模式,比如單例模式,但是不管你使用什麼框架來實現,原理都是通用的,如果感興趣的朋友可以用vue也實現以一下。正文在開始組件設計之前希望大家對css3和js有一定的基礎,並了解基本的react/vue語法.我們先來解構一下Notification組件, 一個Notification分為以下幾個部分:每一個區塊都可以自定義配置, 也可以組合其他組件.並且我們可以配置提醒框出現的位置,就像antd
  • 如何寫一個vue組件專題及常見問題 - CSDN
    轉自:https://www.cnblogs.com/pengchenggang/p/10880437.html如何寫好一個vue組件一個適用性良好的組件,一種是可配置項很多,另一種就是容易覆寫,從而擴展功能Vue 組件的 API 來自三部分——prop、事件和插槽:prop 允許外部環境傳遞數據給組件event 允許從組件內觸發外部環境的副作用slot
  • Vue3 & React Hooks 新UI組件原理:Modal 彈窗
    然後又無意間刷到「Portal」,才知道Modal的實現還有如此妙的方式,順而想著乾脆把UI組件庫的實現原理看完。❝以下引自:《Vue 中的 Portal 技術》❞以vue-dom-portal為例,代碼非常簡單無非就是將當前的 dom 移動到指定地方:
  • 基於Vue實現一個有點意思的拼拼樂小遊戲
    cd pinpinle && yarn start複製代碼關於vue-cli3配置實戰,可以移步 一張圖教你快速玩轉vue-cli3H5遊戲核心功能介紹目前筆者主要整理樂如下核心功能,接下來筆者會一一帶大家實現
  • GitHub上star超1.2k的vue表格組件,功能太多又實用
    組件名稱:vxe-table項目地址:Github:https://github.com/xuliangzhan/vxe-table>碼云:https://gitee.com/xuliangzhan_admin/vxe-table一個基於 vue 的表格組件,支持增刪改查、虛擬滾動、懶加載、快捷菜單、數據校驗、樹形結構、列印導出、表單渲染、數據分頁、模態窗口、自定義模板、賊靈活的配置項、豐富的擴展插件等面向現代瀏覽器
  • React組件之間的8種通訊方式
    最簡單的方式是在父組件中傳遞一個函數給子組件,子組件可以使用這個函數來把數據傳給它的父組件。在父組件中可以這樣把一個函數作為屬性傳給子組件,例如:然後子組件可以這樣調用這個函數:最後不要忘記在子組件的 propTypes 中聲明這個函數:4.事件冒泡事件冒泡並非 React 的概念,而是瀏覽器中 DOM 的事件機制。和回調函數類似,它也可以把數據從子組件發送到父組件。
  • 又一款基於Vue的數據可視化組件庫,Github上star超1.4k,太酷炫
    組件庫名稱:DataV項目地址:https://github.com/DataV-Team/DataVDataV是一個基於Vue的數據可視化組件庫(當然也有React版本)提供用於提升頁面視覺效果的SVG邊框和裝飾提供常用的圖表如折線圖等飛線圖/輪播表等其他組件npm安裝$ npm install @jiaminghi/data-view使用import
  • Vue 項目中哪些問題戳中你的痛點?你又是如何解決的?(更新中)
    這裡我說一下vue-awesome-swiper這個輪播組件,真的非常強大,基本可以滿足我們的輪播需求。swiper相信很多人都用過,很好用,也很方便我們二次開發,定製我們需要的輪播效果。vue-awesome-swiper組件實質上基於swiper的,或者說就是能在vue中跑的swiper。
  • 前端組件/庫打包利器rollup使用與配置實戰
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫目前主流的前端框架vue和react都採用rollup來打包,為了探索rollup的奧妙,接下來就讓我們一步步來探索,並基於rollup搭建一個庫打包腳手架,來發布自己的庫和組件。
  • vuex實現預熱篇-vue插件開發
    #泛舟計劃·讓知識更好看#1.如何引用vue插件呢?Vue.use(Vuex)2.use做了什麼事呢?了解了以上的用法,那麼咱就知道大概的結構怎麼寫了吧,來先構造下結構// Myplugin作為對象傳入const MyLoggerPlugin = {}// Vue 就是Vue這個構造函數,options是插件傳入的參數MyLoggerPlugin.install = function(Vue, options) {console.log(Vue, options) //輸出的就是vue
  • 專欄l 利用數字孿生和創成設計實現單色器狹縫組件的正向設計
    單色器的狹縫是一個關鍵組件,狹縫的開口平行性、對稱性以及開閉的均勻性、狹縫寬度和解析度等指標的精度直接影響單色器的光譜解析度和/或空間分辨能力。特別是最窄的狹縫是決定單色器解析度的關鍵因素。來源:安世亞太目前單色器普遍使用的狹縫組件有以下幾種:一種是固定寬度狹縫,此類狹縫的特點是結構簡單,成本低,但不能滿足寬度可調節的要求;另一種是釆用多個固定寬度的狹縫,在使用過程中根據需要相互切換,其缺點可變檔位少,當狹縫寬度小於0.1mm時,狹縫組件的加工或拼裝質量難以保證。
  • 一張圖教你快速玩轉vue-cli3
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言本文系統的梳理了vue-cli3搭建項目的常見用法,目的在於讓你快速掌握獨立搭建vue項目的能力。
  • Vue超好玩的新特性:DOM傳送門
    我們來解釋一下:「目的地」:任意門的使用方法是"在打開門時要想著目的地,否則將通向無法預知的地區"「受目的地環境的影響」:假如你的目的地是一個極寒之地,那麼你就會像下圖這樣:這麼做完全可以實現,事實上以前我們大家一直都是這樣實現類似需求的。
  • 18GW電池+8GW組件!蘇州愛康光電發布高效異質結電池及組件項目設計...
    近日,蘇州愛康光電科技有限公司發布18GW高效異質結太陽能電池項目及8GW組件項目設計招標公告。
  • React 靈魂 23 問,你能答對幾個?
    但是之前數據結構不支持這樣的實現異步 diff,於是 React 實現了一個類似鍊表的數據結構,將原來的 遞歸diff 變成了現在的 遍歷diff,這樣就能做到異步可更新了。虛擬dom 相當於在 JS 和真實 dom 中間加了一個緩存,利用 diff 算法避免了沒有必要的 dom 操作,從而提高性能。9、錯誤邊界是什麼?它有什麼用?在 React 中,如果任何一個組件發生錯誤,它將破壞整個組件樹,導致整頁白屏。這時候我們可以用錯誤邊界優雅地降級處理這些錯誤。
  • 日託光伏深度布局BIPV S6柔性高效組件全面上市
    日託光伏S6組件以MWT技術為基礎,主要通過雷射打孔、背面布線技術,以導電箔取代焊帶,使得組件正負電極均在組件背面,電池表面無主柵線無焊帶,提高了受光面積,規避了焊接應力,真正實現高效可靠,轉換效率可達21.2%,輸出功率可達325W,達到行業領先水平。其次,實現至輕至柔,為柔性所長。
  • 準備將您的Vue應用遷移到Vue 3
    看起來不錯,因為您可以在模板語法中實現它。/helper/filter'// your vue object{methods: { toCurrency }}注意:只是toCurrency可以使用;)感謝ES6對象屬性的簡寫將組件重構model為.sync根據RFC文檔,Vue 3將棄用modelVue組件中的選項,並將其替換sync為multiple model。