Arquitetura de
 Front-end em
 aplicações de
  larga escala
   @shiota | eshiota.com
Front-end
Internet
Arquitetura simples




                      Arquitetura complexa
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
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
Aplicações de pequena escala
style.css




templates



application.js
& jQuery / plugins



imagens




CMS
O CSS de um blog é geralmente simples, com algumas centenas de linhas de código,
concentrado em um único arquivo. Não é tão difícil de manter.
Mesma coisa para o JS: geralmente um arquivo e alguns plugins, e tudo sendo chamado em
uma única função auto-executável.
O tema Twenty Ten do Wordpress mostra uma estrutura básica de blog: alguns templates,
um CSS. Provavelmente terá um único arquivo de JavaScript também.
Aplicações de Larga escala
Uma Intranet utilizada por dezenas de empresas e milhares de funcionários não é uma
aplicação trivial.
Ela possui diversas seções, funções, e muitos trechos podem ser reutilizados ou apenas
adaptados.
O JavaScript de uma aplicação desse porte pode ficar com alguns milhares de linhas de
código...
... o mesmo para o CSS. Nestes dois casos, eu apenas concatenei os diferentes módulos em
um único arquivo. É impossível manter dessa forma.
HTML, CSS e JavaScript
            modulares
Noção de módulo
HTML & CSS modular
<section>
  <h3>Quick links</h3>

  <ul class="arrowed">
    <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>
<section>
  <h3>Your starred content</h3>

  <ul class="iconed">
    <li class="file-locked">
      <a href="#">php|tek - 02 - PHP is cool</a><br />
      <small>in group: <a href="#">PHP</a></small>
    </li>
    <li class="photo-locked">
      <a href="#">php|tek entrance hall</a><br />
      <small>in group: <a href="#">PHP</a></small>
    </li>
    <li class="doc">
      <a href="#">Doctrine 101</a><br />
      <small>in company: <a href="#">MIH SWAT</a></small>
    </li>
    <li class="forum">
      <a href="#">PHP group files...</a><br />
      <small>in group: <a href="#">PHP</a></small>
    </li>
  </ul>
</section>
<section>
  <h3>Recommended contacts</h3>

  <ul>
    <li class="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="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>
<section>
  <h3></h3>

  <ul>
    <li></li>
  </ul>
</section>
<section>
  <h3>Recommended contacts</h3>

  <ul>
    <li class="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>
  </ul>
</section>
<article class="card group-activity">
  <div class="card-content">
    <p>
      <a href="#" class="actor">Rafael Dohms</a> joined a group:
      <a href="#" class="subject">PHP South Africa</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>
<article class="card">
  <div class="card-content">
  </div>

  <img />
</article>
.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 */
}
.button
                   .send-button




.large-button

                .confirm-button
.button
                   .send-button




.small-button

                .confirm-button
oocss.org
smacss.com
JavaScript modular
JavaScript não é (só) jQuery
shareForm.js
                                      shareField.js
shareTypeSelector.js




                       newsfeed.js
submit                              get



                                              onsuccess                            data
                       shareForm                                  shareProxy
               init                    init




