前端工程化之組件化

前端工程化是從軟件工程衍生出的概念, 它是指以工程化的方法構(gòu)建和維護(hù)有效射亏、實(shí)用和高質(zhì)量的軟件。
前端從刀耕火種到現(xiàn)在社區(qū)空前繁榮的發(fā)展歷史卓起,也是工程化的演變歷史衫生。從演變的歷史來看,前端工程化主要分模塊化心包,組件化类咧,工具化馒铃,自動(dòng)化蟹腾。
本篇文章主要談?wù)劷M件化以及組件化的實(shí)踐方案。

組件化
組件并不是一個(gè)新的概念区宇。c++ 中就提出過這樣的概念娃殖,組件是對數(shù)據(jù)和方法的封裝,即對象议谷。
前端中的組件則是對一塊視圖區(qū)域的封裝炉爆,多個(gè)組件組合成了一個(gè)復(fù)合組件,多個(gè)組件組合成了一個(gè)頁面卧晓,所以頁面也是組件芬首。

一個(gè)組件中包含了完整視圖結(jié)構(gòu),樣式表以及交互邏輯逼裆。它是封閉的結(jié)構(gòu)郁稍,對外提供屬性的輸入用來滿足用戶的多樣化需求,對內(nèi)自己管理內(nèi)部狀態(tài)來滿足自己的交互需求胜宇,所以組件的封裝也是對象的封裝耀怜,同樣要做到高內(nèi)聚恢着,低耦合。

組件化的目的是為了實(shí)現(xiàn)代碼的更高層次的復(fù)用财破,提高開發(fā)效率掰派,同樣它也是前端工程化的基礎(chǔ)。組件化的頁面不僅僅利于開發(fā)者在進(jìn)行單元測試左痢,同樣也有益于測試的有效進(jìn)行靡羡。

組件化的實(shí)踐
Web Components
Web Components 是W3C提出的組件化規(guī)范。允許開發(fā)者使用規(guī)范提供的API 以及HTML 模板來實(shí)現(xiàn)可重用的定制元素俊性,與HTML 原本支持的標(biāo)簽元素相同亿眠,可以直接在頁面中使用。
Web Components 主要有三種技術(shù)構(gòu)成:

  • Custom elements(自定義元素):一組JavaScript API磅废,允許您定義custom elements及其行為纳像,然后可以在的用戶界面中按照需要使用它們。
  • Shadow DOM(影子DOM):一組JavaScript API拯勉,用于將封裝的“影子”DOM樹附加到元素(與主文檔DOM分開呈現(xiàn))并控制其關(guān)聯(lián)的功能竟趾。通過這種方式,可以保持元素的功能私有宫峦,這樣它們就可以被腳本化和樣式化岔帽,而不用擔(dān)心與文檔的其他部分發(fā)生沖突。
  • HTML templates(HTML模板): <template><slot> 元素使您可以編寫不在呈現(xiàn)頁面中顯示的標(biāo)記模板导绷。然后它們可以作為自定義元素結(jié)構(gòu)的基礎(chǔ)被多次重用犀勒。
    相關(guān)的開發(fā)教程,請參見 Web Components

但是由于規(guī)范不是很完善妥曲,現(xiàn)只有Firefox(版本63)贾费、Chrome和Opera都默認(rèn)支持Web組件; Safari支持許多web組件特性,但比前面的瀏覽器少; Edge正在開發(fā)一個(gè)實(shí)現(xiàn)檐盟。

Stencil Js
Stencil 是由Ionic 團(tuán)隊(duì)開發(fā)出的前端視圖庫褂萧,類似react。它可以將開發(fā)者開發(fā)的組件編譯成 Web Components 葵萎,可以直接運(yùn)行在瀏覽器中导犹。抹平了前端不同技術(shù)架構(gòu)造成的隔離,比如采用Stencil 開發(fā)的組件羡忘,在Vue 和 React 中都可以使用谎痢,不需要再進(jìn)行額外的兼容工作。

Angular Directives / VUE Components / React Components / Backbone Components / Anything... Components

社區(qū)的視圖庫或者框架提供的組件解決方案卷雕,是當(dāng)前最具代表性的組件化的實(shí)踐节猿,但是不同的框架之間的隔離也同樣是前端需要考慮的問題。

