Slides
Transcrição
Slides
Criando uma arquitetura de front-end do zero @shiota 2013 olá! slideshare.net/eshiota github.com/eshiota @shiota front-end engineer @ como a Baby (re)nasceu * reprodução pelo WaybackMachine, não é 100% precisa * reprodução do dia 27/08/2013 projeto greenfield (a.k.a. o sonho de todo desenvolvedor) como estruturar o front-end do zero? filosofia de um front-end de larga escala Alta performance client-side. Interface facilmente modificável. Componentes portáveis entre diferentes aplicações. Bulletproof web design. * veja http://simplebits.com/publications/bulletproof/ Tipografia e grids flexíveis, responsive-ready. Baixa barreira de entrada para outros desenvolvedores. definição 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 experiência em todos os navegadores? A escolha dos navegadores e o nível de suporte influencia escolhas e tempo de desenvolvimento. CSS pre-processors: sass O uso de partials ajuda a organizar os módulos. @import "compass/css3"; @import @import @import @import "base/variables"; "base/functions"; "base/mixins"; "base/helpers"; @import @import @import @import @import @import @import "core/reset"; "core/basic"; "core/forms"; "core/tables"; "core/typography"; "core/icons"; "core/buttons"; @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 Variáveis ajudam a manter os mesmos padrões 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 padronizam repetições de código. /********************************************************************* * =Image replacement * * `display` property may be overridden by the element. *********************************************************************/ @mixin 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 { @include image_replacement; width: 200px; height: 280px; background: url("mylogo.png"); } .my-other-logo { @include image_replacement; width: 100px; height: 150px; background: url("myotherlogo.png"); } Mixins permitem a criação 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 moderação) Funções 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 não há curva de adaptação para quem já escreve CSS. Extensões podem auxiliar de jeitos inimagináveis. (mais sobre isso daqui a pouco) modularização Front-end deve saber de programação? CSS possui muitas similaridades com princípios de programação. módulos contextualizados módulos padrões grid estrutura base (reset, base styles) Single Responsability Principle Módulos 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 Módulos de CSS devem poder ser extendidos sem modificar sua definição 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 Módulos macro não devem ter seus layouts alterados por módulos micro. .flyout 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 sincronização e persistência de modelos client-side? Você precisa de uma solução pronta pra fazer bind entre view e dados? Você precisa de uma estrutura pronta e fechada para manter a consistência do código? Às vezes você não precisa de um framework terceiro. =) Talvez tudo o que você precise seja um código consistente e organizado. decisões 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 benefícios. Pesquise outras soluções. 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 flow da aplicação. 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() afterCommand() productsControllerCommand() productsShowCommand() Os commands não contêm lógica, apenas inicializam outros módulos. namespaces "JavaScript é zoado! Não tem nem namespaces!" window.MYAPP = { commands : { productsShowCommand : function () { console.log("Execute code from products#show page"); } } }; MYAPP.commands.productsShowCommand(); "Mas ficar 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 Define namespaces e coloca açúcar sintático na definição de funções 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 criação de novos módulos. 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? É nóish. 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 módulos só conhecem o mediator. 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 não 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 dão a segurança para atualizações, modificações e substituições. 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 %> Próximo do HTML puro. Todos sabem (ou deveriam saber) escrever HTML puro. Menos uma dependência no projeto. classes semânticas Dica 1: Sigam tudo o que o Diego Eis falou. =) Usem classes que descrevem o conteúdo e não 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 semânticas 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-filters products-search-filtered-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 através dos data-attributes. dicas adicionais performance client-side Faça sprites. De preferência, de maneira automágica. $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 estarão 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 fizer sentido. code standards "O código deve parecer que foi escrito pela mesma pessoa, independente de quem o escreveu." Código consistente: maior legibilidade, manutenção mais fácil, evita bikeshedding. build para o deploy Ruby on Rails » Asset Pipeline » ♥ TL;DR Alta performance client-side Interface facilmente modificável Componentes portáveis entre diferentes aplicações Bulletproof web design Tipografia e grids flexíveis, responsive-ready Baixa barreira de entrada para outros desenvolvedores Não saia adicionando códigos de terceiros sem pensar. Front-end deixou de ser coisa de agência e sobrinho. obrigado! slideshare.net/eshiota github.com/eshiota @shiota
Documentos relacionados
Fundo Flutuante com transição de imagem Flash AS2
var loader_counter:Number = 0;
var loader_animator:Number;
var loader_format:TextFormat = new TextFormat();