【原译】使用MobX怎样管理JavaScript应用状态

  若是你曾经用jQuery写过复杂的应用,你可能就会遇到管理不一样页面部分UI内容同步的问题。经常,数据修改后须要体如今多个地方,随着项目的复杂化,你可能很难去管理。为了解决这个问题,咱们经常须要使用事件来让页面的多个部分知道数据改变了。javascript

  因此,如今你是怎样管理这个状态的问题的呢?我肯定你是经过订阅的方式来作的。这是对的,我敢确定,若是你没有使用订阅的方式,那你实现起来太辛苦了,除非你使用了MobX。html

什么是状态?

  这里是一个person对象,好比某我的,他有firstName,lastName和age,另外fullName()方法将显示他的全名。前端

var person = {
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 37,
    fullName: function () {
        this.firstName + ' ' + this.lastName;
    }
};

  那么当这我的的信息修改时,是怎样体现到你输出内容上的呢?你在何时触发这些通知呢?在MobX以前,你可使用setter来触发jQuery事件或者js信号。这些方案很好,可是使用它们不够清晰。我想在person对象的任何部分改变时就去自动触发。java

  这里是一段修改firstName信息的代码,若是我修改age,那么这是会触发修改通知的,由于咱们将数据订阅绑定在person的整个对象上了。react

person.events = {};

person.setData = function (data) {
    $.extend(person, data);
    $(person.events).trigger('changed');
};

$(person.events).on('changed', function () {
    console.log('first name: ' + person.firstName);
});

person.setData({age: 38});

  咱们怎样解决这个问题呢?很简单,为person对象的每一个属性设置一个setter来触发每个事件。等等,可是这样若是age和firstName同时改变了该怎么办。因此你得设置一个延时来保证两个属性都改变完成时才触发。这彷佛行得通而且我就是这么干的。git

经过MobX来解决

  MobX是一个简单、高效的前端状态管理脚本库。   根据文档,Just do something to the state and MobX will make sure your app respects the changes。github

var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 37,
    fullName: function () {
    this.firstName + ' ' + this.lastName;
    }
});

  注意下区别,mobx.observable是我作的惟一区别,咱们再来看下console.log输出的内容。api

mobx.autorun(function () {
    console.log('first name: ' + person.firstName);
});

person.age = 38; // 不打印内容
person.lastName = 'RUBY!'; // 仍然不打印内容
person.firstName = 'Matthew!'; // 打印内容

  经过使用autorun, MobX只会检测使用到的内容。若是你以为不够清楚,来看下下面的:缓存

mobx.autorun(function () {
    console.log('Full name: ' + person.fullName);
});

person.age = 38; // 不打印内容
person.lastName = 'RUBY!'; // 打印内容
person.firstName = 'Matthew!'; // 打印内容

  相信你已经理解了。ruby

MobX核心概念

  • observer
var log = function(data) {
    $('#output').append('<pre>' +data+ '</pre>');
}

var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 34
});

log(person.firstName);

person.firstName = 'Mike';
log(person.firstName);

person.firstName = 'Lissy';
log(person.firstName);

  MobX可观察的对象都是普通的对象。这个例子中咱们没有观察任何内容,这里例子也向你展现了怎样将MobX使用到你的项目中。只须要使用mobx.observable()或 mobx.extendObservable()就能够了。

  • autorun
var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 0
});

mobx.autorun(function () {
    log(person.firstName + ' ' + person.age);
});

// this will print Matt NN 10 times
_.times(10, function () {
    person.age = _.random(40);
});

// this will print nothing
_.times(10, function () {
    person.lastName = _.random(40);
});

  当变量值改变时,你确定想作一些事情,因此使用autorun(),将会在任何一个观察的内容改变时触发回调。注意下上面的例子中age改变时autorun()为何不会触发。

  • computed
var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 0,
    get fullName () {
        return this.firstName + ' ' + this.lastName;
    }
});
log(person.fullName);

person.firstName = 'Mike';
log(person.fullName);

