如何寫出簡潔篙程、優(yōu)雅、可維護的組件别厘。

功能分離

這個算是面向?qū)ο罄锏乃枷敕课纾诮M件里,有很多功能是獨立的,比如最常見的發(fā)送驗證碼郭厌,確認密碼等。把這些邏輯封裝成一個或幾個函數(shù)寫在組件里的話雕蔽,這在組件很小的時候沒有什么影響折柠,但是當組件功能比較復雜的時候,就會有些問題:

  1. 組件邏輯區(qū)域會變的很大批狐,各種方法混雜很難一眼辨識
  2. 因為定義功能需要的變量和方法不在一起扇售,導致修改麻煩

功能分離就是把這些功能抽離出來,寫出一個類嚣艇,然后在組件里引入承冰。
下面是一個簡單的彈窗控制的功能的類和這個類的使用:

export class DialogCtrl {
  isVisible = false;

  open () {
    this.isVisible = true;
  }
  close () {
    this.isVisible = false;
  }
}

然后在需要的組件里引入并實例化:

DialogCtrl = new this.CommonService.DialogCtrl(); // 是否打開彈窗

在html里可以直接這樣用:

<nz-modal
    [nzVisible]="DialogCtrl.isVisible" 
    [nzTitle]="'更新密碼'" 
    [nzContent]="modalContent" 
    (nzOnCancel)="DialogCtrl.close()" 
    [nzConfirmLoading]="isSubmiting"
    nzOkText="保存"
    (nzOnOk)="savePassword()"></nz-modal>

這個nz-modal是一個彈窗,在組件里我們只有一個變量的聲明食零,如此簡潔困乒!而在html里DialogCtrl.isVisible,DialogCtrl.close()的形式也很容易理解它的作用和出處贰谣。
這樣做的另一個好處是利于實現(xiàn)復用娜搂。對于可以復用的功能,比如上面發(fā)送驗證碼的邏輯吱抚,可以建一個全局的服務來提供百宇。在angular里,通過angular的服務和依賴注入可以很輕松的實現(xiàn)秘豹,這里是我集中功能的common.service.ts服務文件:

Paste_Image.png

common.servide.ts文件:

@Injectable()
export class CommonService {
  // 功能類集合
  public DialogCtrl = DialogCtrl;
  public MessageCodeCtrl = MessageCodeCtrl;
  public CheckPasswordCtrl = CheckPasswordCtrl;

  constructor(
    private http: HttpClient
  ) { }

  /* 獲取短信驗證碼(這些功能需要用到的方法)
  -------------------------- */
  public getVerificationCode (phoneNum: string): Observable<any> {
    return this.http.get('/account/short_message?phoneNumber=' + phoneNum);
  }
}

需要的地方只要注入這個服務就可以獲取想要的功能携御。相比較直接建立一個組件來實現(xiàn),我覺得這樣寫有一些優(yōu)勢:
靈活性更高既绕。寫成組件會有樣式的限制啄刹,而這樣寫沒有。
更簡潔岸更。寫成組件鸵膏,與之溝通只能通過子父組件的傳入變量,監(jiān)聽子組件事件的方法怎炊,你使用的組件不可避免的會多出這些變量和方法谭企。

狀態(tài)管理

不知道大伙兒有沒有這樣的感覺,自己寫新項目的時候覺得邏輯清晰评肆,代碼簡練债查,功能也都實現(xiàn)了,但是過一段時間去看或者要改自己的代碼的時候...哇瓜挽,這是什么玩意兒盹廷。至少我有過:flushed:
前端復雜的地方源于數(shù)不清的狀態(tài),于是我為那些有復雜狀態(tài)的組件建立一個集中管理狀態(tài)的對象(這里我取名為Impure):

/* 變量定義 -- 狀態(tài)
  -------------------------- */
  registerForm: FormGroup;  // 注冊賬號表單
  registerInfoForm: FormGroup; // 公司信息表單
  isSubmitting = false; // 表單是否正在提交
  nowForm = 'registerForm';  // 當前正在操作的表單
  MessageCodeCtrl = new this.CommonService.MessageCodeCtrl(this.Msg, this.CommonService); // 驗證碼控制

  /* 變量定義 -- 定值
  -------------------------- */
  registerFormSubmitAttr = ['login', 'password', 'shortMessageCode', 'roles', 'langKey'];
  registerInfoFormFormSubmitAttr = ['simName', 'contacter', 'officeTel', 'uid'];

  /* 改變狀態(tài)事件
  -------------------------- */
  Impure = {

    // 表單初始化
    RegisterFormInit: () => this.registerForm = this.registerFormInit(),
    RegisterInfoFormInit: () => this.registerInfoForm = this.registerInfoFormInit(),

    // 驗證碼不合法
    MessageCodeInvalid: {
      notSend: () => this.Msg.error('您還未發(fā)送驗證碼'),
      notRight: () => this.Msg.error('驗證碼錯誤')
    },

    // 表單提交
    FormSubmit: {
      invalid: () => this.Msg.error('表單填寫有誤'),
      before: () => this.isSubmitting = true,
      registerOk: () => {
        this.Msg.success('賬號注冊成功');
        this.nowForm = 'registerInfoForm';
      },
      registerInfoOk: () => {
        this.Msg.success('保存信息成功!請耐心等待管理員審核');
        this.Router.navigate(['/login']);
      },
      fail: () => this.Msg.error('提交失敗久橙,請重試'),
      after: () => this.isSubmitting = false
    }
  };