封裝React 組件
封裝組件最主要要遵循開閉原則和單一職責(zé)原則爽蝴,這也是封裝一個(gè)高內(nèi)聚低耦合的組件的核心沐批。
簡而言之纫骑,就是首先要知道你的組件是做什么的,并且它只做這一件事九孩;其次對你的組件的對外的屬性輸出和對內(nèi)的狀態(tài)管理有明確和清晰的認(rèn)知先馆,什么開放給用戶,什么自己管理躺彬。

1.界定一個(gè)組價(jià)的Scope
這里推薦React 官方文檔上拆分一個(gè)頁面成多個(gè)組件的方法煤墙,React 設(shè)計(jì)哲學(xué)

thinking-in-react-components.png
  1. FilterableProductTable (橙色): 是整個(gè)示例應(yīng)用的整體
  2. SearchBar (藍(lán)色): 接受所有的用戶輸入
  3. ProductTable (綠色): 展示數(shù)據(jù)內(nèi)容并根據(jù)用戶輸入篩選結(jié)果
  4. ProductCategoryRow (天藍(lán)色): 為每一個(gè)產(chǎn)品類別展示標(biāo)題
  5. ProductRow (紅色): 每一行展示一個(gè)產(chǎn)品

對應(yīng)的層級(jí):
FilterableProductTable

  • SearchBar
  • ProductTable
    • ProductCategoryRow
    • ProductRow

2.區(qū)分Props 和 State
在React 中 UI = Component(Props, State)宪拥。
Props 是組件對外的接口仿野,不可變。組件內(nèi)可以引用其他組件她君,組件之間的引用形成了一個(gè)樹狀結(jié)構(gòu)(組件樹)脚作,如果下層組件需要使用上層組件的數(shù)據(jù)或方法,上層組件就可以通過下層組件的props屬性進(jìn)行傳遞缔刹,因此props是組件對外的接口球涛。

State 是組件對內(nèi)的狀態(tài),可變校镐。state必須能代表一個(gè)組件UI呈現(xiàn)的完整狀態(tài)集亿扁,即組件對應(yīng)UI的任何改變,都可以從state的變化中反映出來鸟廓;同時(shí)从祝,state還必須是代表一個(gè)組件UI呈現(xiàn)的最小狀態(tài)集,即state中的所有狀態(tài)都是用于反映組件UI的變化引谜,沒有任何多余的狀態(tài)牍陌,也不需要通過其他狀態(tài)計(jì)算而來的中間狀態(tài)。

組件中用到的一個(gè)變量是不是應(yīng)該作為組件state煌张,可以通過下面的4條依據(jù)進(jìn)行判斷:

  1. 這個(gè)變量是否是通過props從父組件中獲饶派摹?如果是骏融,那么它不是一個(gè)狀態(tài)。
  2. 這個(gè)變量是否在組件的整個(gè)生命周期中都保持不變萌狂?如果是档玻,那么它不是一個(gè)狀態(tài)。
  3. 這個(gè)變量是否可以通過state 或props 中的已有數(shù)據(jù)計(jì)算得到茫藏?如果是误趴,那么它不是一個(gè)狀態(tài)。
  4. 這個(gè)變量是否在組件的render方法中使用务傲?如果不是凉当,那么它不是一個(gè)狀態(tài)枣申。這種情況下,這個(gè)變量更適合定義為組件的一個(gè)普通屬性(除了props 和 state以外的組件屬性 )看杭,例如組件中用到的定時(shí)器忠藤,就應(yīng)該直接定義為this.timer,而不是this.state.timer楼雹。

同時(shí)模孩,并不是組件中用到的所有變量都是組件的State!當(dāng)存在多個(gè)組件共同依賴同一個(gè)狀態(tài)時(shí)贮缅,一般的做法是狀態(tài)上移榨咐,將這個(gè)狀態(tài)放到這幾個(gè)組件的公共父組件中。

在項(xiàng)目中開發(fā)組件
在項(xiàng)目中開發(fā)組件谴供,最重要的是明確組件的作用域块茁。

  • 業(yè)務(wù)組件,它會(huì)包含你的業(yè)務(wù)邏輯桂肌,方便你對代碼的分割龟劲。
  • UI組件(以上提出的關(guān)于組價(jià)的封裝的方法論都是UI 組件), 即一個(gè)公共的組件,它所包含的應(yīng)該只有該UI 組件的交互邏輯轴或。如果當(dāng)你覺得它會(huì)有一些公共的業(yè)務(wù)邏輯的時(shí)候昌跌,請?jiān)龠M(jìn)行二次封裝,即在該UI 組件的基礎(chǔ)上在進(jìn)行一層業(yè)務(wù)的包裹照雁。