person.firstName = 'Lissy';
log(person.fullName);

  注意下fullName()方法没有使用参数和get的用法,MobX会自动建立一个计算的值。这是我最喜欢MobX的一个缘由。注意下person.fullName有什么不一样的地方?这是一个函数,可是你没有调用就获取到告终果!一般,你使用的是person.fullName(),而不是person.fullName。这里就是你遇到的第一个getter函数。

  不只如此,MobX还会监听你计算值得依赖的变量,而且只在它们修改的时候运行触发更新。若是没有修改,将会直接返回以前缓存中的值。看下面一个例子。

var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 0,
    get fullName () {
        // Note how this computed value is cached.
        // We only hit this function 3 times.
        log('-- hit fullName --');
        return this.firstName + ' ' + this.lastName;
    }
});

mobx.autorun(function () {
    log(person.fullName + ' ' + person.age);
});

// this will print Matt Ruby NN 10 times
_.times(10, function () {
    person.age = _.random(40);
});

person.firstName = 'Mike';
person.firstName = 'Lissy';

  这里咱们使用了person.fullName不少次,可是函数只有在firstName和lastName修改时才会运行。这就是MobX能够提供你应用速度的一种方式。还有其它的特性,能够看文档:https://mobxjs.github.io/mobx/refguide/api.html

使用MobX进行项目实践

  让咱们直接看例子:

<h1>Test</h1>
<script>
var person = {
    events: {},
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 37,
    fullName: function() {
        return this.firstName + ' ' + this.lastName;
    },
    setPersonData: function(personData) {
        $.extend(this, personData);
        $(this.events).trigger('changed', personData);
    }
};
var renderCount = 0;
$(person.events).on('changed', function() {
    renderCount += 1;
    $('h1').text(person.fullName() + ' render count: '+ renderCount);
});

// this will trigger every time
_.times(10, function() {
    person.setPersonData({age: _.random(20)});
});
</script>

  注意下这里name被重复渲染了10次,尽管咱们没有修改firstName或lastName,你可以使用多个事件来优化这里,在渲染以前作判断。可是太复杂了。下面是MobX的例子;

<h1>Test</h1>

<script>
var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 37,
    get fullName () {
        return this.firstName + ' ' + this.lastName;
    }
});
var renderCount = 0;
mobx.autorun(function() {
    renderCount++;
    $('h1').text(person.fullName + ' render count: ' + renderCount);
});

// this will trigger the render one time
_.times(10, function() {
    person.setPersonData({
        age: _.random(20)
    });
});
</script>

  注意下这里为何没有事件、触发器或on绑定。使用MobX,你会使用修改后的最新值。并且它只被渲染一次,这是由于咱们没有修改内容时autorun只是一直在监听。

// observable person
var person = mobx.observable({
    firstName: 'Matt',
    lastName: 'Ruby',
    age: 37
});

// reduce the person to simple html
var printObject = function(objectToPrint) {
    return _.reduce(objectToPrint, function(result, value, key) {
        result += key + ': ' + value + '<br/>';
        return result;
      }, '');
};

// print out the person anytime there's a change
mobx.autorun(function(){
    $('#person').html(printObject(person));
});

// watch all the input for changes and update the person
// object accordingly.
$('input').on('keyup', function(event) {
    person[event.target.name] = $(this).val();
});

  这里咱们能编辑person的全部信息来观察输出的内容状况,如今这个例子中有个小小的问题,这里input的值和person的值不一样步,咱们改下:

mobx.autorun(function(){
    $('#person').html(printObject(person));
     // update the input values
    _.forIn(person, function(value, key) {
        $('input[name="'+key+'"]').val(value);
    });
});

  或许你会说,你这里有从新渲染了啊。是的,你这看到的就是为何不少人选择使用React的缘由。React容许你将输出内容拆分小的组件进行个别的渲染。这里有个完整的例子:a jQuery example that I have optimized

  MobX可能在实际项目中使用吗?不必定,若是我须要这种细粒度的操做使用React就能够了。当我在实际项目中使用MobX和jQuery时,我使用autorun()就能够解决不少问题了。the same example built with React and MobX

更多相关资料

MobX blogs, tutorials and videos

Egghead.io course: Manage Complex State in React Apps with MobX

Practical React with MobX

Simple MobX Examples

MobX API reference

原文做者:Matt Ruby

原文地址:https://www.sitepoint.com/manage-javascript-application-state-mobx/

译文做者:ouven

相关文章
相关标签/搜索