Mobx运用在react

准备:

  • 此文章里的demo以create-react-app,vue-cli的默认demo为基础。
  • mobx需要es7的装饰器语法,所以需要对项目进行一定的修改与配置,需要先eject。如果不eject也可以,但是不推荐,方法也在后面引用的链接里。详见create-react-app + mobx其中@observer装饰器报错

demo功能很简单,效果及说明如下:

说明:

  1. full name = first name + last name,且响应式显示
  2. 点reset按钮,清空所有数据
  3. fist name,last name都为空时,full name显示提示语

分析:

变量:firstName,lastName

方法:getFullName、doReset(为了方便,react还要封装一个setInputValue方法,让input监听onchange时调用)

react、vue、react + mobx三种方案的代码实现对比:

react实现:

 
  1. import React, { Component } from 'react';

  2. import ReactDOM from 'react-dom';

  3. import registerServiceWorker from './registerServiceWorker';

  4.  
  5. class App extends Component {

  6. constructor(props) {

  7. super(props);

  8. this.state = {

  9. firstName: '',

  10. lastName: ''

  11. }

  12. }

  13.  
  14. setValue(key, event) {

  15. this.setState({

  16. [key]: event.target.value

  17. });

  18. }

  19.  
  20. getFullName() {

  21. let { firstName, lastName } = this.state;

  22. if (!firstName && !lastName) {

  23. return 'Please input your name!'

  24. } else {

  25. return firstName + ' ' + lastName;

  26. }

  27. }

  28.  
  29. doReset() {

  30. this.setState({

  31. firstName: '',

  32. lastName: ''

  33. })

  34. }

  35.  
  36. render() {

  37. const st = this.state;

  38. const fullName = this.getFullName();

  39. return (

  40. <div>

  41. <h1>This is normal react!</h1>

  42. <p>First name: <input type="text" value={st.firstName} onChange={e => this.setValue('firstName', e)} /></p>

  43. <p>Last name: <input type="text" value={st.lastName} onChange={e => this.setValue('lastName', e)} /></p>

  44. <p>Full name: {fullName}</p>

  45. <p><button onClick={() => { this.doReset() }}>Reset</button></p>

  46. </div>

  47. );

  48. }

  49. }

  50.  
  51. ReactDOM.render(<App />, document.getElementById('root'));

  52. registerServiceWorker();

vue实现:

 
  1. <template>

  2. <div>

  3. <h1>This is vue!</h1>

  4. <p>First name: <input type="text" v-model="firstName" /></p>

  5. <p>Last name: <input type="text" v-model="lastName" /></p>

  6. <p>Full name: {{fullName}}</p>

  7. <p><button @click="doReset">Reset</button></p>

  8. </div>

  9. </template>

  10.  
  11. <script>

  12. export default {

  13. data() {

  14. return {

  15. firstName: "",

  16. lastName: ""

  17. };

  18. },

  19. computed: {

  20. fullName() {

  21. const { firstName, lastName } = this;

  22. if (!firstName && !lastName) {

  23. return "Please input your name!";

  24. } else {

  25. return firstName + " " + lastName;

  26. }

  27. }

  28. },

  29. methods: {

  30. doReset() {

  31. this.firstName = "";

  32. this.lastName = "";

  33. }

  34. }

  35. };

  36. </script>

对比:

  • 模板,vue因为有自己的语法糖,所以较简洁,这点优势不算大。但有一点需要注意一下,react要生成fullName需要在render里调用getFullName方法,这个其实有点让render不太“纯”(只负责渲染,业务逻辑尽量少)了。而vue引入了computed的概念,值得借鉴。
  • 方法,按照性质来说,方法可以分为两类,就像vue把方法分成了computed和methods。而react的所有方法都写在一起,当方法特别多的时候,比较难区分,虽然可以通过一些约定来形成一些规范,但是实在难保证效果,vue的思想值得借鉴。
  • 赋值,react需要setState,vue则直接赋值(起码在语法上是),虽然觉得react这种写法可以使数据在变化时,特别容易识别,但是写起来还是有点麻烦。。。

区别还能说出来更多,但是最主要的就是这几点,那么当react遇到mobx时会发生什么呢?

