本文基於React+TypeScript+Mobx+AntDesignMobile技術棧,使用Create-React-App腳手架進行一個移動端項目搭建,主要介紹項目搭建過程和相關配置,以及狀態管理工具Mobx的使用,最後實現點擊按鈕數字+1和一個簡單的TodoList小功能,但願能對你們有所幫助。GitHub代碼地址javascript
npm install create-react-app -g
css
create-react-app my-app --typescript
注意:
若是同窗你是參考typescript官方文檔進行react項目搭建,裏面有用create-react-app my-app --scripts-version=react-scripts-ts
命令建立的,千萬別用,如今已通過時了,
webpack版本有問題,後續配置各類想不到的坑 TypeScript中文官網html
引入AntDesignMobile,實現組件按需加載java
本步驟官網有比較詳細的介紹,我就不一一列舉配置過程了,建議你們不要eject暴露全部內建配置,後續版本升級維護可能比較麻煩,推薦使用 react-app-rewired 插件便可配置; AntDesignMobile官網
安裝React路由,狀態管理工具mobx,配置sassnode
npm install history @types/history react-router-dom @types/react-router-dom // 安裝react路由 npm install mobx-react mobx // 安裝mobx npm install node-sass // 安裝sass工具,安裝以後無需另外配置sass,腳手架工具已經集成了
npm run start
Mobx是一個功能強大,基於面向對象編程方式的一個狀態管理工具,上手相對比較容易。就連Redux的做者也曾經向你們推薦過它,對TypeScript支持比較友好,參考官網一張流程圖:
Mobx中文官網react
MobX 爲現有的數據結構(如對象,數組和類實例)添加了可觀察的功能。 經過使用 @observable 裝飾器(ES.Next)來給你的類屬性添加註解就能夠簡單地完成這一切。
import { observable } from "mobx"; class Todo { id = Math.random(); @observable title = ""; @observable finished = false; }
使用 MobX, 你能夠定義在相關數據發生變化時自動更新的值。 經過@computed 裝飾器或者利用 (extend)Observable 時調用 的getter / setter 函數來進行使用,下面代碼中當queue或者refresh發生變化時,MobX會監聽數據變化確保,經過Computed觸發fooProps方法自動更新。
import React from 'react'; import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx"; import {observer} from 'mobx-react'; // 定義數據Store class Store { @observable queue:number = 1; @action refresh = ():void => { this.queue += 1; console.log('this.queue -> ', this.queue); } @computed get fooProps():any { return { queue: this.queue, refresh: this.refresh }; } }
任何應用都有動做。動做是任何用來修改狀態的東西,mobx推薦將修改被觀測變量的行爲放在action中。action只能影響正在運行的函數,而沒法影響當前函數調用的異步操做,參考官方文檔用法:
action(fn) action(name, fn) @action classMethod() {} @action(name) classMethod () {} @action boundClassMethod = (args) => { body } @action(name) boundClassMethod = (args) => { body } @action.bound classMethod() {} action 裝飾器/函數遵循 javascript 中標準的綁定規則。 可是,action.bound 能夠用來自動地將動做綁定到目標對象。 注意,與 action 不一樣的是,(@)action.bound 不須要一個name參數,名稱將始終基於動做綁定的屬性。 class Ticker { @observable tick = 0 @action.bound increment() { this.tick++ // 'this' 永遠都是正確的 } } const ticker = new Ticker() setInterval(ticker.increment, 1000)
import React from 'react'; import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx"; import {observer} from 'mobx-react'; import {Button} from 'antd-mobile'; import './index.scss'; // 定義數據Store,用Mobx做爲狀態管理工具 class Store { @observable queue:number = 1; @action refresh = ():void => { this.queue += 1; console.log('this.queue -> ', this.queue); } @computed get fooProps():any { return { queue: this.queue, refresh: this.refresh }; } } // ts組件接收父組件傳遞過來的數據必須定義接口類型,不然報錯 interface BarProps{ queue :number } // @observer修飾類,Bar組件接受Foo組建傳過來的queue屬性 @observer class Bar extends React.Component<BarProps>{ render() { const {queue} = this.props return ( <div className="queue">{queue}</div> ) } } interface FooProps { queue: number, refresh():void } // Foo組件接收來自Add組件的store數據 @observer class Foo extends React.Component<FooProps>{ render() { const {queue,refresh} = this.props; return ( <div> <Button type="primary" onClick = {refresh}>Refresh</Button> <Bar queue = {queue} /> </div> ) } } // 初始化store數據,傳遞給Foo組件 const store = new Store(); class Add extends React.Component{ render() { return ( <div> <h2 className="add"> hello react-ts-mobx</h2> <Foo queue = {store.queue} refresh = {store.refresh} /> </div> ) } } export default observer(Add)
import React from 'react'; import {observable, isArrayLike, computed, action, autorun, when, reaction,runInAction} from "mobx"; import {observer} from 'mobx-react'; import './index.scss'; // 定義Todo數據類型 class Todo { id:number = new Date().getTime(); title:string = ''; finished:boolean = false; constructor(title:string) { this.title = title; } } // Store數據方法管理 class Store { @observable title:string = ''; @observable todos:Todo[] =[]; // 添加Todo的Title @action createTitle (e:any) { console.log('e',e.target.value); this.title = e.target.value; } // 增長Todo數據 @action createTodo = () => { this.todos.unshift(new Todo(this.title)); this.title = ''; } // 刪除Todo @action delTodo (id:number) { this.todos.forEach((item,index) => { if (item.id === id) { this.todos.splice(index,1) } }) } // 監聽todos數據變化,顯示剩餘待辦數量 @computed get unfinished () { return this.todos.filter(todo => !todo.finished).length; } } interface TodoItemProps { todo:any; store:any; } // 每一條Todo數據組件 @observer class TodoItem extends React.Component<TodoItemProps> { render() { const {todo,store} = this.props return ( <div className="item"> <span>{todo.title}</span> <span onClick={()=> store.delTodo(todo.id)}>X</span> </div> ) } } const store = new Store(); @observer class TodoList extends React.Component { render() { return ( <div> <h2>TodoList</h2> <header> <input type="text" placeholder="please input" value={store.title} onChange = {e => store.createTitle(e)} /> <button onClick ={store.createTodo}>add</button> </header> <ul> {store.todos.map((todo)=>{ return <li key={todo.id}> <TodoItem todo={todo} store = {store}/> </li> })} </ul> <footer> {store.unfinished} item(s) unfinished </footer> </div> ) } } export default TodoList
本人剛接觸TypeScript和Mobx不久,總結學習方法:應該先熟悉一些基本概念後,慢慢的作一些小功能,遇到問題認真思考後再查資料或者請教別人,反覆看文檔,增強對知識的理解;歡迎大佬們留言交流;GitHub代碼地址webpack