這是一個簡單的有兩個表單的注冊組件俄占,因為兩個表單html耦合度很高管怠,所以寫在了一起。
在組件內(nèi)將變量分為狀態(tài)和定值的兩類缸榄,聲明了一個Impure對象來集中管理這些狀態(tài)渤弛,原則上這個組件里所有狀態(tài)的改變都寫在Impure里,而將事件觸發(fā)的判斷條件甚带,數(shù)據(jù)處理寫在Impure外面她肯。
可以對比下這兩個使用Impure和不使用Impure的表單提交方法:

/* 注冊賬號表單提交(Impure)
  -------------------------- */
  async register (form) {
    const _ = this.Fp._; // ramda庫,用于數(shù)據(jù)處理
    const { MessageCodeInvalid, FormSubmit } = this.Impure;

    // 表單不合法
    if (form.invalid) { FormSubmit.invalid(); return; }

    // 驗證碼不合法
    if (!this.MessageCodeCtrl.code) { MessageCodeInvalid.notSend(); return; }
    if (this.MessageCodeCtrl.code !== form.controls.shortMessageCode.value) { MessageCodeInvalid.notRight(); return; }

    // 表單提交
    FormSubmit.before();
    const data = _.compose(_.pick(this.registerFormSubmitAttr), _.map(_.prop('value')))(form.controls); // 數(shù)據(jù)處理
    const res = await this.AccountService.producerRegisterFirst(data).toPromise();
    if (!res) { FormSubmit.registerOk(); } else { FormSubmit.fail(); }
    FormSubmit.after();
  }

  /* 公司信息表單提交(非Impure)
  -------------------------- */
  async registerInfo ({ simName, contacter, officeTel }) {
    // 表單不合法
    if (this.registerInfoForm.invalid) { this.Msg.error('表單填寫有誤'); return; }

    // 表單提交
    this.isSubmitting = true;
    const data = { // 數(shù)據(jù)處理
      simName: simName.value,
      contacter: contacter.value,
      officeTel: officeTel.value,
      uid: this.registerForm.controls.phone.value
    };
    const res = await this.AccountService.producerRegisterSecond(data).toPromise();
    if (!res) {
      this.Msg.success('保存信息成功!請耐心等待管理員審核');
      this.Router.navigate(['/login']);
    } else {
      this.Msg.error('提交失敗,請重試');
    }
    this.isSubmitting = false;
  }

使用Impure管理狀態(tài)后鹰贵,邏輯清晰晴氨,在提交表單時你 只需要關(guān)注事件發(fā)生的條件 就可以了,而第二個條件和狀態(tài)寫在一起會很混亂(其實就是我以前寫的)碉输,不能一眼清楚這個狀態(tài)改變發(fā)生在什么時候籽前,特別是你一段時間再來看的時候。
其實這里的數(shù)據(jù)處理(這里面的data)應該單獨拿出來寫一個方法的腊瑟,我只是來頂一下用純函數(shù)來處理數(shù)據(jù)的優(yōu)點聚假,這里的_是用了ramda這個庫。相比較第二個處理方式闰非,第一種方式更加優(yōu)雅膘格,簡潔,很容易看出數(shù)據(jù)的源頭是什么(這里是form.controls),單獨抽離成數(shù)據(jù)處理函數(shù)也有很高的復用性财松。
假如某一天你要改下這里兩個表格的成功后的狀態(tài)瘪贱,不再需要到這兩個長長的提交函數(shù)里找到它們?nèi)缓笠粋€一個改,只要在Impure里面改就可以了辆毡,你甚至不需要看那兩個提交的方法菜秦。
這樣子,一個組件可以大致分為狀態(tài)舶掖,狀態(tài)管理(impure)球昨,改變狀態(tài)的事件(狀態(tài)改變的判斷條件),和數(shù)據(jù)處理(純函數(shù))四部分眨攘,各司其職主慰,很好維護。

結(jié)語

這些適合我但不一定適合所有人鲫售,每個人都有自己的風格共螺,各位看官感受下就好。以后我有其它方面的總結(jié)也會拿出來分享情竹。

學習前端的同學注意了C瓴弧!!
學習過程中遇到什么問題或者想獲取學習資源的話雏蛮,歡迎加入前端學習交流群461593224涎嚼,我們一起學前端!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挑秉,一起剝皮案震驚了整個濱河市铸抑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌衷模,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒲赂,死亡現(xiàn)場離奇詭異阱冶,居然都是意外死亡,警方通過查閱死者的電腦和手機滥嘴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門木蹬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人若皱,你說我怎么就攤上這事镊叁。” “怎么了走触?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵晦譬,是天一觀的道長。 經(jīng)常有香客問我互广,道長敛腌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任惫皱,我火速辦了婚禮像樊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旅敷。我一直安慰自己生棍,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布媳谁。 她就那樣靜靜地躺著涂滴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪韩脑。 梳的紋絲不亂的頭發(fā)上氢妈,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音段多,去河邊找鬼首量。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的加缘。 我是一名探鬼主播鸭叙,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拣宏!你這毒婦竟也來了沈贝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤勋乾,失蹤者是張志新(化名)和其女友劉穎宋下,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辑莫,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡学歧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了各吨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枝笨。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖揭蜒,靈堂內(nèi)的尸體忽然破棺而出横浑,到底是詐尸還是另有隱情,我是刑警寧澤屉更,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布徙融,位于F島的核電站,受9級特大地震影響偶垮,放射性物質(zhì)發(fā)生泄漏张咳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一似舵、第九天 我趴在偏房一處隱蔽的房頂上張望脚猾。 院中可真熱鬧,春花似錦砚哗、人聲如沸龙助。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽提鸟。三九已至,卻和暖如春仅淑,著一層夾襖步出監(jiān)牢的瞬間称勋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工涯竟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赡鲜,地道東北人空厌。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像银酬,于是被迫代替她去往敵國和親嘲更。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容