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

Fundo Flutuante com transição de imagem Flash AS2 var loader_counter:Number = 0; var loader_animator:Number; var loader_format:TextFormat = new TextFormat();

Leia mais