Vous êtes sur la page 1sur 199

Criando uma arquitetura de front-end do zero

@shiota

2013

ol!
slideshare.net/eshiota github.com/eshiota @shiota

como a Baby (re)nasceu

* reproduo pelo WaybackMachine, no 100% precisa

* reproduo do dia 27/08/2013

projeto greeneld
(a.k.a. o sonho de todo desenvolvedor)

como estruturar o front-end do zero?

losoa de um front-end de larga escala

Alta performance client-side.

Interface facilmente modicvel.

Componentes portveis entre diferentes aplicaes.

Bulletproof web design.

* veja http://simplebits.com/publications/bulletproof/

Tipograa e grids exveis, responsive-ready.

Baixa barreira de entrada para outros desenvolvedores.

denio de suporte aos navegadores

latest

latest

5+ iOS 6+

8+

u mad?

latest

latest

5+ iOS 6+

8+

O meu website precisa ter o visual exatamente igual em todos os navegadores?

O meu website precisa ter exatamente a mesma experincia em todos os navegadores?

A escolha dos navegadores e o nvel de suporte inuencia escolhas e tempo de desenvolvimento.

CSS

pre-processors: sass

O uso de partials ajuda a organizar os mdulos.

@import "compass/css3";!

!
@import @import @import @import "base/variables";! "base/functions";! "base/mixins";! "base/helpers";! "core/reset";! "core/basic";! "core/forms";! "core/tables";! "core/typography";! "core/icons";! "core/buttons";!

!
@import @import @import @import @import @import @import

!
@import "layout/main";! @import "layout/header";! @import "layout/footer";!

!
@import @import @import @import "ui/loader";! "ui/loaderBar";! "ui/flashMessage";! "ui/breadcrumb";

app/! assets/! stylesheets/! base/! _functions.scss! _mixins.scss! _variables.scss! ui/! _breadcrumb.scss! _carousel.scss! _dentedBox.scss! _flashMessage.scss

Variveis ajudam a manter os mesmos padres de interface.

/*********************************************************************! *! * Variables Module! *! * All constants that will be used through the styles must be! * defined here.! *! *********************************************************************/!

!
/*********************************************************************! * =Dimensions! *********************************************************************/!

!
$SITE_WIDTH: 978px;! $FOOTER_HEIGHT : 812px;! $DEFAULT_FONT_SIZE : 16px;!

!
/*********************************************************************! * =Colors! *********************************************************************/!

!
$TEXT_COLOR: #555;! $LINK_COLOR: #447f87;!

!
$PURPLE: #905194;! $LIGHT: #fefefa;! $ORANGE: #fbb100;! $YELLOW: #fffd7d;

Mixins e placeholders padronizam repeties de cdigo.

/*********************************************************************! * =Image replacement! *! * `display` property may be overridden by the element.! *********************************************************************/!

!
%image_replacement {! text-indent: 101%;! overflow: hidden;! white-space: nowrap;! display: block;! }

.my-logo {! text-indent: 100%;! overflow: hidden;! white-space: nowrap;! display: block;! width: 200px;! height: 280px;! background: url("mylogo.png");! }! ! .my-other-logo {! text-indent: 100%;! overflow: hidden;! white-space: nowrap;! display: block;! width: 100px;! height: 150px;! background: url("myotherlogo.png");! }

.my-logo {! @extend %image_replacement;! width: 200px;! height: 280px;! background: url("mylogo.png");! }! ! .my-other-logo {! @extend %image_replacement;! width: 100px;! height: 150px;! background: url("myotherlogo.png");! }

Mixins permitem a criao de temas.

/*! <div class="section-header my-theme1">! ...! </div>! */!