封裝UI組件

社區(qū)中優(yōu)秀的React 組件庫有很多蚕愤,但是不同的公司有不同的設(shè)計(jì)語言,如果單純的采用某社區(qū)UI庫的話饺蚊,比如Ant-design, 勢必不能滿足業(yè)務(wù)的需求和風(fēng)格的需求萍诱。

所以對于我們來說,我們現(xiàn)階段采用的封裝復(fù)合公司風(fēng)格的UI 組件時(shí)污呼, React-component裕坊,在這個(gè)基礎(chǔ)庫上進(jìn)行二次開發(fā)。

  • React-component, 是Ant-design 的組件基礎(chǔ)庫燕酷,它提供了完善的API以及簡單的樣式籍凝,所以我們需要對它的樣式進(jìn)行定制,滿足公司的設(shè)計(jì)語言苗缩。
import * as React from "react";
import styles from "./Switch.less";
import RcSwitch from "rc-switch";
import "rc-switch/assets/index.css";

type SwitchChangeEventHandler = (
    checked: boolean,
    event: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>
) => void;

interface SwitchProps {
    className?: string;
    prefixCls?: string;
    disabled?: boolean;
    checkedChildren?: React.ReactNode;
    unCheckedChildren?: React.ReactNode;
    onChange?: SwitchChangeEventHandler;
    onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
    onClick?: SwitchChangeEventHandler;
    tabIndex?: number;
    checked?: boolean;
    defaultChecked?: boolean;
    loadingIcon?: React.ReactNode;
    style?: React.CSSProperties;
    title?: string;
    text?: string[];
}

interface SwitchState {
    checked: boolean;
}

export default class Switch extends React.PureComponent<SwitchProps, SwitchState> {
    constructor(props) {
        super(props);
        this.state = {
            checked: !!props.checked
        };
    }
    ...
    ...
    ...

    render() {
        const { text } = this.props;
        const { checked } = this.state;
        const label = checked ? text[0] : text[1];

        return (
            <div className={styles.switch}>
                {text && text.length && <span className={styles.label}>{label}</span>}
                <RcSwitch {...this.props} className={styles.bgColor} onChange={this.onChangeHandler} />
            </div>
        );
    }
}

參考:
React 深入系列3:Props 和 State

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饵蒂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子酱讶,更是在濱河造成了極大的恐慌退盯,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渊迁,居然都是意外死亡慰照,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門琉朽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毒租,“玉大人,你說我怎么就攤上這事漓骚◎蛳危” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵蝌蹂,是天一觀的道長噩斟。 經(jīng)常有香客問我,道長孤个,這世上最難降的妖魔是什么剃允? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮齐鲤,結(jié)果婚禮上斥废,老公的妹妹穿的比我還像新娘。我一直安慰自己给郊,他們只是感情好牡肉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淆九,像睡著了一般统锤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炭庙,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天饲窿,我揣著相機(jī)與錄音,去河邊找鬼焕蹄。 笑死逾雄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腻脏。 我是一名探鬼主播鸦泳,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼迹卢!你這毒婦竟也來了辽故?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤腐碱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體症见,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喂走,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谋作。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芋肠。...
    茶點(diǎn)故事閱讀 38,789評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖遵蚜,靈堂內(nèi)的尸體忽然破棺而出帖池,到底是詐尸還是另有隱情,我是刑警寧澤吭净,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布睡汹,位于F島的核電站,受9級(jí)特大地震影響寂殉,放射性物質(zhì)發(fā)生泄漏囚巴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一友扰、第九天 我趴在偏房一處隱蔽的房頂上張望彤叉。 院中可真熱鬧,春花似錦村怪、人聲如沸秽浇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驮瞧,卻和暖如春腊敲,著一層夾襖步出監(jiān)牢的瞬間击喂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工碰辅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懂昂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓没宾,卻偏偏與公主長得像凌彬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子循衰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評論 2 351