Arquitetura de Front-end em Aplicações de Larga Escala

Arquitetura de Front-end em Aplicações de Larga Escala

Para desenvolver um site institucional simples ou um hotsite pequeno, não é preciso muito mais que um pouco de HTML, um ou dois arquivos de CSS, alguns arquivos de JavaScript, e um sistema de CMS. No entanto, à medida que a complexidade de um site aumenta, o código passa a se tornar cada vez mais extenso, complexo, difícil de ser organizado, e projeto acaba virando um grande "code spaghetti".

Para garantir que isso não aconteça, é necessário implementar uma estrutura sólida para o HTML, CSS e JavaScript, de modo que os componentes da aplicação funcionem independentemente e sejam facilmente mantidos e modificados. Nesta palestra, irei mostrar como elaborar uma arquitetura de front-end que sustente uma aplicação de larga escala.

Fa59eb5159c978e2577e65f45fa9171f?s=128

Eduardo Shiota Yasuda

August 24, 2012
Tweet

Transcript

  1. Arquitetura de Front-end em aplicações de larga escala @shiota |

    eshiota.com RAILS ED ITIO N
  2. Front-end

  3. Internet

  4. Arquitetura simples Arquitetura complexa

  5. Na minha visão, aplicações JavaScript de larga escala são aplicações

    não- triviais que requerem um esforço significante de manutenção por parte do desenvolvedor, onde a maior parte do trabalho de manipulação de dados e visualização é atribuída ao navegador. Addy Osmani Developer Programs Engineer @ Google
  6. Aplicações com front-end de larga escala são aplicações não-triviais que

    requerem um esforço significante de manutenção por parte do desenvolvedor, onde organização, modularização, otimização e reutilização de código são cruciais. Eduardo Shiota Yasuda Loves cat videos on Youtube
  7. Aplicações de pequena escala

  8. style.css templates application.js & jQuery / plugins imagens CMS

  9. CSS simples Centenas de linhas Fácil de manter

  10. JS Simples Plugins e algumas funções Uma função autoexecutável jQuery

  11. Aplicações de Larga escala

  12. None
  13. None
  14. None
  15. None
  16. None
  17. HTML, CSS e JavaScript modulares

  18. Noção de módulo

  19. None
  20. None
  21. None
  22. None
  23. HTML & CSS modular

  24. None
  25. <section class="side-block"> <h3>Quick links</h3> <ul class="arrowed-list side-list"> <li> <a href="#">Your

    profile</a> </li> <li> <a href="#">MKX settings</a> </li> <li> <a href="#">MIH SWAT</a> </li> <li> <a href="#">Invite users</a> </li> </ul> </section>
  26. <section class="side-block"> <h3>Your starred content</h3> <ul class="iconed side-list"> <li class="file-locked">

    <a href="#">RubyConf - 02 - Ruby is cool</a><br /> <small>in group: <a href="#">Ruby</a></small> </li> <li class="photo-locked"> <a href="#">RubyConf entrance hall</a><br /> <small>in group: <a href="#">Ruby</a></small> </li> <li class="doc"> <a href="#">MongoDB</a><br /> <small>in company: <a href="#">MIH SWAT</a></small> </li> <li class="forum"> <a href="#">Ruby group files...</a><br /> <small>in group: <a href="#">Ruby</a></small> </li> </ul> </section>
  27. <section class="side-block"> <h3>Recommended contacts</h3> <ul class="side-list"> <li class="data-card"> <div class="card-content">

    <hgroup> <h4>Lorem ipsum</h4> <h5>CEO @ <a href="#">MIH SWAT</h5> </hgroup> <p><a href="#">[add as contact]</a></p> </div> <img src="image.jpg" alt="Lorem ipsum" /> </li> <li class="data-card"> <div class="card-content"> <hgroup> <h4>Lorem ipsum</h4> <h5>Technical Architect @ <a href="#">MIH SWAT</h5> </hgroup> <p><a href="#">[add as contact]</a></p> </div> <img src="image.jpg" alt="Lorem ipsum" /> </li> </ul> </section>
  28. <section class="side-block"> <h3></h3> <ul class="side-list"> <li></li> </ul> </section>

  29. /********************************** * patterns/side_list.scss **********************************/ .side-list { list-style: none outside; padding:

    0; } .side-list li { margin-bottom: 10px; overflow: hidden; } /********************************** * patterns/arrowed_list.scss **********************************/ .arrowed-list li { margin-bottom: 0.2em; position: relative; } .arrowed-list li:before { content: "\25B8"; display: inline-block; margin-right: 0.3333em; }
  30. /********************************** * patterns/side_block.scss **********************************/ .side-block { margin-bottom: 1.5em; } .side-block

    h3 { border-bottom: 1px solid #cecece; font-size: 1em; /* 16px / 16px */ margin-bottom: 0.8em; padding-bottom: 0.1em; }
  31. None
  32. <section> <h3>Recommended contacts</h3> <ul class="side-list small-entity-list"> <li class="data-card"> <div class="card-content">

    <hgroup> <h4>Lorem ipsum</h4> <h5>CEO @ <a href="#">Buscapé</h5> </hgroup> <p><a href="#">[add as contact]</a></p> </div> <img src="image.jpg" alt="Lorem ipsum" /> </li> </ul> </section>
  33. <article class="data-card group-activity"> <div class="card-content"> <p> <a href="#" class="actor">Rafael Dohms</a>

    joined a group: <a href="#" class="subject">I love Ruby on Rails</a> </p> <p> <date>2 hours ago</date> <a href="#">Unlike</a> <a href="#">Comment entry</a> </p> </div> <img src="image.jpg" alt="Rafael Dohms" /> </article>
  34. <article class="data-card"> <div class="card-content"> </div> <img /> </article>

  35. /********************************** * patterns/data_card.scss **********************************/ .data-card { min-height: 120px; position: relative;

    } /* Override width and height as needed */ .data-card > img { position: absolute; left: 0; top: 0; width: 120px; height: 120px; } /* Override padding-left as needed */ .data-card .card-content { padding-left: 140px; }
  36. /*************************************** * patterns/small_entity_list.scss * * children extend patterns/data_card ***************************************/ .small-entity-list

    .data-card { min-height: 60px; } .small-entity-list .data-card > img { width: 60px; height: 60px; } .small-entity-list .data-card .card-content { padding-left: 80px; }
  37. .button .large-button .confirm-button .send-button

  38. .button { border-radius: 5px; font-family: "proxima-nova", sans-serif; height: 2em; line-height:

    2em; padding: 1em; color: #fff; text-shadow: 0 -1px 0 #000; } .large-button { font-size: 3em; } .confirm-button { background: #ffba00; box-shadow: 0 3px 0 #cd9600; } .send-button { /* styles for icon placement */ }
  39. .button .large-button .confirm-button .send-button

  40. .button .small-button .confirm-button .send-button

  41. Multiple classes + Single responsabilities Short inheritance (3-4 levels max)

    Portable classes
  42. Organizando os módulos

  43. /* * application.css example w/ Asset Pipeline * *= require

    base/reset *= require core/typography *= require core/forms *= require_tree ./patterns *= require ui/buttons *= require ui/loader *= require ui/datepicker */
  44. application.scss w/ SASS

  45. base/_functions.scss

  46. core/_typography.scss

  47. ui/_loader.scss

  48. application-ec8971025292ecb7dd2c99d430d7a76e.css

  49. Leituras

  50. oocss.org

  51. smacss.com

  52. JavaScript modular

  53. JavaScript não é (só) jQuery

  54. shareTypeSelector.js shareForm.js shareField.js newsfeed.js

  55. shareForm shareTypeSelector shareField mediator.js trigger("share- type-changed") on("share-type-changed") shareProxy newsfeed on("new-post")

    trigger("new-post") init init submit get data onsuccess
  56. Modules + Tests = OMGBBQW00T

  57. /** Loader constructor @params {Function} placement Function that determines the

    loader's placement @constructor **/ ns("EDEN.ui.Loader", function (placement) { if (!(this instanceof EDEN.ui.Loader)) { return new EDEN.ui.Loader(placement); } this.frame = 1; this.framesQty = 8; this.stack = []; this.animating = false; this.$loader = $("<div class='loader'><b> </b></div>"); this.$renderer = this.$loader.find("b"); this.placement = placement; this.init(); });
  58. /** Animation speed (in frames per second) @property fps @type

    Number @default 20 **/ EDEN.ui.Loader.prototype.fps = 20; /** Fading speed @property fadeSpeed @type Number @default 150 **/ EDEN.ui.Loader.prototype.fadeSpeed = 150;
  59. /** Inits the loader by inserting it into the DOM.

    If a placement argument wasn't passed to the constructor, uses a generic placement. @method init **/ EDEN.ui.Loader.prototype.init = function () { if (!this.placement) { this.placement = function ($loader) { $loader.appendTo($("body")); }; } this.placement.call(this, this.$loader); };
  60. /** Starts the loader by fading in and starting the

    animation. If there are multiple processes, stacks the requests. @method start **/ EDEN.ui.Loader.prototype.start = function () { this.stack.push((new Date()).getTime()); if (this.stack.length === 1) { this._startAnimation(); } }; /** Stops the loader by fading out and stoping the animation If there are any processes pending, pops the requests until it can actually stop. @method stop **/ EDEN.ui.Loader.prototype.stop = function () { this.stack.pop(); if (!this.stack.length) { this._stopAnimation(); } };
  61. /** Starts the loader animation @private **/ EDEN.ui.Loader.prototype._startAnimation = function

    () { this.animating = true; this._renderAnimation(); }; /** Stops the loader animation @private **/ EDEN.ui.Loader.prototype._stopAnimation = function () { this.animating = false; };
  62. /** Loops the animation, calling itself according to the fps

    @private **/ EDEN.ui.Loader.prototype._renderAnimation = function () { if (!this.animating) { return true; } this._draw(); setTimeout(this._renderAnimation.bind(this), 1000 / this.fps); }; /** Draws the animation @private **/ EDEN.ui.Loader.prototype._draw = function () { this.$renderer.removeClass().addClass("f" + this.frame); this.frame = this.frame + 1 > this.framesQty ? 1 : this.frame + 1; }; /** Returns the animation stack. @return Array @private **/ EDEN.ui.Loader.prototype._getAnimationStack = function () { return this.stack; };
  63. não achei um logo maior :(

  64. it("appends the loader to body as a default", function ()

    { var loader = new Loader(); expect($("body").find(".loader").length).toEqual(1); }); it("appends the loader through an argument function", function () { var loader = new Loader(function ($loader) { $("#loader-placeholder").append($loader); }); expect($("#loader-placeholder").find(".loader").length).toEqual(1); }); it("stops the animation if stack is empty", function () { loader.start(); loader.stop(); expect($(".loader").data("spinning")).not.toBeTruthy(); });
  65. it("renders the animation at default speed (20fps)", function () {

    jasmine.Clock.useMock(); spyOn(loader, "draw"); loader.start(); jasmine.Clock.tick(2000); // The first renderAnimation call renders the first frame, and then // starts the frame counting. So it'll always be (fps * seconds) + 1 expect(loader.draw.calls.length).toEqual(41); });
  66. None
  67. None
  68. jasmincerice + guard-jasmine + PhantomJS + Jenkins = OMGWTFBBQW00TROFL

  69. github.com/bradphelan/jasminerice/

  70. github.com/netzpirat/guard-jasmine

  71. phantomjs.org

  72. Jenkins » rake guard:jasmine

  73. None
  74. SOLID principles Loose coupling » Portable Testable MV**

  75. Organizando os módulos

  76. // application.js with Asset Pipeline // //= require jquery //=

    require jquery_ujs //= require jquery/jquery.inputmask //= require jquery/jquery.validate //= require jquery/jquery.uniform //= require i18n //= require i18n/translations //= require i18n/setLocale //= require_tree shims //= require tools/namespace //= require accounting //= require handlebars-1.0.0.beta.6 //= require eden/events //= require eden/dispatcher //= require eden/appMediator //= require_tree ./jquery //= require_tree ./eden/ui //= require_tree ./eden/presenters //= require_tree ./eden/validators //= require ./eden/forms/SubmitButton //= require_tree ./eden/forms //= require_tree ./eden/components //= require_tree ./eden/views //= require_tree ./eden/proxies //= require_tree ./eden/commands //= require_tree ./eden/services //= require_tree ./eden/modules //= require eden/app
  77. documentcloud.github.com/jammit/

  78. requirejs.org

  79. //my/shirt.js now has some dependencies, a cart and inventory //module

    in the same directory as shirt.js define(["./cart", "./inventory"], function(cart, inventory) { //return an object to define the "my/shirt" module. return { color: "blue", size: "large", addToCart: function() { inventory.decrement(this); cart.add(this); } }; } );
  80. github.com/jwhitley/requirejs-rails

  81. Leituras

  82. None
  83. addyosmani.com/largescalejavascript

  84. addyosmani.github.com/todomvc/

  85. Ferramentas para manter a organização

  86. github.com/cowboy/grunt

  87. html5boilerplate.com

  88. compass-style.org

  89. Exemplos com compass

  90. Geração de sprites

  91. /* Attributes a sprite map to a variable */ $icon-sprite:

    sprite-map("icon/*.png", $spacing: 16px, $repeat: no-repeat, $layout: vertical);
  92. $icon-sprite: sprite-map("icon/*.png", $spacing: 16px, $repeat: no-repeat, $layout: vertical);

  93. /* Compass sprite function receives the map variable and image

    as arguments */ background: sprite($icon-sprite, arrow_dropdown) no-repeat; /* Compiled CSS */ background: url(/assets/icon-s5dab8c2901.png) -40px -158px no-repeat;
  94. Inline images - base64

  95. /* Generates a base64 image */ background: #f5f3eb inline-image("bg_dots.png") repeat;

    /* Compiled CSS */ background: #f5f3fb url('data:image/ png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAAAGXRFWH RTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF5+TW//////// 4qZUpQAAAAN0Uk5T// 8A18oNQQAAACBJREFUeNpiYGBgAgMGBkYog4mJXAbILAiDkVxzAAIMAEMOAPMId2O WAAAAAElFTkSuQmCC') repeat;
  96. Vendor prefixes

  97. .my-gradient { background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0,

    0, 0)); background-image: -moz-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -ms-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -o-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); }
  98. .my-gradient { background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0,

    0, 0)); background-image: -moz-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -ms-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -o-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); }
  99. /* Generates vendor-prefixed rules */ .my-gradient { @include background-image( linear-gradient(right,

    rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)) ); } /* Compiled CSS */ .my-gradient { background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -moz-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -ms-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: -o-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); background-image: linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)); }
  100. Resumindo

  101. Modules, motherf*cker.

  102. Aprenda JavaScript.

  103. Use ferramentas e frameworks. <3

  104. Se você chegar nesse ponto...

  105. None
  106. DOUBLE RAINBOW ALL THE WAY OMG SO INTENSE

  107. None
  108. Thanks! @shiota | eshiota.com