!
.section-header {! width: 100%;! height: 15em;! color: #fff;! background-color: #905194;! background-position: center center;! background-repeat: no-repeat;! background-image: url("themes/default_bg.jpg");! text-align: center;! }!

!
.my-theme1 {! background-color: #fbb100;! color: #fff;! background-image: url("themes/theme1_bg.jpg");! }!

!
.my-theme2 {! background-color: #fefefa;! color: #333;! background-image: url("themes/theme2_bg.jpg");! }

/*! <div class="section-header my-theme1">! ...! </div>! */!

@mixin header_theme($background_color: #905194, $text_color: #fff, $image: "default_bg.jpg") {! background-color: $background_color;! color: $text_color;! background-image: url("themes/#{$image}");! }

/*! <div class="section-header my-theme1">! ...! </div>! */!

!
.section-header {! @include header_theme;! width: 100%;! height: 15em;! background-position: center center;! background-repeat: no-repeat;! text-align: center;! }!

!
.my-theme1 {! @include header_theme(#fefefa, #333, "theme2_bg.jpg");! }!

!
.my-theme2 {! @include header_theme(#fefefa, #333, "theme2_bg.jpg");! }

(use com moderao)

Funes aceleram o processo de desenvolvimento.

// Returns unitless number! @function remove-unit($number) {! $unit: unit($number);! $one: 1;!

! !

@if $unit == "px" { $one: 1px; }! @if $unit == "em" { $one: 1em; }! @if $unit == "%" { $one: 1%; }! @return $number / $one;!

}!

// Returns flexible value using `target "context` formula.! // Returns `em` by default, accepts `%` as format.! @function flex($target, $context, $unit: "em") {! $size: remove-unit($target) / remove-unit($context);!

! !

@if $unit == "em" { @return #{$size}em; }! @if $unit == "%" { @return percentage($size); }!

}! // Alias to `flex` function, using `%` as format.! @function perc($target, $context: $DEFAULT_FONT_SIZE) {! @return flex($target, $context, "%");! }!

// Alias to `flex` function, using `em` as format.! @function em($target, $context: $DEFAULT_FONT_SIZE) {! @return flex($target, $context, "em");! }

.product-title {! font-size: 1.5625em; /* 25px / 16px */! }

.product-title {! font-size: em(25px, 16px);! }

.product-title {! font-size: em(25px);! }

Sintaxe SCSS: quase no h curva de adaptao para quem j escreve CSS.

Extenses podem auxiliar de jeitos inimaginveis.


(mais sobre isso daqui a pouco)

modularizao

Front-end deve saber de programao?

CSS possui muitas similaridades com princpios de programao.

mdulos contextualizados mdulos padres

grid estrutura base (reset, base styles)

Single Responsability Principle


Mdulos de CSS possuem comportamentos contidos e isolados.

/*****************************************************************************! *! * UI "Flyout! *! * Flyouts are those UI components that look like tooltip, and! * are activated when the user clicks on a link. The flyout window! * opens text to the link, like those present on the iPad.! *! * **Usage**! *! * <div class="flyout-container">! * <div class="flyout [vertical-position]-[horizontal-position]-flyout">! * Flyout content! * </div>! * </div>! *! *****************************************************************************/!

! !

.flyout-container {! position: relative;! z-index: 100; // may be adjusted as needed through a context! }! .flyout {! @include box-sizing(border-box);! background: #f9f9f9;! border-radius: 2px;! border: 1px solid #d5d5d5;! box-shadow: 0 2px 0 rgba(0, 0, 0, .1);! display: none;! position: absolute;!

// tip! &:after {! content: "";! display: block;! width: 40px;! height: 22px;! background: sprite($icon-sprite, tooltip_top_large_gray) no-repeat;! position: absolute;! }!

}!

...

Open/close Principle
Mdulos de CSS devem poder ser extendidos sem modicar sua denio core.

/*******************************************************************************! *! * UI > Loader! *! * Animated loader for AJAX requests! *! *******************************************************************************/!

! !

@mixin loader_sprite_position($xoffset, $yoffset) {! background-position: sprite-position($icon-sprite, loader_sprite, $xoffset, $yoffset);! }! .loader {! width: 25px;! height: 25px;! display: none;! }!

.loader b {! display: block;! width: 25px;! height: 25px;! background-image: sprite-url($icon-sprite);! }!

.loader .loader .loader .loader .loader .loader .loader .loader .loader

b,! .f1 .f2 .f3 .f4 .f5 .f6 .f7 .f8

{ { { { { { { {

@include @include @include @include @include @include @include @include

loader_sprite_position(-10px, -10px); }! loader_sprite_position(-45px, -10px); }! loader_sprite_position(-80px, -10px); }! loader_sprite_position(-115px, -10px); }! loader_sprite_position(-150px, -10px); }! loader_sprite_position(-185px, -10px); }! loader_sprite_position(-220px, -10px); }! loader_sprite_position(-255px, -10px); }

// On ui/_buttons.scss!

!
.bt-wrapper .loader {! position: absolute;! z-index: 4;! right: 20px;! top: 50%;! margin-top: -9px;! }!

!
// On modules/_checkoutAddressForm.scss!

!
.address-form .cep-input .loader {! position: absolute;! right: -33px;! top: em(29px);! }

Dependency Inversion Principle


Mdulos macro no devem ter seus layouts alterados por mdulos micro.

.yout

guias de estilo

#cheat #wip

JavaScript

qual framework usar?

Analise qual (ou se) vale a pena.

Voc precisa de rotas client-side?

Voc precisa de sincronizao e persistncia de modelos client-side?

Voc precisa de uma soluo pronta pra fazer bind entre view e dados?

Voc precisa de uma estrutura pronta e fechada para manter a consistncia do cdigo?

s vezes voc no precisa de um framework terceiro. =)

Talvez tudo o que voc precise seja um cdigo consistente e organizado.

decises de arquitetura

"Mas Shiota, todo mundo falou pra eu abandonar o jQuery!"

O jQuery diminui bastante a barreira de entrada e d agilidade.

Analise a necessidade. Pese os benefcios. Pesquise outras solues.

single entry points

(function(){! window.app = jQuery.extend({! init: function(){! tab = $('.tabs li > a.tab-toggle');! tabs = $('.tabs').find('> div');!

if (tabs.length > 1){! tab.each(function (i){$(this).attr('href', '#content-' + ++i)});! tabs.each(function(i){$(this).attr('id', 'content-' + ++i)});! tabs.addClass('tab-inactive');! $('.tabs li:first-child a').addClass('state-active');! }! $('#initial-cash, #financing_value_vehicles, #tax, #bid-initial-cash, #bid-product-value').maskMoney({! thousands: '.',! decimal: ',',! allowZero: true,! allowNegative: false,! defaultZero: true! });! /** FINANCING CALCULATOR **/! $("#financing_value_vehicles").on("blur", function(){! var price = (accounting.unformat($(this).val(), ",")) || 0;! var suggestedInitialPayment = price * 0.2;! var formattedResult = accounting.formatMoney(suggestedInitialPayment, "", "2", ".", ",");! $("#initial-cash").val(formattedResult);! });!

! ! ! ! ! ! ! ! !

$("#calculate-financing").click(function(event){! var price = (accounting.unformat($("#financing_value_vehicles").val(), ",")) || 0;! var rate = (accounting.unformat($("#tax").val(), ",") / 100) || 0;! var initialCash = (accounting.unformat($("#initial-cash").val(), ",")) || 0;! var value = (accounting.unformat($("#amount-finance").val(), ",")) || 0;! var finance = price - initialCash;! var months = (accounting.unformat($("#prize_parcela").val(), ",")) || 0;! var tax = parseFloat(rate);!

Page load jQuery load jQuery plugins application.js

Pontos nicos de entrada controlam o ow da aplicao.

Page load Vendor code Application modules application.js dispatcher.js

beforeCommand

controllerCommand

actionCommand

afterCommand

Page load Vendor code Application modules application.js dispatcher.js

beforeCommand

controllerCommand

actionCommand

afterCommand

<body data-dispatcher="<%= dispatcher_label %>">

<body data-dispatcher="products#show">

dispatcher.js

products#show

beforeCommand() productsControllerCommand()

afterCommand()

productsShowCommand()

Os commands no contm lgica, apenas inicializam outros mdulos.

namespaces

"JavaScript zoado! No tem nem namespaces!"

window.MYAPP = {! commands : {! productsShowCommand : function () {! console.log("Execute code from products#show page");! }! }! };! ! MYAPP.commands.productsShowCommand();

"Mas car declarando objetos um saco, e voc pode acabar sobrescrevendo..."

;(function (root) {! root.ns = function (name, obj, scope) {! var parts = name.split(".")! , curScope = scope || root! , curPart! , curObj! ;!

!
obj = obj || {};!

!
while (typeof (curPart = parts.shift()) !== "undefined") {! curObj = (parts.length > 0)! ? ((typeof curScope[curPart] !== "undefined") ? curScope[curPart] : {})! : obj;!

!
curScope[curPart] = curObj;!

!
curScope = curScope[curPart];! }!

!
return curScope;! };! })(this);

ns("MYAPP.commands.productsShowCommand", function () {! console.log("Execute code from products#show page");! });! ! // Same as:! ! window.MYAPP = {! commands : {! productsShowCommand : function () {! console.log("Execute code from products#show page");! }! }! };

module.js

Dene namespaces e coloca acar sinttico na denio de funes construtoras.

window.EDEN = window.EDEN || {};! EDEN.forms = EDEN.forms || {};!

!
EDEN.forms.AddressForm = function (el) {! this.element = $(el);! this.init();! }!

!
$.extend(EDEN.forms.AddressForm.prototype, {!

!
// Public methods! // --------------!

!
// Inits the instance! init : function () {! // Do something! }!

!
});!

!
var shippingAddressForm = new EDEN.forms.AddressForm($("#shipping-address"));

Module("EDEN.forms.AddressForm", function (AddressForm) {!

! ! ! ! ! !

AddressForm.fn.initialize = function (el) {! this.element = $(el);! // Do form stuff! }!

});! var shippingAddressForm = Module.run("EDEN.forms.AddressForm", $("#shipping-address"));! // or! var shippingAddressForm = new EDEN.forms.AddressForm($("#shipping-address"));

Padroniza a criao de novos mdulos.

desacoplamento via eventos

MEDIATOR

Mediator, me avisa quando sair o novo do Game of Thrones?

Blz

MEDIATOR

Mediator, me avisa quando sair o novo do Mythbusters?

nish.

MEDIATOR

Ae, vou baixar, acho que vai ser feliz e tal =D

Mediator, saiu um eppy novo de Game of Thrones.

Subscribers, saiu um eppy novo de Game of Thrones!

MEDIATOR

Ae, vou baixar! Mediator, saiu um eppy novo de Mythbusters.

Subscribers, saiu um eppy novo de Mythbusters!

MEDIATOR

Os mdulos s conhecem o mediator.

mdulos desacoplados, com comportamentos especcos e isolados

// Code inside ShippingAddressForm!

!
_registerInterests : function () {! this.element.find(".cep-input")! .on("keyup paste cut", this._onCepModification.bind(this)); },! !

!
_onCepModification : function (event) {! if (this.isCepFilled()) {! EDEN.mediator.trigger("shipping-cep-change", event.target.value);! } else {! EDEN.mediator.trigger("shipping-cep-incomplete", event.target.value);! }! }

// Code inside checkoutModule!

!
_registerInterests : function () {! EDEN.mediator.on("shipping-cep-change", this._onShippingCepChange, this);! this.shippingService.on("get-success", this._onShippingGetSuccess, this);! },!

!
_onShippingCepChange : function (cep) {! this.shippingService.get(cep);! }!

!
_onShippingGetSuccess : function (data) {! EDEN.mediator.trigger("shipping-rate-change", data.rate);! EDEN.mediator.trigger("delivery-estimate-change", data.estimate);! }

// Code inside purchseInfo!

_registerInterests : function () {! EDEN.mediator.on("shipping-rate-change", this._onShippingRateChange, this);! EDEN.mediator.on("delivery-estimate-change", this._onDeliveryEstimateChange, this);! },!

! ! ! ! ! ! ! !

_onShippingRateChange : function (rate) {! this.updateShippingRate(rate);! },! _onDeliveryEstimateChange : function (days) {! this.updateDeliveryEstimate(days);! },! updateShippingRate : function (rate) {! var formatter = EDEN.currency.formatter;! this.element.find(".shipping-rate").text(formatter(rate));! this.shippingRate = rate;!

this.updateTotal();! },! updateTotal : function () {! var total = this.subtotal + this.shippingRate,! formatter = EDEN.currency.formatter;! this.element.find(".total").text(formatter(total));! EDEN.mediator.trigger("cart-total-change", total);!

// Code inside installmentSelector!

!
_registerInterests : function () {! EDEN.mediator.on("cart-total-change", this._onCartTotalChange, this);! },!

!
_onCartTotalChange : function (total) {! this.updateInstallments(total);! },!

!
updateInstallments : function (total) {! // Updates the values! }

testes

describe("EDEN.ui.Loader", function () {! var Loader = EDEN.ui.Loader;!

! ! ! ! ! ! !

beforeEach(function () {! loadFixtures("loader.html");! });! afterEach(function () {! $("body").find(".loader").remove();! });! it("accepts instance creation without new operator", function () {! var newLoader = Loader();! expect(newLoader).toBeInstanceOf(Loader);! });! 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);! });! });

"Mas se eu escrever teste de JavaScript, eu no entrego o projeto!"

(e os testes de JavaScript quebram o build do CI.)

"Mas pra que teste de JavaScript?"

"Precisamos atualizar o jQuery de 1.4 para 1.10"

Testes do a segurana para atualizaes, modicaes e substituies.

HTML

sintaxe: erb

<% product.foreach_variant(current_cart) do |variant, size, i| %>! <label class="variant">! <input value="<%= size.value %>" name="product_size" type="radio"! data-price="<%= variant.price.to_f %>" data-variant-sku="<%= variant.sku %>"! class="<%= "disabled" unless variant.has_stock? %>" />! <span class="variant-name"><%= size.value %></span>! </label>! <% end %>

Prximo do HTML puro.

Todos sabem (ou deveriam saber) escrever HTML puro.

Menos uma dependncia no projeto.

classes semnticas

Usem classes que descrevem o contedo e no o estilo.

<div class="column-left">! <!-- Dropdown Categoria -->! <!-- Dropdown Marca -->! </div>!
!

<div class="column-right">! <!-- Product Navigation -->! <!-- Products List -->! </div>

column-left?

column-right?

Classes semnticas desacoplam o documento do estilo.

<div class="products-search-filters">! <!-- Dropdown Categoria -->! <!-- Dropdown Marca -->! </div>!
!

<div class="products-search-filtered-results">! <!-- Product Navigation -->! <!-- Products List -->! </div>

products-search-lters

products-search-ltered-results

Maior portabilidade do markup entre diferentes projetos.

data-attributes

contentDropdown.js

Componentes com mesmo comportamento podem ter estilos diferentes.

<div class="size-selector">! <div class="size-selector-header" data-dropdown-header>! <!-- some title, could be anything -->! </div>!

!
<div class="size-selector-content" data-dropdown-content>! <!-- content here -->! </div>! </div>!

!
<div class="filter-selector">! <div class="filter-selector-header" data-dropdown-header>! <!-- some title, could be anything -->! </div>!

!
<div class="filter-selector-content" data-dropdown-content>! <!-- content here -->! </div>! </div>

Classes diferentes, estilos diferentes.

data-attributes iguais, comportamento igual.

// Inits the `ContentDropdown` instance! //! // * `el`: jQuery selector! init : function (el) {! this.element = $(el);! this.content = this.element.byData("dropdown-content");! this.selection = this.element.byData("dropdown-selection");! }

$.fn.byData = function (dataAttr) {! return $(this).find("[data-" + dataAttr + "]");! };

Comportamento adicionado atravs dos data-attributes.

dicas adicionais

performance client-side

Faa sprites. De preferncia, de maneira automgica.

$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);

/* 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;

Use inline images para imagens < 1KB que estaro apenas em um lugar do CSS.

/* Generates a base64 image */!

!
background: #f5f3eb inline-image("bg_dots.png") repeat;

/* Compiled CSS */!

!
background: #f5f3fb url('data:image/ png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAAAGXRFWHRTb2Z0d2FyZQBBZG9 iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF5+TW////////4qZUpQAAAAN0Uk5T// 8A18oNQQAAACBJREFUeNpiYGBgAgMGBkYog4mJXAbILAiDkVxzAAIMAEMOAPMId2OWAAAAAElFTkSuQmCC ') repeat;

Use lazy-load onde zer sentido.

code standards

"O cdigo deve parecer que foi escrito pela mesma pessoa, independente de quem o escreveu."

Cdigo consistente: maior legibilidade, manuteno mais fcil, evita bikeshedding.

build para o deploy

Ruby on Rails Asset Pipeline

TL;DR

Alta performance client-side Interface facilmente modicvel Componentes portveis entre diferentes aplicaes Bulletproof web design Tipograa e grids exveis, responsive-ready Baixa barreira de entrada para outros desenvolvedores

No saia adicionando cdigos de terceiros sem pensar.

Front-end deixou de ser coisa de agncia e sobrinho.

obrigado!
slideshare.net/eshiota github.com/eshiota @shiota

Vous aimerez peut-être aussi