若是有任何錯誤,請務必指出。
參考連接:https://github.com/typescript...react
react
:import * as React from 'react'
爲每一個文件建立一個類組件。在一個文件內請儘可能只寫一個類組件,其餘的請使用函數組件git
// ReactTs.tsx class TsExample extends React.Component<any, any> {}
使用 extends
來繼承 React.component
接口github
React.Component
後面帶了 <any, any>
這個奇怪的字符串。這是 React.Component
接口的類型參數。第一個是 props
的類型,第二個是 state
的類型。爲了給 props
和 state
定義類型,咱們可使用 interface
關鍵字。咱們能夠將類型定義寫在當前的組件文件,可是因爲在當前文件中具備了 import
關鍵字,當前文件會成爲一個模塊,即沒法被全局引用。因此建議新建一個文件 index.d.ts
來儲存 interface\type
等typescript
// index.d.ts interface ITsExampleProps { name: string }
注:export
和 import
會將一個文件變成模塊,即裏面的變量不會被暴露到全局數組
接下來咱們爲這個組件初始化 state
app
// ReactTs.tsx class TsExample extends React.Component<ITsExampleProps, ITsExampleState> { public state = { count: 0 } }
此處建議不要 public state = {}
和 constructor
兩種方法混用函數
爲組件添加 UI:每一個類組件都應該有一個 render
方法來進行 UI 渲染。render
方法中必須返回一個 ReactNode
,ReactNode
能夠是一個HTMLElement
或者一個字符串,數字工具
// ReactTs.tsx class TsExample extends React.Component<ITsExampleProps, ITsExampleState> { public state = { count: 0 } public render() { let { count } = this.state return <div styleName="wrap">這裏是 ui</div> } }
爲組件添加並調用方法,使用 state
ui
// ReactTs.tsx class TsExample extends React.Component<IExampleProps, IExampleState> { public state = { count: 0, currentCount: 0 } public plusCount = () => { this.setState({ count: ++this.state.count }) } public render() { let { count } = this.state return ( <div styleName=""> {/* 調用state */} <div>{count}</div> {/* 調用方法 */} <Button onClick={this.plusCount}>增長</Button> <div> <Button onClick={() => { this.showCurrentCount(this.state.count) }} > 當前數量 </Button> </div> </div> ) } }
8) 接下來咱們將該組件做爲一個子組件放到另外一個組件中this
// example.tsx class Example extends React.Component<IExampleProps, IExampleState> { public render() { return ( <div styleName="example"> <ReactTs /> </div> ) } }
爲子組件傳入一個參數
// example.tsx class Example extends React.Component<IExampleProps, IExampleState> { public render() { return ( <div styleName='example'> <ReactTs name="React-Ts" /> </div> ) } }
10) 在子組件中使用參數:this.props.name
// ReactTs.tsx public render() { let {count} = this.state return ( <div styleName='React-Ts'> <div>名字:{this.props.name}</div> <div>增長數量:{count}</div> <div>當前數量:{this.state.currentCount}</div> <div><Button onClick={this.plusCount}>增長</Button></div> <Button onClick={() => { this.showCurrentCount(this.state.count) }}> 當前數量 </Button> </div> ) }
函數式組件類型可使用React.FC
,使用這個類型有個好處就是,提醒你必須返回一個ReactNode
const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => { return <div></div> }
3) 在函數式組件中,咱們沒法像在類組件中同樣使用state
和生命鉤子函數,但React
提供了HOOK
4) 使用 useState
來存儲函數組件的狀態。在下面的例子中,咱們使用了React.useState
,傳入color
的初始值,返回了一個對象,咱們使用解構,得到了color, setColor
。其中 color
至關於 this.state.color
,setColor('green')
至關於 this.setState({color: 'green'})
。
const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => { const [color, setColor] = React.useState('blue') const changeColor = () => { setColor('green') } return ( <div> <div style={{ color }}>函數式組件</div> <Button type="primary" className="example-button" onClick={changeColor}> 點擊換色{' '} </Button> </div> ) }
假如你想像在類組件中使用 componentWillUnmount
,componentDidMount
,componentDidUpdate
,你可使用useEffect
。useEffect
接受一個回調函數,這個回調函數內代碼會在 componentDidMount
,componentDidUpdate
時執行。回調函數的返回值應該是一個函數,這個函數會在 componentWillUnmount
被執行。
const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => { const [color, setColor] = React.useState('blue') const [fontSize, setFontsize] = React.useState('16px') const changeColor = () => { setColor('green') } React.useEffect(() => { let timer = setInterval(() => { setFontsize('100px') }, 10000) return () => { clearInterval(timer) } return ( <div> <div style={{color,fontSize}}>函數式組件</div> <Button type='primary' className='example-button' onClick={changeColor}>點擊換色 </Button> </div> ) }
假如咱們須要操做 DOM
,咱們能夠經過 useRef
來獲取。text.current
就是被咱們綁定的元素。注意:不容許直接操做 DOM,除非無可奈何
const FuncExample: React.FC<IFunExampleProps> = (props: IFunExampleProps) => { const [color, setColor] = React.useState('blue') const [fontSize, setFontsize] = React.useState('16px') const text = React.useRef<HTMLDivElement>(null) const changeColor = () => { setColor('green') } const changeBgC = () => { (text.current as HTMLDivElement).style.backgroundColor = '#e9e9e9' } React.useEffect(() => { let timer = setInterval(() => { setFontsize('100px') }, 10000) return () => { clearInterval(timer) } return ( <div> <div style={{color,fontSize}}>函數式組件</div> <Button type='primary' className='example-button' onClick={changeColor}>點擊換色 </Button> <Button type='primary' className='example-button' onClick={changeBgC}>點擊換背景色</Button> </div> ) }
defaultProps
,這個默認參數是從多個組件的 props
中提取出來的。這時可使用 交叉類型 &
,注意,請不要在交叉類型中使用相同的屬性,就算使用了,也請不要爲兩個同名屬性定義不一樣的基礎類型,這樣將會形成這個屬性須要同時知足兩種基礎類型。type propsType = typeof defaultProps & { count: number } const defaultProps = { name: 'world' } const DefaultPrppsExample = (props: propsType) => { return ( <div> {props.name} {props.count} </div> ) } // 在另外一個組件中使用 ;<DefaultPrppsExample count={1} name={'默認參數示例'} />
createRef
和 forwardRef
在函數式組件中,咱們可使用 useRef
來獲取到 React
元素。在類組件中,咱們可使用 createRef
來獲取 React
元素。當咱們須要獲取某個組件中的元素時,使用 forwardRef
來獲取這個組件內部的元素。仍是不建議直接操縱 DOM
元素
const RefExample = React.forwardRef((props: React.CSSProperties, ref: React.Ref<HTMLInputElement>) => { return <input style={{ ...props}} ref={ref} /> }) class TsExample extends React.Component<ITsExampleProps, ITsExampleState> { ... public state = { inputRef: React.createRef() } public getFocus = () => { if (this.state.inputRef) { // 注意:這裏斷言的類型應該是使用 RefObject,由於其裏面的具備 current: T | null, // 若是使用的是 ReactRef ,這個類型是一個聯合類型,裏面有多是 null ((this.state.inputRef as React.RefObject<HTMLInputElement>).current as HTMLInputElement).focus() } } ... <div styleName="example-item"> <h3>使用 createRef 和 forwardRef</h3> <RefExample color='red' ref={this.state.inputRef} /> <Button onClick={this.getFocus}>獲取焦點</Button> </div> ... }
3) React
默認會把咱們的子組件掛載到父組件上。當咱們想本身指定掛載的元素時,就須要用到 createPortal
了。
這裏寫了一個簡陋的蒙層組件(樣式未徹底實現),即便咱們是TsExample
中使用了這個組件,但這個組件仍是會被掛載到一個插入 body
的元素上
注意:這裏咱們在父組件上綁定了一個點擊事件,PortalExample
被點擊時依然會冒泡進而觸發父組件的點擊事件
const PortalExample = (props: IPortarExampleProps) => { const [modelDOM, setModelDOM] = React.useState(document.createElement('div')) modelDOM.classList.add('modal') if (props.visible) { document.body.append(modelDOM) return ReactDOM.createPortal( <div styleName='modal-inner'> 蒙層 </div>, modelDOM) }else { if (modelDOM.parentNode) { modelDOM.parentNode.removeChild(modelDOM) } return null } } class TsExample extends React.Component<ITsExampleProps, ITsExampleState> { ... <div styleName="example-item" onClick={this.handleModalClick}> <h1>使用 createPortal 將子組件掛載到父組件之外的元素內</h1> <PortalExample visible={this.state.modalVisible} /> <Button onClick={this.toggleModal}>切換modal</Button> </div> ... }
TS
類型基本使用咱們在使用類型註解的時候每每會使用如下類型
js
的七種基本類型 string, number, boolean, undefined, null, symbol
,還有 void, never
type, interface
來聲明變量的形狀使用 |
來創造一個聯合類型。例如:type pet = 'cat' | 'dog'
當咱們須要規定一個函數參數的類型時,又恰巧有一個已經有一個接口已經知足了,咱們可使用 keyof
來快速生成一個聯合類型
當咱們在使用聯合類型時,假如兩個子類型 A 和 B 都是接口或類等時,咱們預想的是要麼是 A,要麼是 B。但實際上,A&B
也是符合 A|B
的。
這個時候咱們須要先用 in
關鍵字來判斷是符合哪一個類型再進行操做
interface IMan { handsNum: number } interface IDog { tailNum: number } public typeGuarding = (obj: IMan | IDog) => { if ('handsNum' in obj) { ... } else if ('tailNum' in obj) { ... } }
使用 &
來創造一個聯合類型,以前已經有示例了
可選類型其實不是一個類型,但在接口和函數參數中都有可選這個概念。
interface IPet { name: string age?: number } interface IMan { ... } const feedPet (pet: IPet, man?: IMan) => { ... }
注意:函數的可選參數必須寫在最後面
在函數聲明中,直接爲參數賦予一個值,能夠起到默認參數的做用
interface IPetDefault { name: string age = 1 } const feedPet (pet: IPet, man={name: '我'}) => { ... }
當咱們須要使用枚舉類型時,就須要用到enum
關鍵字。默認,第一個的值爲 0,而後遞增,能夠手動指定每個的值,以後的值也會遞增。咱們能夠經過值查key
,也能夠經過key
查值
enum animal { 烏龜, 鱷魚, 麻雀, 河馬 } animal['烏龜'] // 0 animal[0] // 烏龜
當咱們給一個函數傳入參數時,咱們知道這個參數時符合被規定的參數類型的,但編譯器是不知道的。爲了讓編譯器能知道類型符合,咱們就須要使用類型斷言,關鍵字:as
type animalName = '烏龜' | '鱷魚' | '麻雀' | '河馬' animal[this.state.animalName as animalName] // 0
類型斷言裏還有另外一個類型:在值的後面使用 !
,表示這個值不爲 null
type animalNameWithNull = '烏龜' | '鱷魚' | '麻雀' | '河馬' | null animal[(this.state.animalName as animalName)!] // 0
注:類型擴展是直譯的結果,若是你知道這種用法的名字,請修正它
TS
在檢查一個類型是否符合是,並非查找類型的名字,而是比較類型的結構。也就是說,當你爲兩種類型userId
和 orderId
都定義了同樣的結構:{name: string}
。這時候,TS
會斷定它們是同一種類型。請看下面的例子:
interface IOrderId { name: string } interface IUserId { name: string } let userId: IUserId = { name: 'userId' } let orderId: IOrderId = userId // ok
這裏你是否會疑惑呢?兩種不一樣的類型卻能夠相互賦值。這就是 結構類型檢查 了。假如名字不一樣,類型就不一樣的就是 名稱類型檢查
那咱們如何來避免呢?第一種方法是多加一個字段
interface MyUserId { name: string type: 'user' } interface MyOrderId { name: string type: 'order' } let myUserId: MyUserId = { name: 'user', type: 'user' } let myOrderId: MyOrderId = myUserId // error
還有另外一種方法,那就是利用 unique symbol
和 交叉類型
type UserIdString = string & { readonly brand: unique symbol } type OrderIdString = string & { readonly brand: unique symbol } const getUniqueUserId = (id: string) => { return id as UserIdString } const getUniqueOrderId = (id: string) => { return id as OrderIdString } let uniqueUserId: UserIdString = getUniqueUserId('1') let uniqueOrderId: OrderIdString = uniqueUserId // error
unique symbol
是 symbol
的子類型。咱們在上面用交叉類型將 {readonly brand: unique symbol}
也加入到類型中去,這樣,兩種類型就算結構同樣,它們也是不相同的
函數重載用於咱們的函數擁有不一樣的輸入或輸出時
第一個函數聲明應該是最精確的,由於編譯器是從前日後開始匹配的。函數實現應該是兼容各個函數聲明的。
這裏函數重載的意義在於,若是們直接使用第三種聲明,那麼傳入 3 個參數也是能夠接受的,可是據咱們所知, padding 是不接受 3 個參數的
// 重載 function padding(all: number) function padding(topAndBottom: number, leftAndRight: number) function padding(top: number, right: number, bottom: number, left: number) // Actual implementation that is a true representation of all the cases the function body needs to handle function padding(a: number, b?: number, c?: number, d?: number) { if (b === undefined && c === undefined && d === undefined) { b = c = d = a } else if (c === undefined && d === undefined) { c = a d = b } return { top: a, right: b, bottom: c, left: d } }
typeof
關鍵字快速定義一個類型有時候,咱們在前面定義了一個變量,並使用類型推導(也就是說,這時候咱們並無明確地給它聲明類型)。而後咱們想要使用這個變量的類型了,怎麼辦?回去定義一下類型嗎?使用 typeof 變量名
就能夠獲取到這個變量的類型了
TS
中內置了一些語法糖給咱們使用,例如 Partial、Omit、Exclude
等
這裏舉例 partial
的用法:Patial
關鍵字可讓將一個接口內部的屬性變成可選
interface dog { name: string bark: boolean } type littleDog = Partial<dog> let dog1: littleDog = { name: 'dog1' } let dog2: littleDog = { bark: false }
當咱們須要引入某些模塊時,這些模塊又沒有被聲明過,這個時候就會報錯了。咱們只須要使用
decleare module 模塊名
就可解決
decleare module "*.png" import * as logo from 'logo.png'