npm i mobx mobx-react
複製代碼
npm i typescript
複製代碼
在package.json中輸入以下依賴,並從新執行npm installhtml
"devDependencies": { "@babel/core": "^7.5.5", "@babel/plugin-proposal-decorators": "^7.4.4", "@babel/preset-typescript": "^7.3.3", "@babel/runtime": "^7.5.5", "@types/react-dom": "^16.8.5", "@types/react": "^16.9.2", "@types/react-native": "^0.60.7", "babel-jest": "^24.9.0", "babel-plugin-transform-class-properties": "^6.24.1", "jest": "^24.9.0", "metro-react-native-babel-preset": "^0.56.0", "react-test-renderer": "16.8.6" }, 複製代碼
在.babelrc文件中應用如下插件腳本,如沒有則在工程根目錄新建java
{ "presets": [ "@babel/preset-typescript", [ "module:metro-react-native-babel-preset" ] ], "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ], "transform-class-properties" ] } 複製代碼
在tsconfig.json文件中應用如下校驗配置,如沒有則在工程根目錄新建node
{ "compilerOptions": { "target": "es2017", "module": "commonjs", "jsx": "preserve", "strict": true, "noImplicitAny": false, "moduleResolution": "node", "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "esModuleInterop": true, "removeComments": false, "resolveJsonModule": true, "isolatedModules": true, "allowJs": true, "checkJs": true }, "exclude": [ "node_modules" ], "types": [ "react", "react-dom", "react-native" ] } 複製代碼
具體字段含義參照www.html.cn/doc/typescr…react
import { observable, autorun } from 'mobx'; const value = observable(0); const number = observable(100); autorun(() => { console.log(value.get()); }); value.set(1); value.set(2); number.set(101); 複製代碼
能夠看到,控制檯中依次輸出0,1,2。 observable能夠用來觀測一個數據,這個數據能夠數字、字符串、數組、對象等類型(相關知識點具體會在後文中詳述),而當觀測到的數據發生變化的時候,若是變化的值處在autorun中,那麼autorun就會自動執行。 上例中的autorun函數中,只對value值進行了操做,而並無number值的什麼事兒,因此number.set(101)這步並不會觸發autorun,只有value的變化才觸發了autorun。typescript
假如如今咱們一個數字,但咱們對它的值不感興趣,而只關心這個數組是否爲正數。這個時候咱們就能夠用到computed這個屬性了。npm
const number = observable(10); const plus = computed(() => number.get() > 0); autorun(() => { console.log(plus.get()); }); number.set(-19); number.set(-1); number.set(1); 複製代碼
依次輸出了true,false,true。 第一個true是number初始化值的時候,10>0爲true沒有問題。 第二個false將number改變爲-19,輸出false,也沒有問題。 可是當-19改變爲-1的時候,雖然number變了,可是number的改變實際上並無改變plus的值,因此沒有其它地方收到通知,所以也就並無輸出任何值。 直到number從新變爲1時才輸出true。json
實際項目中,computed會被普遍使用到。react-native
mobx推薦將修改被觀測變量的行爲放在action中。 來看看如下例子:api
import {observable, action} from 'mobx'; class Store { @observable number = 0; @action add = () => { this.number++; } } const newStore = new Store(); newStore.add(); 複製代碼
以上例子使用了ES7的decorator,在實際開發中很是建議用上它,它能夠給你帶來更多的便捷數組
接下來講一個重點action只能影響正在運行的函數,而沒法影響當前函數調用的異步操做 好比官網中給了以下例子
@action createRandomContact() { this.pendingRequestCount++; superagent .get('https://randomuser.me/api/') .set('Accept', 'application/json') .end(action("createRandomContact-callback", (error, results) => { if (error) console.error(error); else { const data = JSON.parse(results.text).results[0]; const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture); contact.addTag('random-user'); this.contacts.push(contact); this.pendingRequestCount--; } })); } 複製代碼
重點關注程序的第六行。在end中觸發的回調函數,被action給包裹了,這就很好驗證了上面加粗的那句話,action沒法影響當前函數調用的異步操做,而這個回調毫無疑問是一個異步操做,因此必須再用一個action來包裹住它,這樣程序纔不會報錯。
若是你使用async function來處理業務,那麼咱們可使用runInAction這個API來解決以前的問題。
import {observable, action, useStrict, runInAction} from 'mobx'; useStrict(true); class Store { @observable name = ''; @action load = async () => { const data = await getData(); runInAction(() => { this.name = data.name; }); } } 複製代碼
在React中,咱們通常會把和頁面相關的數據放到state中,在須要改變這些數據的時候,咱們會去用setState這個方法來進行改變。 先設想一個最簡單的場景,頁面上有個數字0和一個按鈕。點擊按鈕我要讓這個數字增長1,就讓咱們要用Mobx來處理這個試試。
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' import React, {PureComponent} from 'react' import {observer} from 'mobx-react' import {action, observable} from 'mobx' class MyState { @observable num = 1 @action public addNum() { this.num++ } } const state = new MyState() interface Props { } interface State { } /** * 註釋: * 時間: 2019/8/21 0021 11:52 * @author 郭翰林 */ @observer export default class App extends PureComponent<Props, State> { static propTypes = {} constructor(props) { super(props) } render() { return ( <View style={{flex: 1, justifyContent: "space-between", alignItems: "center"}}> <View> <Text style={{color: 'red', fontSize: 18, fontWeight: "bold"}}> {state.num} </Text> </View> <TouchableOpacity style={styles.buttonStyle} onPress={() => { state.addNum() }}> <Text style={{color: '#ffffff', fontSize: 14}}> 增長 </Text> </TouchableOpacity> </View> ) } } const styles = StyleSheet.create({ buttonStyle: { justifyContent: "center", alignItems: "center", width: 250, borderRadius: 8, height: 50, marginBottom: 25, backgroundColor: '#fc704e' } }) 複製代碼
上例中咱們使用了一個MyState類,在這個類中定義了一個被觀測的num變量和一個action函數addNum來改變這個num值。 以後咱們實例化一個對象,叫作newState,以後在個人React組件中,我只須要用@observer修飾一下組件類,即可以愉悅地使用這個newState對象中的值和函數了。
在不使用其它框架、類庫的狀況下,React要實現跨組件交互這一功能相對有些繁瑣。一般咱們須要在父組件上定義一個state和一個修改該state的函數。而後把state和這個函數分別傳到兩個子組件裏,在邏輯簡單,且子組件不多的時候可能還好,但當業務複雜起來後,這麼寫就很是繁瑣,且難以維護。而用Mobx就能夠很好地解決這個問題。來看看如下的例子:
class MyState { @observable num1 = 0; @observable num2 = 100; @action addNum1 = () => { this.num1 ++; }; @action addNum2 = () => { this.num2 ++; }; @computed get total() { return this.num1 + this.num2; } } const newState = new MyState(); const AllNum = observer((props) => <div>num1 + num2 = {props.store.total}</div>); const Main = observer((props) => ( <div> <p>num1 = {props.store.num1}</p> <p>num2 = {props.store.num2}</p> <div> <button onClick={props.store.addNum1}>num1 + 1</button> <button onClick={props.store.addNum2}>num2 + 1</button> </div> </div> )); @observer export default class App extends React.Component { render() { return ( <div> <Main store={newState} /> <AllNum store={newState} /> </div> ); } } 複製代碼
有兩個子組件,Main和AllNum (均採用無狀態函數的方式聲明的組件) 在MyState中存放了這些組件要用到的全部狀態和函數。 以後只要在父組件須要的地方實例化一個MyState對象,須要用到數據的子組件,只須要將這個實例化的對象經過props傳下去就行了。
那若是組件樹比較深怎麼辦呢? 查看最新Mobx5的@inject屬性
一般,在和Mobx數據有關聯的時候,你須要給你的React組件加上@observer,你沒必要太擔憂性能上的問題,加上這個@observer不會對性能產生太大的影響,並且@observer還有一個相似於pure render的功能,甚至能起到性能上的一些優化。
所謂pure render見下例:
@observer export default class App extends React.Component { state = { a: 0, }; add = () => { this.setState({ a: this.state.a + 1 }); }; render() { return ( <div> {this.state.a} <button onClick={this.add}>+1</button> <PureItem /> </div> ); } } @observer class PureItem extends React.Component { render() { console.log('PureItem的render觸發了'); return ( <div>大家的事情跟我不要緊</div> ); } } 複製代碼
若是去掉子組件的@observer,按鈕每次點擊,控制檯都會輸出 PureItem的render觸發了 這句話。
@observer class MyComponent extends React.Component { state = { a: 0 }; @observable b = 1; render() { return( <div> {this.state.a} {this.b} </div> ) } } 複製代碼
在添加@observer後,你的組件會多一個生命週期componentWillReact。當組件內被observable觀測的數據改變後,就會觸發這個生命週期。 注意setState並不會觸發這個生命週期!state中的數據和observable數據並不算是一類。
另外被observable觀測數據的修改是同步的,不像setState那樣是異步,這點給咱們帶了很大便利。
Mobx想要入門上手能夠說很是簡單,只須要記住少許概念並能夠完成許多基礎業務了。但深刻學習下去,也仍是要接觸許多概念的。例如Modifier、Transation等等。 最後與Redux作一個簡單的對比
一、Mobx寫法上更偏向於OOP 二、對一份數據直接進行修改操做,不須要始終返回一個新的數據 三、對typescript的支持更好一些 四、相關的中間件不多,邏輯層業務整合是一個問題