单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。

有单向绑定,就有双向绑定。如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定。

什么情况下用户可以更新View呢?填写表单就是一个最直接的例子。当用户填写表单时,View的状态就被更新了,如果此时MVVM框架可以自动更新Model的状态,那就相当于我们把Model和View做了双向绑定:

在浏览器中,当用户修改了表单的内容时,我们绑定的Model会自动更新:

下面介绍实现以上逻辑交互,不同的实现方式

原生React方式

export default class Form extends Component {
    // 实际业务中,数据可能是来自props
    state = {
        name: '',
        email: ''
    }
    changeFromEvt = (key, evt) => {
        this.setState({
            [key]: evt.target.value
        })
    }

    render() {
        return (
            <form>
                <input
                    value={this.state.name}
                    onChange={this.changeFromEvt.bind(this, 'name')}
                />
                <input
                    type="email"
                    value={this.state.email}
                    onChange={this.changeFromEvt.bind(this, 'email')}
                />
            </form>
        )
    }
}

Hoc方式

function PPHOC(WrappedComponent) {
  return class PP extends React.Component {
    state = { fields: {} }
    
    getField(fieldName) {
      if (!this.state.fields[fieldName]) {
        this.state.fields[fieldName] = {
          value: '',
          onChange: event => {
            this.state.fields[fieldName].value = event.target.value
            this.forceUpdate()
          }
        }
      }

      return {
        value: this.state.fields[fieldName].value,
        onChange: this.state.fields[fieldName].onChange
      }
    }

    render() {
      const props = Object.assign({}, this.props, {
        fields: this.getField.bind(this),
      })
      return (
        <WrappedComponent {...props}/>
      )
    }
  }
}
export default class Form extends React.Component {
    render() {
        return (
            <form>
                <input
                    {...this.props.fields('name')}
                />
                <input
                    type="email"
                    {...this.props.fields('email')}
                />
            </form>
        )
    }
}

Mobx(观察者)方式

import { observable, action } from 'mobx'
export default class Form extends Component {
    @observable form = {
        name: '',
        email: ''
    }
    
    changeFromEvt = (key, evt) => {
        this[key] = evt.target.value
    }

    render() {
        return (
            <form>
                <input
                    value={this.state.name}
                    onChange={this.changeFromEvt.bind(this, 'name')}
                />
                <input
                    type="email"
                    value={this.state.email}
                    onChange={this.changeFromEvt.bind(this, 'email')}
                />
            </form>
        )
    }
}

双向绑定方式 (主角)

import { observable, binding } from 'react-mobx-vm'

class Form {
    @observable name = ''
    @observable email = ''
}

@binding(new Form())
export default class Form extends Component {

    render() {
        return (
            <form>
                <input
                    data-bind="name"
                />
                <input
                    type="email"
                    data-bind="email"
                />
            </form>
        )
    }
}