為了賬號安全,請及時綁定郵箱和手機立即綁定

你能使用 typescript 寫好一個最簡單的 Button 組件嗎?

2020.03.23 10:28 2634瀏覽

先來一個硬廣:我在慕課網上線了新課

面對的用戶是:想要提高React 水平,寫出符合大廠規范代碼,了解開源庫的發布驗證流程的同學,這門課從設計,到漸進式開發,再到單元測試,發布再到 CI/CD 整個流程全覆蓋。同時我還為這門課推出了官方的演示網站:http://vikingship.xyz/ ,有所有組件和文檔的演示,可以看看。

這里就講講組件庫實現的第一個組件 Button,大家一看這個組件,應該是覺得太簡單啦,這還用學?閉著眼都能寫出來,把需求扔過來吧。結果是怎樣呢?讓我們來試試吧。

第一步:根據原型圖完成需求分析

設計師把熱乎乎的需求扔到了我的臉上,這是簡化了的原型圖,去掉了很多需求。

http://img4.sycdn.imooc.com/5e78300400018dd512560942.jpg

確實看起來也很簡單,不就是來回變換 class嘛 ,讓一個普通的 button 顯示的不一樣嘛,這有何難?當然要注意的就是link button,和 disabled 兩種狀態,它們是兩個特殊的屬性。將需求分析一下,得到的代碼邏輯無非就是這樣。

http://img1.sycdn.imooc.com/5e78301e00012c7111101240.jpg

第二步:開始初步編碼

好,開始代碼,特別注意我們在上面備注提到的兩點,typescript 有字符串字面量這種美好的東西,自然適合一系列定死的常量。

