若是你曾經用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是一個簡單、高效的前端狀態管理腳本庫。 根據文檔,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
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()就能夠了。
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()爲何不會觸發。
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
讓咱們直接看例子:
<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
原文做者:Matt Ruby
原文地址:https://www.sitepoint.com/manage-javascript-application-state-mobx/
譯文做者:ouven