Upgrade to Pro — share decks privately, control downloads, hide ads and more …

jQuery to Backbone – into JavaScript architecture.

Ayumu Sato
February 09, 2013

jQuery to Backbone – into JavaScript architecture.

Frontrend Vol.4(2013年2月9日開催)で行われた、"jQuery to Backbone – アーキテクチャを意識したJavaScript入門"のセッション資料です。

参考: http://frontrend.github.io/events/04/

Ayumu Sato

February 09, 2013
Tweet

More Decks by Ayumu Sato

Other Decks in Programming

Transcript

  1. 依存するライブラリ ✓ jQuery ✓ Zepto.js (lightweight clone) ✓ Underscore.js ✓

    Lodash (more faster) Utility Belt Library Selector Based Library _. $.
  2. Web サイト 従来の Webサービス Webアプリ 新しめな Webサービス Webアプリ サーバーサイドで 画面遷移を設計

    静的HTML CMS利用 シングルページ リッチインターフェース RESTful API 次第に高まってきた フロントエンド実装の比重
  3. Most Developers realize that structured, maintainable code is important. via.

    Digesting JavaScript MVC https://speakerdeck.com/addyosmani/digesting-javascript-mvc?slide=10 ディベロッパーにとって、構造的で メンテナンスしやすいコードが何たるかを知ることが重要。 “
  4. Backbone.js AngularJS Ember.js KnockoutJS Dojo YUI Agility.js Knockback.js CanJS Maria

    cujo.js dermis Montage Ext.js Sammy.js Stapes Epitome soma.js DUEL PureMVC Olives PlastronJS Dijon rAppid.js Funnyface.js Spine Batman.js GWT Closure MVC Extension Frameworks MarionetteJS Thorax Chaplin 巷に溢れかえる MV*フレームワーク TodoMVCだけで30以上が列挙される
  5. “ it serves as a foundation for your application, you're

    meant to extend and enhance it in the ways you see fit 方法は1つではないし、柔軟に拡張できる via. Backbone.js FAQ http://backbonejs.org/#FAQ-why-backbone
  6. Abstract Factory Builder Factory Method Prototype Singleton Adapter Bridge Composite

    Decorator Facade Flyweight Proxy Chain of Responsibility Command Interpreter Iterator Mediator Memento Observer State Strategy Template Method Visitor GoFに代表される 様々なデザインパターン 一般に共有できるボキャブラリ
  7. {} Facade - 複数の処理をまとめる // ΫϦοΫͨ͠ͱ͖ʹෳ਺ͷૢ࡞Λಉ࣌ʹߦ͏ // ࠷΋޿ٛʹ͸୭΋͕͋ͨΓ·͑ʹߦ͏ύλʔϯ onLikeClick: function()

    { this.likeModel.save(); this.changeState(‘liked’); this.$el.addClass(‘animate’); // ... // .. // . }
  8. {} Flyweight - インスタンス生成 // ಈతʹΠϯελϯεΛ࡞Γ͍ͨ৔߹ͳͲ // ಉ͡໾ׂͷΠϯελϯε͸ෳ਺ੜ੒ͤͣ͞ʹ࢖͍ճ͢ var factory1

    = Flyweight.getFactory('model'); var factory2 = Flyweight.getFactory('view'); var factory3 = Flyweight.getFactory('model'); // ظ଴͢Δಈ࡞ͱͯ͠ɺmodelͱͯ͠ݺΜͩΒಉҰͰ͋Δ͜ͱ console.log(factory1 === factory2); //=> false console.log(factory1 === factory3); //=> true
  9. {} Observer - イベントを監視 // ؂ࢹ͞ΕΔଆͷModelͱɺ؂ࢹ͢ΔଆͷView var model = new

    FooModel(); var view = new BarView(); // Modelʹ௚઀ɺViewͷϝιουΛϦεφʔͰొ࿥ model.on('change', view.render); // fetch()ʹΑͬͯɺchangeΠϕϯτ͕τϦΨʔ model.fetch(); // view.render()͕࣮ߦ͞ΕΔ
  10. {} Mediator - イベントを仲介 // MediatorΛ࡞੒ʢBackboneͷྫʣ var mediator = _.clone(Backbone.Events);

    // MediatorʹϦεφʔΛొ࿥ mediator.on('model:change', view.render); // MediatorΛ௨ͯ͠ΠϕϯτΛτϦΨʔ mediator.trigger('model:change', this);
  11. {} 典型的なView var ViewClass = Backbone.View.extend({ el: ‘#main’, initialize: function()

    { // process... }, render: function() { this.$el.html(‘rendering html strings’); } }); var view = new ViewClass(); // `initalize` view.render(); // `render`
  12. {} 典型的なModel var ModelClass = Backbone.Model.extend({ defaults: {}, url: ‘api/v1/path/to’,

    initialize: function() { // process... } }); var model = new ModelClass(); // `initalize` var view = new ViewClass({model: model}); model.fetch({ success: view.render });
  13. {} 典型的なCollection var Persons = Backbone.Collection.extend({ url: ‘api/v1/path/to’, model: Person

    }); var persons = new Persons(); persons.fetch({ success: function() { this.where({ name: ‘anonymous’ })[0].sayName(); // ‘anonymous!’ } });
  14. {} 典型的なRouter var Router = Backbone.Router.extend({ routes: { 'store/:storeId': 'gotoStore'

    }, gotoStore: function(storeId) { new StoreView({ model: new Store(storeId); }); } }); var app = new Router(); Backbone.history.start();
  15. Backbone  Router     Views Models Collection

    via. Backbonification - Migrating NewsBlur From DOM Spaghetti to Backbone.js https://speakerdeck.com/samuelclay/backbonification-migrating-newsblur-from-dom-spaghetti-to-backbone-dot-js?slide=12
  16. {} ピュアなjQueryコードからスタート var $list = $('#js-gists'); $.ajax({ method: 'GET', url:

    'https://api.github.com/gists', data: oauthData, dataType: 'json' }).done(function(data) { var i = 0, html = '', item; while (item = data[i++]) { html += '<li>'+ '<a data-src="'+item.url+'" href="#">'+item.description+'</a>'+ '<a href="'+item.html_url+'">Show in gists</a>'+ '</li>'; } $list.html(html); }); $list.on('click', '[data-src]', previewGist);
  17. {} おもむろにViewを作成 var GistsListView = Backbone.View.extend({ el: '#js-gists', initialize: function()

    { var $list = this.$el; $.ajax({ method: 'GET', url: 'https://api.github.com/gists', data: oauthData, dataType: 'json' }).done(function(data) { var i = 0, html = '', item; while (item = data[i++]) { html += '<li>'+ '<a data-src="'+item.url+'" href="#">'+item.description+'</a>'+ '<a href="'+item.html_url+'">Show in gists</a>'+ '</li>'; } $list.html(html); }); $list.on('click', '[data-src]', previewGist); } }); var gistsList = new GistsListView();
  18. {} renderメソッドを抽出 var GistsListView = Backbone.View.extend({ el: '#js-gists', initialize: function()

    { _.bindAll(this); $.ajax({ method: 'GET', url: 'https://api.github.com/gists', data: oauthData, dataType: 'json' }).done(this.render); this.$el.on('click', '[data-src]', previewGist); }, render: function(data) { var i = 0, html = '', item; while (item = data[i++]) { html += '<li>'+ '<a data-src="'+item.url+'" href="#">'+item.description+'</a>'+ '<a href="'+item.html_url+'">Show in gists</a>'+ '</li>'; } this.$el.html(html); return this; } }); var gistsList = new GistsListView();
  19. {} テンプレートの分離 var GistsListView = Backbone.View.extend({ el: '#js-gists', tmpl: _.template($('#tmpl-js-gists').html()),

    initialize: function() { _.bindAll(this); $.ajax({ method: 'GET', url: 'https://api.github.com/gists', data: oauthData, dataType: 'json' }).done(this.render); this.$el.on('click', '[data-src]', previewGist); }, render: function(data) { this.$el.html(this.tmpl({items: data})); return this; } }); var gistsList = new GistsListView();
  20. {} Underscoreテンプレート <script id="tmpl-js-gists" type="tmpl/text"> <% _.each(items, function(item) { %>

    <li> <a data-id="<%= item.id %>" data-src="<%= item.url %>"> <%= item.description %> </a> <a href="<%= item.html_url %>">Show in gists</a> </li> <% }); %> </script>
  21. {} イベントの定義 var GistsListView = Backbone.View.extend({ el: '#js-gists', tmpl: _.template($('#tmpl-js-gists').html()),

    events: { 'click [data-src]': previewGist }, initialize: function() { _.bindAll(this); $.ajax({ method: 'GET', url: 'https://api.github.com/gists', data: oauthData, dataType: 'json' }).done(this.render); }, render: function(data) { this.$el.html(this.tmpl({items: data})); return this; } }); var gistsList = new GistsListView();
  22. {} Collectionを作成 var Gists = Backbone.Collection.extend({ url: 'https://api.github.com/gists?' + $.param(oauthData)

    }); var GistsListView = Backbone.View.extend({ el: '#js-gists', tmpl: _.template($('#tmpl-js-gists').html()), events: { 'click [data-src]': 'preview' }, initialize: function() { _.bindAll(this); this.collection.fetch({ success: this.render }); }, render: function() { this.$el.html(this.tmpl({items: this.collection.toJSON()})); return this; } }); var gistsList = new GistsListView({ collection: new Gists() });
  23. {} Modelを作成 // Model & Collection var Gist = Backbone.Model.extend({

    url: function() { return this.get('url'); } }); var Gists = Backbone.Collection.extend({ url: 'https://api.github.com/gists?' + $.param(oauthData), model: Gist }); // GistListView#preview preview: function(event) { gistPreview.model = this.collection.where({ id: $(event.currentTarget).attr('data-id') })[0]; gistPreview.show(); return false; },
  24. {} Collectionのソート var Gists = Backbone.Collection.extend({ url: 'https://api.github.com/gists?' + $.param(oauthData),

    model: Gist, order_by: 'updated_at', comparator: function(gist) { switch(this.order_by) { case 'updated_at': return - new Date(gist.get('updated_at')).getTime(); } } }); // GistListView events: { 'click #js-sort-updated': 'sortByUpdatedAt', }, initialize: function() { _.bindAll(this); this.collection.fetch({success: this.render}); this.collection.on('sort', this.render); }, sortByUpdatedAt: function() { this.collection.order_by = 'updated_at'; this.collection.sort(); },
  25. Backbone has made me a better programmer via. Backbone has

    made me a better programmer | Float Left http://floatleft.com/notebook/backbone-has-made-me-a-better-programmer “
  26. 1. Two equestrian riders, girls on horseback, in low tide

    reflections on serene Morro Strand State Beach http://www.flickr.com/photos/ mikebaird/2985066755 2. Energy Drinks - Monster, Red Bull and Rockstar http://www.flickr.com/ photos/aukirk/8170825503 3. - Good Friends http://www.flickr.com/photos/ngmmemuda/4166182931 4. Rhino relaxation http://www.flickr.com/photos/macinate/2810203599 5. Whale backbone http://www.flickr.com/photos/vagawi/2257918524/ 6. Sleeping 猫 http://www.flickr.com/photos/hansel5569/7687221498/ 7. Alien vs Predator http://www.flickr.com/photos/steampirate/ 1056958115/ Photo Credits...thx♡