<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從零到一搭建React組件庫(kù)

          共 10030字,需瀏覽 21分鐘

           ·

          2021-04-24 20:53

          作者:ToSmile
          來(lái)源:Segmentfault 思否


          最近一直在搗鼓如何搭建React組件庫(kù),至于為什么會(huì)產(chǎn)生這個(gè)想法,主要是因?yàn)榻M件庫(kù)對(duì)于前端生態(tài)來(lái)說(shuō)究極重要,每一個(gè)著眼于長(zhǎng)遠(yuǎn)發(fā)展、看重開(kāi)發(fā)效率的的互聯(lián)網(wǎng)公司基本上都會(huì)量身定制自己的組件庫(kù),它的好處不用多說(shuō)。對(duì)于前端工程師而言,去理解以及掌握它,可以讓我們?cè)诮窈蟮墓ぷ髦幸约皯?yīng)聘過(guò)程中多出一項(xiàng)特殊技能,并且對(duì)自身的縱向發(fā)展也就是很有利的。下面是我記錄我在搭建組件庫(kù)的過(guò)程。

          初始化工程


          搭建工程不打算采用 create-react-app 腳手架來(lái)搭建,因?yàn)槟_手架封裝好了很多東西,而有些東西對(duì)于組件庫(kù)并不適用,用來(lái)搭建組件庫(kù)過(guò)于臃腫,因此我不打算借助任何腳手架來(lái)搭建工程。

          首先,先創(chuàng)建一個(gè)工程文件夾 pony-react-ui,在該文件夾下執(zhí)行如下命令:

          npm init // 生成package.json
          tsc --init // 生成tsconfig.json

          然后,按照如下目錄結(jié)構(gòu)初始化工程:

          pony-react-ui
          ├── src
              ├── assets
              ├── components
                  ├── Button
                      ├── Button.tsx
                      └── index.ts
                  └── Dialog
                      ├── Dialog.tsx
                      └── index.ts
              ├── styles
                  ├── _button.scss
                  ├── _dialog.scss
                  ├── _mixins.scss
                  ├── _variables.scss
                  └── pony.scss
              └── index.ts // 打包的入口文件,引入pony.scss,拋出每一個(gè)組件
          ├── index.js // 入口文件,package.json中main字段指定的文件
          ├── package.json
          ├── tsconfig.json
          ├── webpack.config.js
          └── README.md

          編寫(xiě)一個(gè)Button組件

          Button組件應(yīng)該滿足一下需求:

          • 不同尺寸
          • 不同類型
          • 不同顏色
          • 禁用狀態(tài)
          • 點(diǎn)擊事件
          Button.tsx
          import React from 'react';
          import classNames from 'classnames';

          export interface IButtonProps {
            onClick?: React.MouseEventHandler;
            // 類型
            primary?: boolean;
            secondary?: boolean;
            outline?: boolean;
            dashed?: boolean;
            link?: boolean;
            text?: boolean;
            // 尺寸
            xLarge?: boolean;
            large?: boolean;
            small?: boolean;
            xSmall?: boolean;
            xxSmall?: boolean;
            // 顏色
            success?: boolean;
            warn?: boolean;
            danger?: boolean;
            // 禁用狀態(tài)
            disabled?: boolean;
            className?: string;
            style?: React.CSSProperties;
            children?: React.ReactNode;
          }

          export const Button = (props: IButtonProps) => {
            const {
              className: tempClassName,
              style,
              onClick,
              children,
              primary,
              secondary,
              outline,
              dashed,
              link,
              text,
              xLarge,
              large,
              small,
              xSmall,
              xxSmall,
              success,
              danger,
              warn,
              disabled,
            } = props;
            
            
            const className = classNames(
              {
                'pony-button'true,
                'pony-button-primary': primary,
                'pony-button-secondary': secondary && !text,
                'pony-button-outline': outline,
                'pony-button-dashed': dashed,
                'pony-button-link': link,
                'pony-button-text': text && !secondary,
                'pony-button-text-secondary': secondary && text,
                'pony-button-round': round,
                'pony-button-rectangle': noRadius,
                'pony-button-fat': fat,
                'pony-button-xl': xLarge,
                'pony-button-lg': large,
                'pony-button-sm': small,
                'pony-button-xs': xSmall,
                'pony-button-xxs': xxSmall,
                'pony-button-long': long,
                'pony-button-short': short,
                'pony-button-success': success,
                'pony-button-warn': warn,
                'pony-button-danger': danger,
                'pony-button-disabled': disabled,
              },
              tempClassName
            );
            
            return (
              <button 
                type="button"
                className={className}
                style={style}
                onClick={onClick}
                disabled={disabled}>
                <span className="pony-button__content">{children}</span>
              </button>
            );
          }
          在Button/index.ts文件中拋出Button組件以及定義的類型
          export * from './Button';
          這樣,一個(gè)示例組件就基本完成了,有同學(xué)肯定會(huì)有這么一個(gè)疑問(wèn),為什么在Button.tsx中沒(méi)有引入它的樣式文件_button.scss,而是在使用時(shí)引入全局樣式或者單獨(dú)引入_button.scss呢?
          // 單獨(dú)引入組件樣式
          import { Button } from 'pony-react-ui';
          import 'pony-react-ui/lib/styles/button.scss';

          // 全局引入組件樣式,打包時(shí)抽離出來(lái)的樣式
          import 'pony-react-ui/lib/styles/index.scss';
          因?yàn)檫@跟樣式的權(quán)重有關(guān),通過(guò)import引入的樣式權(quán)重會(huì)低于JSX中className定義的樣式,因此才可以在組件外部修改內(nèi)部的樣式。
          舉個(gè)實(shí)例:
          import { Button } from 'pony-react-ui';

          import 'pony-react-ui/lib/styles/button.scss';
          import styles from './index.module.scss';

          const Demo = () => (
            <div className={styles.btnBox}>
              <Button onClick={submit}>submit</Button>
            </div>
          )
          引入組件庫(kù)中的Button.scss和本地的index.module.scss在打包后會(huì)以<style></style>標(biāo)簽注入到頁(yè)面中,而且順序是:
          <style type="text/css">
            // Button.scss的樣式
          </style>

          <style type="text/css">
            // index.module.scss的樣式
          </style>
          因此,index.module.scss中的樣式權(quán)重是高于Button.scss中的樣式,可以在index.module.scss中修改Button.scss的樣式

          編寫(xiě)樣式

          ├── styles
              ├── _button.scss
              ├── _dialog.scss
              ├── _mixins.scss
              ├── _variables.scss
              └── pony.scss
          我在style文件下存放所有的樣式文件,與_button.scss、_dialog.scss類型的樣式文件屬于組件的樣式文件,_mixins.scss用于存放mixin指令,提高樣式邏輯復(fù)用
          // _mixins.scss

          @mixin colors($text$border$background) {
            color: $text;
            background-color: $background;
            border-color: $border;
          }

          // 設(shè)置按鈕大小
          @mixin button-size($padding-x, $height$font-size) {
            height: $height;
            padding: 0 $padding-x;
            font-size: $font-size;
            line-height: ($height - 2);
          }
          比如,在_button.scss中使用
          $values#ff0000, #00ff00, #0000ff;
          .primary {
            @include colors($values...);
          }
          node-sass會(huì)將其編譯成
          .primary {
            color: #ff0000;
            background-color: #00ff00;
            border-color: #0000ff;
          }
          _variables.scss用于存放一些樣式常量,比如定義不同尺寸按鈕的字體大小:
          $button-font-size: 14px !default;
          $button-xl-font-size: 16px !default;
          $button-lg-font-size: 16px !default;
          $button-sm-font-size: 12px !default;
          pony.scss會(huì)引入所有的樣式文件,_mixins.scss、_variables.scss這類工具類樣式文件需要置前引入,因?yàn)楹竺娴慕M件樣式文件可能依賴它們
          @import 'variables';
          @import 'mixins';
          @import 'button';
          @import 'dialog';
          ...
          在對(duì)樣式文件構(gòu)建處理時(shí),我沒(méi)有使用css modules去避免樣式重名,而是使用BEM規(guī)范書(shū)寫(xiě)樣式規(guī)避這一問(wèn)題。為什么我要這么做呢?
          rules: [
            {
              test: /\.(sa|sc|c)ss$/,
              use: [
                loader: 'css-loader',
                options: {
                  modules: false // 禁止css modules
                }
              ]
            }
          ]
          因?yàn)槭褂胏ss modules導(dǎo)致無(wú)法從組件外部修改組件內(nèi)部樣式了,從外部修改組件樣式一般會(huì)這樣寫(xiě):
          <Button className="btn">按鈕</Button>

          // 修改Button內(nèi)部樣式,假如組件內(nèi)部樣式有個(gè)樣式類名為pony-button-promary
          .btn {
              :global {
                  .pony-button-promary {
                      color: #da2227;
                  }
              }
          }
          但是,采用了css modules后,pony-button-promary類型后面會(huì)多出一串hash值,而且在每次修改Button組件后,生成的hash都會(huì)不同,這將導(dǎo)致在深度遍歷查找過(guò)程中找不到類名
          .btn {
              :global {
                  // 下次修改Button組件構(gòu)建后,生成的hash不一定為sadf6756 
                  .pony-button-promary-sadf6756 {
                      color: #da2227;
                  }
              }
          }

          打包輸出UMD規(guī)范

          打包輸出es module規(guī)范

          docz生成組件使用文檔

          發(fā)布到npm倉(cāng)庫(kù)



          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開(kāi)更多互動(dòng)和交流,掃描下方”二維碼“或在“公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费av在线观看网站 | 豆花视频在线网站 | 日本一区二区三区中文字幕 | 夜夜骑青青草夜夜嗨 | 日日免费视频 |