shareTypeSelector                             shareField                                     newsfeed



         trigger("share-
         type-changed")     trigger("new-post")         on("share-type-changed")    on("new-post")




                                              mediator.js
Modules ± Tests = OMGBBQw00t
/**
  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();
});
/**
  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;
/**
  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);
};
/**
  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();
     }
};
/**
  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;
};
/**
  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;
};
Jasmine
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();
});
addyosmani.com/largescalejavascript
requirejs.org
//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);
        }
     };
   }
);
Otimização do front-end
html5boilerplate.com
compass-style.org
Exemplos com compass
core/_typography.scss
ui/_loader.scss
application.scss
application-ec8971025292ecb7dd2c99d430d7a76e.css
$icon-sprite: sprite-map("icon/*.png", $spacing:
16px, $repeat: no-repeat, $layout: vertical);
$icon-sprite: sprite-map("icon/*.png", $spacing:
16px, $repeat: no-repeat, $layout: vertical);
background: sprite($icon-sprite, arrow_dropdown) no-repeat;




background: url(/assets/icon-s5dab8c2901.png) -40px -158px no-repeat;
background: #f5f3eb inline-image("bg_dots.png") repeat;




background: #f5f3fb url('data:image/
png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAAAGXRF
WHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF5+TW///////
/4qZUpQAAAAN0Uk5T//
8A18oNQQAAACBJREFUeNpiYGBgAgMGBkYog4mJXAbILAiDkVxzAAIMAEMOAPMId
2OWAAAAAElFTkSuQmCC') repeat;
Estude
Modularize
 Organize
 Comente
  Otimize
$$ Profit $$
Thanks!
@shiota | eshiota.com

Evento Front End SP - Arquitetura de Front

  • 1.
    Arquitetura de Front-endem aplicações de larga escala @shiota | eshiota.com
  • 2.
  • 3.
  • 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-endde 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.
  • 8.
  • 9.
    O CSS deum blog é geralmente simples, com algumas centenas de linhas de código, concentrado em um único arquivo. Não é tão difícil de manter.
  • 10.
    Mesma coisa parao JS: geralmente um arquivo e alguns plugins, e tudo sendo chamado em uma única função auto-executável.
  • 11.
    O tema TwentyTen do Wordpress mostra uma estrutura básica de blog: alguns templates, um CSS. Provavelmente terá um único arquivo de JavaScript também.
  • 12.
  • 13.
    Uma Intranet utilizadapor dezenas de empresas e milhares de funcionários não é uma aplicação trivial.
  • 14.
    Ela possui diversasseções, funções, e muitos trechos podem ser reutilizados ou apenas adaptados.
  • 15.
    O JavaScript deuma aplicação desse porte pode ficar com alguns milhares de linhas de código...
  • 16.
    ... o mesmopara o CSS. Nestes dois casos, eu apenas concatenei os diferentes módulos em um único arquivo. É impossível manter dessa forma.
  • 17.
    HTML, CSS eJavaScript modulares
  • 18.
  • 23.
    HTML & CSSmodular
  • 25.
    <section> <h3>Quicklinks</h3> <ul class="arrowed"> <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> <h3>Yourstarred content</h3> <ul class="iconed"> <li class="file-locked"> <a href="#">php|tek - 02 - PHP is cool</a><br /> <small>in group: <a href="#">PHP</a></small> </li> <li class="photo-locked"> <a href="#">php|tek entrance hall</a><br /> <small>in group: <a href="#">PHP</a></small> </li> <li class="doc"> <a href="#">Doctrine 101</a><br /> <small>in company: <a href="#">MIH SWAT</a></small> </li> <li class="forum"> <a href="#">PHP group files...</a><br /> <small>in group: <a href="#">PHP</a></small> </li> </ul> </section>
  • 27.
    <section> <h3>Recommendedcontacts</h3> <ul> <li class="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="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> <h3></h3> <ul> <li></li> </ul> </section>
  • 30.
    <section> <h3>Recommendedcontacts</h3> <ul> <li class="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> </ul> </section>
  • 31.
    <article class="card group-activity"> <div class="card-content"> <p> <a href="#" class="actor">Rafael Dohms</a> joined a group: <a href="#" class="subject">PHP South Africa</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>
  • 32.
    <article class="card"> <div class="card-content"> </div> <img /> </article>
  • 34.
    .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 */ }
  • 35.
    .button .send-button .large-button .confirm-button
  • 36.
    .button .send-button .small-button .confirm-button
  • 37.
  • 38.
  • 39.
  • 40.
    JavaScript não é(só) jQuery
  • 41.
    shareForm.js shareField.js shareTypeSelector.js newsfeed.js
  • 42.
    submit get onsuccess data shareForm shareProxy init init shareTypeSelector shareField newsfeed trigger("share- type-changed") trigger("new-post") on("share-type-changed") on("new-post") mediator.js
  • 43.
    Modules ± Tests= OMGBBQw00t
  • 44.
    /** Loaderconstructor @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(); });
  • 45.
    /** Animationspeed (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;
  • 46.
    /** Initsthe 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); };
  • 47.
    /** Startsthe 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(); } };
  • 48.
    /** Startsthe 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; };
  • 49.
    /** Loopsthe 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; };
  • 50.
  • 51.
    it("appends the loaderto 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(); });
  • 55.
  • 56.
  • 57.
    //my/shirt.js now hassome 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); } }; } );
  • 58.
  • 59.
  • 60.
  • 61.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
    $icon-sprite: sprite-map("icon/*.png", $spacing: 16px,$repeat: no-repeat, $layout: vertical);
  • 68.
    $icon-sprite: sprite-map("icon/*.png", $spacing: 16px,$repeat: no-repeat, $layout: vertical);
  • 69.
    background: sprite($icon-sprite, arrow_dropdown)no-repeat; background: url(/assets/icon-s5dab8c2901.png) -40px -158px no-repeat;
  • 70.
    background: #f5f3eb inline-image("bg_dots.png")repeat; background: #f5f3fb url('data:image/ png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAAAGXRF WHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF5+TW/////// /4qZUpQAAAAN0Uk5T// 8A18oNQQAAACBJREFUeNpiYGBgAgMGBkYog4mJXAbILAiDkVxzAAIMAEMOAPMId 2OWAAAAAElFTkSuQmCC') repeat;
  • 72.
  • 73.