// 把要的依賴整出來  
import React, { FC } from ‘react’  
// 大小兩種 size  
export type ButtonSize = ‘lg’ | ‘sm’  
// 四種不同的 type  
export type ButtonType = ‘primary’ |default| ‘danger’ | ‘link’  
//屬性很重要 確定好它就很完美了  
interface BaseButtonProps {  
  className?: string;  
  /**設置 Button 的禁用 _/  
  disabled?: boolean;  
  /**設置 Button 的尺寸_ /  
  size?: ButtonSize;  
  /**設置 Button 的類型 _/  
  btnType?: ButtonType;  
  href?: string;  
}

接下來就可以寫組件的主體啦

export const Button: FC<BaseButtonProps> = (props) => {  
  //取出屬性    
  const {   
    btnType,  
    className,  
    disabled,  
    size,  
    children,  
    href,  
  } = props  
  //根據流程圖 來渲染不同的內容    
 if (btnType = ‘link’ && href ) {  
    return (  
      <a href={href}>  
        {children}  
      </a>  
    )  
  } else {  
    return (  
      <button>  
        {children}  
      </button>  
    )  
  }}

接下來就是工作的大頭 拼接 class 了,這里讓找出我們的 添加 class 好幫手 https://github.com/JedWatson/classnames#readme 大家自己研究文檔,用法我就不累贅了。

  // btn, btn-lg, btn-primary,   
  // 添加根據type 和 size 兩類特殊的 class 名稱    
 const classes = classNames(‘btn’, className, {  
    [`btn-${btnType}`]: btnType,  
    [`btn-${size}`]: size,  
  })

別忘了我們說過的特殊 disabled 屬性,button 天然支持這個屬性,而 a 鏈接沒有這么一個屬性,所以我們需要用樣式來模擬這個屬性,所以我們給 a 鏈接添加一個特殊的 class,然后把它們填充到 button 和 a 元素上。

  const classes = classNames(‘btn’, className, {// link 用來模擬 disabled 的樣式和行為歐!      
     ‘disabled’: (btnType = ‘link’) && disabled  
  })<a  
    className={classes}  
    href={href}  
  >  
    {children}  
  </a><button  
    className={classes}  
    // 我是原生屬性!      
    disabled={disabled}  
  >  
    {children}  
  </button>

第三步:弄點好看的樣式

結構到這里沒啥問題,接下來就來添加樣式,我們使用 scss,樣式我不會多寫,但是我們要學會兩點:1 將一些數值定義成變量,假如我們要做一個組件庫,那么用戶自定義也是非常重要的,通過這種方法,可以讓用戶覆蓋你的默認樣式。2 使用 mixin 來梳理可以重復的邏輯,比如這里的大小和不同的 type 明顯就是兩個不同的 mixin。

.btn {  
  … 各種樣式  
  font-weight: $btn-font-weight;
  line-height: $btn-line-height;
  color: $body-color;
  box-shadow: none;  
  @include button-size( $btn-padding-y,  $btn-padding-x,  $btn-font-size,  $border-radius);
  
  &.disabled,
  &[disabled] {
    cursor: not-allowed;
    opacity: $btn-disabled-opacity;
    box-shadow: none;
    > * {
      pointer-events: none;
    }
  } }  
  }  
.btn-lg {
  @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);
}
.btn-sm {
  @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);
}

.btn-primary {
  @include button-style($primary, $primary, $white)
}
.btn-danger {
  @include button-style($danger, $danger, $white)
}

.btn-default {
  @include button-style($white, $gray-400, $body-color, $white, $primary, $primary)
}
 //對于 link ,比較特殊,給它單獨拿出來  
.btn-link {  
    // … 省略掉各種樣式   
    // 特殊處理 disabled   
    &:disabled,  
    &.disabled {  
      color: $btn-link-disabled-color;  
      pointer-events: none;  
    }  
}

看看它們長得怎樣?

http://img3.sycdn.imooc.com/5e7830320001366308720578.jpg

看上去不錯,讓我們來試試它配合 ts 是否方便,聯想是否智能,看看下面這個 gif。

https://s1.ax1x.com/2020/03/23/8TNmMn.gif

初次感受還不錯,那么這個簡單的組件是否完美了?大家都知道 button 組件和 a 鏈接都是標準的 HTML 元素,它們都有很多原生的屬性,https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/button,button 有比如 name,type,a鏈接有 target,rel 等等。現在我們在組件中輸入這些屬性根本沒用!自然就沒有 ts 的自動補全,這是一個大問題。

第四步:繼續編碼 - 進化組件

大家都知道這些原生屬性有一大堆,你看了 mdn 的文檔就知道,難道我們要一個一個寫出來嘛?那樣太麻煩了,可以直接放棄了。這時候 React 里面預定義的屬性和 ts 的內置類型來幫我們了,我們來看看怎么做把!

// react 內置了 button 和 anchor 的類型,里面有它們的全部屬性,你可以按住 cmd點擊一下試試  
import React, { FC, ButtonHTMLAttributes, AnchorHTMLAttributes } from ‘react’

//下面我們要做的就是擴充已經創建好的 ButtonProps,我們使用 ts 的交叉類型來完成它  
type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>  
type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>

// 最后,讓我們把它們合體,重建,然后用ts 內置的 Partial 把這些屬性都轉換成可選  
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProp>

這樣 ButtonProps 就擁有了這些各種各樣的原生屬性。現在讓我們把這些屬性取出來并且放置在組件上,

 const {   
    btnType,  
    className,  
    disabled,  
    size,  
    children,  
    href,  
    // 使用最好用的 三個點 spread 把剩下的屬性都取出來  
    …restProps  
  } = props

//然后再照貓畫虎用 spread 賦值在組件上  
return (  
	<a  
		className={classes}  
		href={href}  
		{…restProps}  
	>  
	{children}  
	</a>  
)  
return (  
	<button  
		className={classes}  
		disabled={disabled}  
		{…restProps}  
	>  
	{children}  
	</button>  
)

好,最后來試試這些原生屬性是否會借 ts 的東風,自動填充出來。

https://s1.ax1x.com/2020/03/23/8Ttj8H.gif

第五步:未完待續

好,那么到目前為止,我們做出了一個看起來還不錯的 Button 組件,其實我們能完成的更多,比如說現在測試完全靠肉眼,需要給它單元測試,假如是一款企業級產品,我們需要自動生成文檔,還需要打包成合適的模塊類型以及發布到 npm 等等工作。現在是不是感覺,這么一個簡單的組件也不是那么簡單的,如果一個 Button 組件成功的提起了你的興趣,不妨來看看和我一起完成一個組件庫是多么有趣的一個過程把。

點擊查看更多內容

本文原創發布于慕課網 ,轉載請注明出處,謝謝合作

3人點贊

若覺得本文不錯,就分享一下吧!

評論

相關文章推薦

正在加載中
意見反饋 幫助中心 APP下載
官方微信

舉報

0/150
提交
取消
TLC官网 <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>