mobx让你写react像写vue一样爽

准备:

  • 此文章里的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实现:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: '',
      lastName: ''
    }
  }

  setValue(key, event) {
    this.setState({
      [key]: event.target.value
    });
  }

  getFullName() {
    let { firstName, lastName } = this.state;
    if (!firstName && !lastName) {
      return 'Please input your name!'
    } else {
      return firstName + ' ' + lastName;
    }
  }

  doReset() {
    this.setState({
      firstName: '',
      lastName: ''
    })
  }

  render() {
    const st = this.state;
    const fullName = this.getFullName();
    return (
      <div>
        <h1>This is normal react!</h1>
        <p>First name: <input type="text" value={st.firstName} onChange={e => this.setValue('firstName', e)} /></p>
        <p>Last name: <input type="text" value={st.lastName} onChange={e => this.setValue('lastName', e)} /></p>
        <p>Full name: {fullName}</p>
        <p><button onClick={() => { this.doReset() }}>Reset</button></p>
      </div>
    );
  }
}

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

vue实现:

<template>
  <div>
    <h1>This is vue!</h1>
    <p>First name: <input type="text" v-model="firstName" /></p>
    <p>Last name: <input type="text" v-model="lastName" /></p>
    <p>Full name: {{fullName}}</p>
    <p><button @click="doReset">Reset</button></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: "",
      lastName: ""
    };
  },
  computed: {
    fullName() {
      const { firstName, lastName } = this;
      if (!firstName && !lastName) {
        return "Please input your name!";
      } else {
        return firstName + " " + lastName;
      }
    }
  },
  methods: {
    doReset() {
      this.firstName = "";
      this.lastName = "";
    }
  }
};
</script>

对比:

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

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

mobx + react实现:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import { observable, computed, useStrict, action } from 'mobx';
import { observer } from 'mobx-react';
useStrict(true)

class VM {
  @observable firstName = '';
  @observable lastName = '';

  @computed get fullName() {
    const { firstName, lastName } = this;
    if (!firstName && !lastName) {
      return 'Please input your name!'
    } else {
      return firstName + ' ' + lastName;
    }
  };

  @action.bound
  setValue(key, event) {
    this[key] = event.target.value;
  }
  @action.bound
  doReset() {
    this.firstName = '';
    this.lastName = '';
  }
}

@observer
class App extends Component {
  render() {
    const vm = this.props.vm;
    return (
      <div>
        <h1>This is mobx-react!</h1>
        <p>First name: <input type="text" value={vm.firstName} onChange={e => vm.setValue('firstName', e)} /></p>
        <p>Last name: <input type="text" value={vm.lastName} onChange={e => vm.setValue('lastName', e)} /></p>
        <p>Full name: {vm.fullName}</p>
        <p><button onClick={vm.doReset}>Reset</button></p>
      </div>
    );
  }
}

ReactDOM.render(<App vm={new VM()} />, document.getElementById('root'));
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

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import { App, AppVM } from './App';

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

子组件App,无状态组件

// App.js
import React from 'react';
import { observer } from 'mobx-react';
import { observable, computed, action } from 'mobx';

export const App = observer(({ vm }) => (
  <div>
    <h1>This is mobx-react!</h1>
    <p>First name: <input type="text" value={vm.firstName} onChange={e => vm.setValue('firstName', e)} /></p>
    <p>Last name: <input type="text" value={vm.lastName} onChange={e => vm.setValue('lastName', e)} /></p>
    <p>Full name: {vm.fullName}</p>
    <p><button onClick={vm.doReset}>Reset</button></p>
  </div>
))

export class AppVM {
  @observable firstName = '';
  @observable lastName = '';

  @computed get fullName() {
    const { firstName, lastName } = this;
    if (!firstName && !lastName) {
      return 'Please input your name!'
    } else {
      return firstName + ' ' + lastName;
    }
  };

  @action.bound
  setValue(key, event) {
    this[key] = event.target.value;
  }
  @action.bound
  doReset() {
    this.firstName = '';
    this.lastName = '';
  }
}
我故意把render放在了上面,怎么样,这么看是不是更像vue了点~


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值