mobx + react实现:

 
  1. import React, { Component } from 'react';

  2. import ReactDOM from 'react-dom';

  3. import registerServiceWorker from './registerServiceWorker';

  4. import { observable, computed, useStrict, action } from 'mobx';

  5. import { observer } from 'mobx-react';

  6. useStrict(true)

  7.  
  8. class VM {

  9. @observable firstName = '';

  10. @observable lastName = '';

  11.  
  12. @computed get fullName() {

  13. const { firstName, lastName } = this;

  14. if (!firstName && !lastName) {

  15. return 'Please input your name!'

  16. } else {

  17. return firstName + ' ' + lastName;

  18. }

  19. };

  20.  
  21. @action.bound

  22. setValue(key, event) {

  23. this[key] = event.target.value;

  24. }

  25. @action.bound

  26. doReset() {

  27. this.firstName = '';

  28. this.lastName = '';

  29. }

  30. }

  31.  
  32. @observer

  33. class App extends Component {

  34. render() {

  35. const vm = this.props.vm;

  36. return (

  37. <div>

  38. <h1>This is mobx-react!</h1>

  39. <p>First name: <input type="text" value={vm.firstName} onChange={e => vm.setValue('firstName', e)} /></p>

  40. <p>Last name: <input type="text" value={vm.lastName} onChange={e => vm.setValue('lastName', e)} /></p>

  41. <p>Full name: {vm.fullName}</p>

  42. <p><button onClick={vm.doReset}>Reset</button></p>

  43. </div>

  44. );

  45. }

  46. }

  47.  
  48. ReactDOM.render(<App vm={new VM()} />, document.getElementById('root'));

  49. registerServiceWorker();

那几个@xxxx虽然可能不太知道是啥意思,但是也差不多能猜出来八九不离十。仔细对比代码后可以发现:

  • 模板,照react变化不大,只是少了fullName之类的computed方法,让render变得更干净。而且render只与props有关,所以结合无状态组件来使用,就只剩下模板了,非常干净,这个大家可以想象一下。
  • 方法,可以看到VM部分,其实就相当于vue的VM(scrpit部分),而且分为了数据,计算属性,方法三个部分。还是比较清晰的。
  • 最后,没有了setState!没有了setState!没有了setState!自动监听了啊有木有!声明时一个@observable就解决了啊有木有!活脱脱变成vue的双胞胎了啊有木有!

总结:

vue作者尤雨溪如是说:

Mobx 在 React 社区很流行,实际上在 Vue 也采用了几乎相同的反应系统。在有限程度上,React + Mobx 也可以被认为是更繁琐的 Vue,所以如果你习惯组合使用它们,那么选择 Vue 会更合理。

结合react可以直接引用外部模块这点(通常是保存公共变量或方法的模块,这点vue需要靠vuex来实现,所以在这点上,react是更方便的),mobx可以替代redux作为项目的数据处理方案,可参考:【译】Redux 还是 Mobx,让我来解决你的困惑!

最后,如果有必要,再封装一些语法糖,甚至可以做到比vue还要简洁。当然,会额外增加学习成本,这个就需要自己权衡了。

最后附上用无状态组件实现的代码,大家可以品一下

父组件index

 
  1. // index.js

  2. import React from 'react';

  3. import ReactDOM from 'react-dom';

  4. import registerServiceWorker from './registerServiceWorker';

  5. import { App, AppVM } from './App';

  6.  
  7. ReactDOM.render(<App vm={new AppVM()} />, document.getElementById('root'));

  8. registerServiceWorker();

子组件App,无状态组件

 
  1. // App.js

  2. import React from 'react';

  3. import { observer } from 'mobx-react';

  4. import { observable, computed, action } from 'mobx';

  5.  
  6. export const App = observer(({ vm }) => (

  7. <div>

  8. <h1>This is mobx-react!</h1>

  9. <p>First name: <input type="text" value={vm.firstName} onChange={e => vm.setValue('firstName', e)} /></p>

  10. <p>Last name: <input type="text" value={vm.lastName} onChange={e => vm.setValue('lastName', e)} /></p>

  11. <p>Full name: {vm.fullName}</p>

  12. <p><button onClick={vm.doReset}>Reset</button></p>

  13. </div>

  14. ))

  15.  
  16. export class AppVM {

  17. @observable firstName = '';

  18. @observable lastName = '';

  19.  
  20. @computed get fullName() {

  21. const { firstName, lastName } = this;

  22. if (!firstName && !lastName) {

  23. return 'Please input your name!'

  24. } else {

  25. return firstName + ' ' + lastName;

  26. }

  27. };

  28.  
  29. @action.bound

  30. setValue(key, event) {

  31. this[key] = event.target.value;

  32. }

  33. @action.bound

  34. doReset() {

  35. this.firstName = '';

  36. this.lastName = '';

  37. }

  38. }

我故意把render放在了上面,怎么样,这么看是不是更像vue了点~

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/82730338