Typescript結合React實踐

慕晨同學發表於2019-07-24

文章首發於:github.com/USTB-musion…

寫在前面

Typescript是JavaScript的一個超集,主要提供了型別系統和對es6的支援。本人使用ts編寫react將近3個月的時間,中間踩了不少坑,從剛開始的覺得ts沒有必要到現在覺得ts真香。在這裡對使用ts編寫react的心得做一下總結。

本文將從以下幾部分進行總結:

  1. Typescript的優勢
  2. TS結合React使用
  3. 總結
  4. 參考連結及擴充套件閱讀

Typescript的優勢

1.幫助更好地重構程式碼

一個好的程式碼習慣是常常對自己寫的程式碼進行小的重構,使得程式碼可維護性更強。但是對於很多線上執行的程式碼,程式碼測試覆蓋率往往不是很高,有時候哪怕一個變數名的改動,都會牽一髮而動全身。而對於使用ts編寫的專案就不會有這種擔心。ts的靜態檢查特性會幫助找出程式碼中有錯誤的部分。

2.vscode等IDE的提示更加智慧

js是一門動態弱型別解釋語言,變數宣告後可以改變型別,而且型別需要在執行時才能確定。而ts的報錯提示是在編譯時,不是在執行時。所以使用ts帶來的靜態型別檢查等特性將使得IDE的提示更加完善。

3.型別宣告本身就是非常好的文件

當你接手一個有歷史包袱的專案時,肯定會頭疼於文件和程式碼註釋的缺失,而對於ts來說,是可以做到程式碼即文件的,通過宣告檔案可以知道哪些欄位的含義以及哪些欄位是必填和選填的。舉個簡單例子,當封裝一個button的元件時:


export interface ButtonProps {
  style?: React.CSSProperties
  className?: string
  label?: React.ReactNode
  type?: 'primary' | 'default' | 'search'
  size?: 'sm' | 'md' | 'lg' | 'mini'
  disabled?: boolean
  title?: string
  onClick?: ((e: React.MouseEvent<HTMLButtonElement>) => void)
}
複製程式碼

通過這些宣告檔案可以知道,當使用這個button檔案時,style是一個可選值,表示一個可以自定義樣式的style欄位。type也是一個可選值,表示按鈕的顏色型別,可以選擇'primary','default','mini'其中的一種。disabled也是一個可選值,傳入的值必須是boolean型別。所以就可以看出型別宣告本身就是非常好的文件。

TS結合React使用

類元件的使用

以下是官網的一個例子,建立Props和State介面,Props介面接受name和enthusiasmLevel引數,State介面接受currentEnthusiasm引數。

import * as React from "react";

export interface Props {
  name: string;
  enthusiasmLevel?: number;
}

interface State {
  currentEnthusiasm: number;
}

class Hello extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { currentEnthusiasm: props.enthusiasmLevel || 1 };
  }

  onIncrement = () => this.updateEnthusiasm(this.state.currentEnthusiasm + 1);
  onDecrement = () => this.updateEnthusiasm(this.state.currentEnthusiasm - 1);

  render() {
    const { name } = this.props;

    if (this.state.currentEnthusiasm <= 0) {
      throw new Error('You could be a little more enthusiastic. :D');
    }

    return (
      <div className="hello">
        <div className="greeting">
          Hello {name + getExclamationMarks(this.state.currentEnthusiasm)}
        </div>
        <button onClick={this.onDecrement}>-</button>
        <button onClick={this.onIncrement}>+</button>
      </div>
    );
  }

  updateEnthusiasm(currentEnthusiasm: number) {
    this.setState({ currentEnthusiasm });
  }
}

export default Hello;

function getExclamationMarks(numChars: number) {
  return Array(numChars + 1).join('!');
}

複製程式碼

無狀態元件的使用

無狀態元件也稱為傻瓜元件,如果一個元件內部沒有自身的state,那麼元件就可以稱為無狀態元件。在@types/react已經定義了一個型別type SFC<P = {}> = StatelessComponent

。我們寫無狀態元件的時候,能指定我們的元件為SFC或StatelessComponent。它已經預定義了children,displayName等。 以button為例:

import React from 'react'

const Button = ({ onClick: handleClick, children }) => (
  <button onClick={handleClick}>{children}</button>
)
複製程式碼

如果採用ts來編寫出來的無狀態元件是這樣的:

import React, { MouseEvent, SFC } from 'react';

type Props = { onClick(e: MouseEvent<HTMLElement>): void };

const Button: SFC<Props> = ({ onClick: handleClick, children }) => (
  <button onClick={handleClick}>{children}</button>
);
複製程式碼

readonly

react規定不能通過this.props.xxx和this.state.xxx直接進行修改,所以可以將State和Props標記為不可變資料:

interface Props {
  readonly number: number;
}

interface State {
  readonly color: string;
}

export class Hello extends React.Component<Props, State> {
  someMethod() {
    this.props.number = 123; // Error: props 是不可變的
    this.state.color = 'red'; // Error: 你應該使用 this.setState()
  }
}
複製程式碼

處理Event物件

在工作中,可能經常會使用Event物件,change事件可以使用React.ChangeEvent, click事件可以使用React.ChangeEvent。

  onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
      // do something
  }
  
  onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
      // do something
  }
複製程式碼

可渲染的介面

React 可以渲染一些像 JSX 或者是 string 的內容,這些被合併到型別 React.ReactNode 中,因此,當你接收可渲染的內容時,你可以使用它:

type Props = {
  header: React.ReactNode;
  body: React.ReactNode;
};

class MyComonent extends React.Component<Props, {}> {
  render() {
    return (
      <div>
        {this.props.header}
        {this.props.body}
      </div>
    );
  }
}

<MyComponent header={<h1>Header</h1>} body={<i>body</i>} />
複製程式碼

總結

在大中型前端專案中,由於js的動態弱型別特性,導致很多錯誤在執行時才發現。ts作為js的超集,為前端開發帶來了編譯時的檢查,將很多的錯誤避免在了編譯階段。也為IDE帶來了更強的智慧提示。雖然學習ts會花一些時間,但這些時間是值得的。使用ts開發專案之後,明顯發現專案的可維護性變強了,bug率降低了,查文件也更加方便,一看型別宣告檔案就明白了各個欄位的含義。

參考連結及擴充套件閱讀

TypeScript 2.8下的終極React元件模式

深入理解 TypeScript

TypeScript-React-Starter

相關文章