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. 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
  2. 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
  3. 12.
  4. 13.
  5. 14.
  6. 15.
  7. 16.
  8. 19.
  9. 20.
  10. 21.
  11. 22.
  12. 24.
  13. 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>
  14. 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>
  15. 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>
  16. 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; }
  17. 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; }
  18. 31.
  19. 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>
  20. 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>
  21. 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; }
  22. 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; }
  23. 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 */ }
  24. 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 */
  25. 49.
  26. 50.
  27. 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(); });
  28. 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;
  29. 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); };
  30. 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(); } };
  31. 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; };
  32. 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; };
  33. 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(); });
  34. 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); });
  35. 66.
  36. 67.
  37. 73.
  38. 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
  39. 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); } }; } );
  40. 81.
  41. 82.
  42. 91.

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

    sprite-map("icon/*.png", $spacing: 16px, $repeat: no-repeat, $layout: vertical);
  43. 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;
  44. 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;
  45. 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)); }
  46. 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)); }
  47. 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)); }
  48. 100.
  49. 105.
  50. 107.