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的支持更好一些 四、相关的中间件不多,逻辑层业务整合是一个问题