本文詳細介紹瞭如何使用 Create-React-App 編寫 TypeScript + React 項目css
對於 TypeScript + React 開發,MicroSoft 編寫了一個 TypeScript-React-Starter 的例子,Github 地址。有須要的朋友能夠去看一下。html
我本身也看了一下,文檔說明講解的很好,可是 Demo 拉下來卻沒法正常運行,一直報錯。因此我本身使用 TypeScript + React + Redux 寫了 Demo,做爲範例來用一下。node
本文 Counter Demo 是一個簡易的例子,能夠用來做爲入門參考,Counter Demoreact
另外還寫了一個 TodoList 的例子,稍微更有難度一些,代碼量和組件更多更詳細。有須要的朋友也能夠參考一下。TodoList Demogit
能夠先下載 Counter Demo 後,運行項目,查看運行效果,而後對照本文進行閱讀,效果更佳!github
TypeScript
編寫 React
須要注意的規範必須遵照的要求:typescript
jsx
語法的文件都須要以 tsx
後綴命名Component<P, S>
泛型參數聲明,來代替 PropTypes
進行類型校驗額外的代碼規範:npm
window
對象屬性,統一在項目根下的 global.d.ts
中進行聲明定義types/
目錄下定義好其結構化類型聲明$ npm install create-react-app -g
複製代碼
$ create-react-app typescript-react-app --scripts-version=react-scripts-ts
複製代碼
react-scripts-ts是一系列適配器,它利用標準的create-react-app工程管道並把TypeScript混入進來。json
項目建立成功後,此時項目結構以下所示:redux
my-app/
├─ node_modules/
├─ public/
├─ src/
│ └─ ...
├─ .gitignore
├─ images.d.ts
├─ package.json
├─ README.md
├─ tsconfig.json
├─ tsconfig.prod.json
├─ tsconfig.test.json
├─ tslint.json
└─ yarn.lock
複製代碼
先運行項目,看看是否可以正常啓動,若是能夠,說明項目建立沒有問題。 運行命令:
$ npm run start
# 或者運行 yarn run start
複製代碼
在當前項目中,能夠看到 index.tsx 和 App.jsx 文件中已經使用了 TypeScript,咱們如今本身來用 TypeScript 編寫一個 React 組件吧。
咱們在 src 下建立一個 components 目錄,新增 Counter 組件:
Counter.tsx
import * as React from 'react';
// 建立類型接口
export interface Iprops {
value: number
}
// 使用接口代替 PropTypes 進行類型校驗
const Counter = ({ value }: Iprops) => {
return <p>Clicked: { value } times</p>
}
export default Counter;
複製代碼
import * as React from 'react';
import './App.css';
import Counter from './components/Counter.jsx';
// import logo from './logo.svg';
class App extends React.Component {
public render() {
return (
<div className="App">
<Counter value={ 0 } />
</div>
);
}
}
export default App;
複製代碼
運行項目:npm run start
,能夠看到瀏覽器中展現出了 Clicked: 0 times
,說明咱們第一個 Counter 組件已經編寫並使用成功了。
剛纔是使用函數組件的方式定義的 Counter 組件,如今咱們使用類的方式來改寫一下。兩種方式都試一試:
Counter.tsx
import * as React from 'react';
// 建立類型接口
export interface IProps {
value: number
}
// 使用接口代替 PropTypes 進行類型校驗
export default class Counter extends React.PureComponent<IProps> {
public render() {
return <p>Clicked: { this.props.value } times</p>
}
}
複製代碼
安裝redux和react-redux以及它們的類型文件作爲依賴。
$ npm install -S redux react-redux @types/react-redux
複製代碼
這裏咱們不須要安裝@types/redux,由於Redux已經自帶了聲明文件(.d.ts文件)。
通常會將經常使用的結構類型存放到 /types 目錄下。因此咱們在 src 目錄下新建 types 目錄。 此時項目中只有一個 state,就是 Counter 中的點擊次數,因此就沒有使用藉口來做爲約束,而是直接使用了 type。
type/index.tsx
// 定義 State 結構類型
export type StoreState = number;
複製代碼
在 src 下建立 constants 目錄,在 index.tsx 文件中添加須要響應的消息類型
constants/index.tsx
// 定義增長 state 類型常量
export const INCREMENT = "INCREMENT";
export type INCREMENT = typeof INCREMENT;
// 定義減小 state 類型常量
export const DECREMENT = "DECREMENT";
export type DECREMENT = typeof DECREMENT;
複製代碼
這裏的const/type模式容許咱們以容易訪問和重構的方式使用TypeScript的字符串字面量類型。 接下來,咱們建立一些 actions 以及建立這些 actions 的函數,src/actions/index.tsx。
actions/index.tsx
export interface IINCREMENTAction {
type: INCREMENT;
}
export interface IDECREMENTAction {
type: DECREMENT;
}
// 定義 modifyAction 類型,包含 IINCREMENTAction 和 IDECREMENTAction 接口類型
export type ModifyAction = IINCREMENTAction | IDECREMENTAction;
// 增長 state 次數的方法
export const increment = (): IINCREMENTAction => ({
type: INCREMENT,
})
// 減小 state 次數的方法
export const decrement = (): IDECREMENTAction => ({
type: DECREMENT
})
複製代碼
actions/index.tsx
中定義了兩個類型,分別負責添加和減小操做的行爲。咱們還定義了一個類型(ModifyAction
),它描述了哪些 action
是能夠增長或減小的。 最後,咱們定義了兩個函數用來建立實際的 actions
。
咱們的reducer將放在src/reducers/index.tsx文件裏。 它的功能是保證增長操做會讓 times
加1,減小操做則要將 times
減1。
reducers/index.tsx
import { ModifyAction } from '../actions';
import { DECREMENT, INCREMENT } from '../constants';
// 處理並返回 state
export default (state = 0, action: ModifyAction): number => {
switch (action.type) {
case INCREMENT:
return state + 1
case DECREMENT:
return state - 1
default:
return state
}
}
複製代碼
以前咱們已經使用了 Counter 組件,可是這個組件是一個純組件,此時咱們須要一個組件將 Counter 和 數據鏈接起來。咱們先修改一下原先的 Counter 組件,在其中添加一些操做按鈕
components/Counter.tsx
import * as React from 'react';
// 建立類型接口
export interface IProps {
value: number,
onIncrement: () => void,
onDecrement: () => void
}
// 使用接口代替 PropTypes 進行類型校驗
export default class Counter extends React.PureComponent<IProps> {
public render() {
const { value, onIncrement, onDecrement } = this.props;
return (
<p>
Clicked: { value } times
<br />
<br />
<button onClick={ onIncrement } style={{ marginRight: 20 }}> + </button>
<button onClick={ onDecrement }> - </button>
</p>
)
}
}
複製代碼
而後咱們再建立一個 container
目錄,用來存放須要與數據交互的組件,新建 CounterCon.tsx
文件.
兩個關鍵點是初始的 Counter
組件和 react-redux
的 connect
函數。 connect
能夠將咱們的 Counter
組件轉換成一個容器,經過如下兩個函數:
container/CounterCon.tsx
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { decrement, increment } from '../actions';
import Counter from '../components/Counter';
import { StoreState } from '../types';
// 將 reducer 中的狀態插入到組件的 props 中
const mapStateToProps = (state: StoreState): { value: number } => ({
value: state
})
// 將 對應action 插入到組件的 props 中
const mapDispatchToProps = (dispatch: Dispatch) => ({
onDecrement: () => dispatch(decrement()),
onIncrement: () => dispatch(increment())
})
// 使用 connect 高階組件對 Counter 進行包裹
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
複製代碼
讓咱們回到src/index.tsx。 要把全部的東西合到一塊兒,咱們須要建立一個帶初始狀態的store,並用咱們全部的reducers來設置它。 而且使用 react-redux 的 Provider 將 props 和 容器鏈接起來
index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import App from './App';
import './index.css';
import reducer from './reducer';
import registerServiceWorker from './registerServiceWorker';
// 一、建立 store
const store = createStore(reducer);
ReactDOM.render(
// 二、而後使用react-redux的Provider將props與容器連通起來
<Provider store={ store }>
<App />
</Provider> ,
document.getElementById('root') as HTMLElement
);
registerServiceWorker();
複製代碼
回到咱們的 App.jsx 文件中,以前咱們引用的是 components 中的 Counter 組件,可是此時咱們須要使用的是與數據有交互的 CounterCon 組件。改寫以下:
App.jsx
import * as React from 'react';
import './App.css';
// 引入 container 組件 CountCon
import CountCon from './container/CountCon';
// import logo from './logo.svg';
class App extends React.Component {
public render() {
return (
<div className="App"> <CountCon /> </div>
);
}
}
export default App;
複製代碼
注意,此時 CountCon
再也不須要 props
了,由於咱們使用了 connect
函數爲包裹起來的 Hello
組件的 props
適配了應用的狀態。
此時,運行項目,點擊 + 或者 - 按鈕,便可看到 times
的次數會發生變化。
至此,對於使用 TypeScript
編寫 React
應用應該有了必定的瞭解。其實寫法也比較固定,剛接觸的話可能有些地方容易出現問題,多寫幾個組件以後,應該就沒什麼問題了。 在編寫項目的過程當中,create-react-app
自帶的 tslint
可能要求比較嚴嚴格,好比:
tslint.json
文件 rules
屬性中添加:"jsx-no-lambda": false
便可tslint.json
文件 rules
屬性中添加:"ordered-imports": false
便可還有不少別的配置,有須要的話,能夠查看文檔:TSLint core rules。
本文 Counter Demo
是一個簡易的例子,能夠用來做爲入門參考:Counter Demo
另外還寫了一個 TodoList
的例子,稍微更有難度一些,代碼量和組件更多更詳細。有須要的朋友也能夠參考一下:TodoList Demo