Typescript結(jié)合React實(shí)踐
作者:慕晨同學(xué)
原文地址:https://juejin.im/post/5d37b5d9f265da1bd605e5e1
寫在前面
Typescript是JavaScript的一個(gè)超集,主要提供了類型系統(tǒng)和對(duì)es6的支持。本人使用ts編寫react將近3個(gè)月的時(shí)間,中間踩了不少坑,從剛開始的覺得ts沒有必要到現(xiàn)在覺得ts真香。在這里對(duì)使用ts編寫react的心得做一下總結(jié)。
本文將從以下幾部分進(jìn)行總結(jié):
Typescript的優(yōu)勢(shì)
TS結(jié)合React使用
總結(jié)
參考鏈接及擴(kuò)展閱讀
Typescript的優(yōu)勢(shì)
1.幫助更好地重構(gòu)代碼
一個(gè)好的代碼習(xí)慣是常常對(duì)自己寫的代碼進(jìn)行小的重構(gòu),使得代碼可維護(hù)性更強(qiáng)。但是對(duì)于很多線上運(yùn)行的代碼,代碼測(cè)試覆蓋率往往不是很高,有時(shí)候哪怕一個(gè)變量名的改動(dòng),都會(huì)牽一發(fā)而動(dòng)全身。而對(duì)于使用ts編寫的項(xiàng)目就不會(huì)有這種擔(dān)心。ts的靜態(tài)檢查特性會(huì)幫助找出代碼中有錯(cuò)誤的部分。
2.vscode等IDE的提示更加智能
js是一門動(dòng)態(tài)弱類型解釋語(yǔ)言,變量聲明后可以改變類型,而且類型需要在運(yùn)行時(shí)才能確定。而ts的報(bào)錯(cuò)提示是在編譯時(shí),不是在運(yùn)行時(shí)。所以使用ts帶來的靜態(tài)類型檢查等特性將使得IDE的提示更加完善。
3.類型聲明本身就是非常好的文檔
當(dāng)你接手一個(gè)有歷史包袱的項(xiàng)目時(shí),肯定會(huì)頭疼于文檔和代碼注釋的缺失,而對(duì)于ts來說,是可以做到代碼即文檔的,通過聲明文件可以知道哪些字段的含義以及哪些字段是必填和選填的。舉個(gè)簡(jiǎn)單例子,當(dāng)封裝一個(gè)button的組件時(shí):
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)
}
通過這些聲明文件可以知道,當(dāng)使用這個(gè)button文件時(shí),style是一個(gè)可選值,表示一個(gè)可以自定義樣式的style字段。type也是一個(gè)可選值,表示按鈕的顏色類型,可以選擇'primary','default','mini'其中的一種。disabled也是一個(gè)可選值,傳入的值必須是boolean類型。所以就可以看出類型聲明本身就是非常好的文檔。
TS結(jié)合React使用
類組件的使用
以下是官網(wǎng)的一個(gè)例子,創(chuàng)建Props和State接口,Props接口接受name和enthusiasmLevel參數(shù),State接口接受currentEnthusiasm參數(shù)。
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('!');
}
無(wú)狀態(tài)組件的使用
無(wú)狀態(tài)組件也稱為傻瓜組件,如果一個(gè)組件內(nèi)部沒有自身的state,那么組件就可以稱為無(wú)狀態(tài)組件。在@types/react已經(jīng)定義了一個(gè)類型type SFC<P = {}> = StatelessComponent
。我們寫無(wú)狀態(tài)組件的時(shí)候,能指定我們的組件為SFC或StatelessComponent。它已經(jīng)預(yù)定義了children,displayName等。以button為例:
import React from 'react'
const Button = ({ onClick: handleClick, children }) => (
<button onClick={handleClick}>{children}</button>
)
如果采用ts來編寫出來的無(wú)狀態(tài)組件是這樣的:
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規(guī)定不能通過this.props.xxx和this.state.xxx直接進(jìn)行修改,所以可以將State和Props標(biāo)記為不可變數(shù)據(jù):
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: 你應(yīng)該使用 this.setState()
}
}
處理Event對(duì)象
在工作中,可能經(jīng)常會(huì)使用Event對(duì)象,change事件可以使用React.ChangeEvent, click事件可以使用React.ChangeEvent。
onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
// do something
}
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// do something
}
可渲染的接口
React 可以渲染一些像 JSX 或者是 string 的內(nèi)容,這些被合并到類型 React.ReactNode 中,因此,當(dāng)你接收可渲染的內(nèi)容時(shí),你可以使用它:
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>} />
總結(jié)
在大中型前端項(xiàng)目中,由于js的動(dòng)態(tài)弱類型特性,導(dǎo)致很多錯(cuò)誤在運(yùn)行時(shí)才發(fā)現(xiàn)。ts作為js的超集,為前端開發(fā)帶來了編譯時(shí)的檢查,將很多的錯(cuò)誤避免在了編譯階段。也為IDE帶來了更強(qiáng)的智能提示。雖然學(xué)習(xí)ts會(huì)花一些時(shí)間,但這些時(shí)間是值得的。使用ts開發(fā)項(xiàng)目之后,明顯發(fā)現(xiàn)項(xiàng)目的可維護(hù)性變強(qiáng)了,bug率降低了,查文檔也更加方便,一看類型聲明文件就明白了各個(gè)字段的含義。
