Assez(tic) de perdre du temps, – automatisez ! Grrr !



Assez(tic) de perdre du temps, – automatisez ! Grrr !

1 0


assez-tic-de-perdre-du-temps-automatisez-conf

Slides de la conférence "Assez(tic) de perdre du temps, automatisez ! Grrr !" au PHP Tour Lyon 2014

On Github jolicode / assez-tic-de-perdre-du-temps-automatisez-conf

Assez(tic) de perdre du temps,

automatisez ! Grrr !

Présentation par Claire Coloma / JoliCode

Claire COLOMA

  • Étudiante en développement mobile à l'ESGI (Paris)
  • Développeuse Web et Mobile à JoliCode

@_ClaireColoma

Je suis apprentie chez JoliCode, on me donne le temps de faire un code de qualité, de faire des recherches en ce sens, et de concevoir un projet de façon plus productive. Je vais donc vous expliquer ce que j'ai appris et les outils que j'utilise et que je préconise.

Nous allons voir :

Les surlangages à utiliser
  • Les préprocesseurs CSS
  • Les surlangages JavaScript
Les outils d'automatisation
  • Assetic
  • Grunt
  • Gulp
  • Bower

Un projet PHP est composé de :

  • HTML
  • CSS
  • JS
  • PHP

Les problématiques front

Performance
  • Minifier
  • Concaténer
  • Minimiser le nombre de requêtes
Maintenance
  • Découplage de fichiers
Nouvelles fonctionnalités
  • Bootstrap
  • Le templating
  • Angular
  • etc.
Cette structure de projet présente certaines problématiques qui sont : - des besoins en performance Toutes ces fonctionnalités comportent beaucoup de fichiers et donc beaucoup de requêtes

Pourquoi utiliser des préprocesseurs CSS ?

  • Code dynamique
  • Réutilisabilité du code
  • Simplicité du code
  • Meilleure maintenabilité
C'est là qu'interviennent les préprocesseurs CSS, qui permettent ...

I. Les surlangages à utiliser

A. Les préprocesseurs CSS

1. Fonctionnalités

Variables

Scss

$primary-color : #cc6699;
            

Less

@primary-color : #5d83a3;
            

Stylus

primary-color = #b3d107
// or
$primary-color = #b3d107
            

Mixins

Scss

@mixin error($borderWidth: 2px) {
  border: $borderWidth solid #F00;
  color: #F00;
}

.generic-error {
  padding: 20px;
  margin: 4px;
  @include error();
}
.login-error {
  left: 12px;
  position: absolute;
  top: 20px;
  @include error(5px);
}
La mixin error prend un argument optionnel qui est ici $borderWidth valant par défaut 2px En Scss on utilise l’instruction @mixin pour déclarer notre “fonction” et on y fait appel avec l’instruction @include.

Mixins

Less

.error(@borderWidth: 2px) {
  border: @borderWidth solid #F00;
  color: #F00;
}

.generic-error {
  padding: 20px;
  margin: 4px;
  .error();
}
.login-error {
  left: 12px;
  position: absolute;
  top: 20px;
  .error(5px);
}
En LESS on utilise plutôt les sélecteur de classe

Mixins

Stylus

error(borderWidth= 2px) {
  border: borderWidth solid #F00;
  color: #F00;
}

.generic-error {
  padding: 20px;
  margin: 4px;
  error();
}
.login-error {
  left: 12px;
  position: absolute;
  top: 20px;
  error(5px);
}
En Stylus les mixins se créent de la même façon que l’on crée une fonction en PHP

Héritage

Scss / Stylus

.block {
  margin: 10px 5px;
  padding: 2px;
}

p {
  @extend .block;
  border: 1px solid #EEE;
}
ul, ol {
  @extend .block;
  color: #333;
  text-transform: uppercase;
}

Rendu CSS

.block, p, ul, ol {
  margin: 10px 5px;
  padding: 2px;
}
p {
  border: 1px solid #EEE;
}
ul, ol {
  color: #333;
  text-transform: uppercase;
}
Scss et Stylus utilise l’instruction @extends pour récupérer un bloc d’instruction - p et ul, ol héritent des styles de '.block'

Héritage

LESS

.block {
  margin: 10px 5px;
  padding: 2px;
}

p {
  .block;
  border: 1px solid #EEE;
}
ul, ol {
  .block;
  color: #333;
  text-transform: uppercase;
}

Rendu CSS

.block {
  margin: 10px 5px;
  padding: 2px;
}
p {
  margin: 10px 5px;
  padding: 2px;
  border: 1px solid #EEE;
}
ul,
ol {
  margin: 10px 5px;
  padding: 2px;
  color: #333;
  text-transform: uppercase;
}
Quant à LESS, cette fonctionnalité n’existe pas et on utilise à la place les mixins. Le rendu CSS est moins performant

Structure

CSS

nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

nav li {
  display: inline-block;
}

nav a {
  display: block;
  padding: 6px 12px;
  text-decoration: none;
}

Scss / LESS / Stylus

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}
On obtient un code plus lisible, et donc plus facilement maintenable

2. Compilation

Sass

$ sass input.scss output.css
            

LESS

$ lessc input.less output.css
            

Stylus

$ stylus < input.styl > output.css
            

Les options de compilation : watch

  • Compiler automatiquement à chaque enregistrement d'une modification

Sass

$ sass --watch input.scss:output.css
            

Stylus

$ stylus --watch < input.styl > output.css
            
Malheureusement LESS ne dispose pas de cette fonctionnalité

Les options de compilation : fichiers multiples

  • Compiler un répertoire d'un coup

Sass

$ sass app/sass:public/stylesheets
            

Stylus

$ stylus styl/
            
SASS : watch nécessaire Avec stylus on a juste indiqué le nom du dossier qui contient les .styl, cependant on ne peut pas indiquer le dossier de destination. LESS ne permet pas cette compilation, la solution serait de créer un fichier .less qui importe tous les fichiers less à compiler.

Les options de compilation : Minify

  • Minifier ses fichiers de style

LESS

$ lessc --clean-css input.less output.css
            

Sass

$ sass input.scss output.css --style compressed
            

Stylus

$ stylus < input.styl > output.css --compress
            
Les 3 préprocesseurs permettent de récupérer un fichier css directement minifier.

3. Comparaison*

Sass (2006)

371

3 912

722

LESS (Fév. 2010)

606

10 627

2 367

Stylus (Déc. 2010)

225

4 464

621

Issues :

116 Open

1 177 Closed

Issues :

250 Open

1 815 Closed

Issues :

168 Open

1 426 Closed

* Données récupérées le 21/06/14

B. Les préprocesseurs Javascript

CoffeeScript

CoffeeScript est le préprocesseur Javascript le plus utilisé, il permet d'améliorer la lisibilité du Javascript, en écrivant du code simple et est surtout moins verbeux

1. Syntaxe

JavaScript
  • " ; " oublié ? => erreur
  • " { " ou " } " oublié ? => erreur
CoffeeScript

Besoin d'aucun des deux

Syntaxe

JavaScript
doClick = function(e) {
  return console.log(e.index);
};
CoffeeScript
doClick = (e) ->
    console.log(e.index)
var math;
math = {
  square: function(x) {
    return Math.sqrt(x);
  },
  cube: function(x) {
    return x * square(x);
  }
};
alert("Three cubed is " + (math.cube(3)));
math =
    square: (x) -> Math.sqrt x
    cube:   (x) -> x * square x
alert "Three cubed is #{math.cube 3}"
if (typeof joli !=="undefined" && joli !==null) {
    alert("I knew it!");
}
alert "I knew it!" if joli?
                    

Syntaxe

JavaScript

  • ===
  • !==
  • !
  • &&
  • ||
  • true
  • false

Hello

CoffeeScript

  • is
  • isnt
  • not
  • and
  • or
  • true, yes, on
  • false, no, off

2. Compilation

  • Compilation d'un simple fichier CoffeeScript
$ coffee --compile --output js/ chocolate.coffee
  • Compilation d'un répertoire CoffeeScript
$ coffee --compile --output js/ coffeedir/
  • Compilation automatique
$ coffee --watch --compile chocolate.coffee
  • Compilation d'un répertoire en un seul fichier JS
$ coffee --join js/project.js --compile coffee/

Récapitulatif

  • Code dynamique
  • Code découplé
  • Nombre de requêtes diminué
  • Moins verbeux
  • Meilleure maintenabilité

Mais...

Beaucoup de commandes Comme vous avez pu le voir, beaucoup de bons outils existent, mais l'implémentation correcte de chacune peut être long et difficile Il faut connaître la commande pour chaque compilation, et en plus la taper à chaque fois qu'on fait des modifications Ce qui peut être fastidieux, Mais les outils d'automatisation sont là pour ça ! Et rendent ces processus facile et rapide! (Gif superhéros ?)

II. Les outils d'automatisation

A. Assetic

Assetic est un framework de gestion d'assets qui a été créé par Kris Wallsmith en 2011 et bien sûr open source

1. Fonctionnalités

  • Gestion des ressources
  • Utilisation de filtres
Les filtres ce sont des processus qui sont appliqués à ces fichiers avant d'être lu par le navigateur

Gestion des ressources

  • Ajouter un dossier complet
  • Combiner des fichiers

Les filtres

  • Réécrire vos liens relatifs : cssrewrite
  • Minifier vos ressources : cssmin
  • Compiler vos fichiers: coffee, etc.
Une fois qu'elles sont gérées par Assetic, vous pouvez appliquer des filtres à vos ressources avant qu'elles ne soient servies. cssrewrite: Quand assetic gère les ressources, il les sert depuis un autre endroit, du coup les chemin relatifs de vos images ou autres sources sont cassés. cssrewrite permet de réparer ces liens.

2. Exemple d'utilisation

CSS

{% stylesheets
    'bundles/event/css/event.css'
    'bundles/event/css/buttons.css'
    'bundles/event/css/main.css'
    filter='cssrewrite'
    filter='cssmin'
    output='css/generated/layout.css'
%}
    <link rel="stylesheet" href="{{ asset_url }}">
{% endstylesheets %}
Un exemple de code généré :
<link rel="stylesheet" href="/app_dev.php/css/all_main1.css" type="text/css">
                
Le asset_url est l'url généré des fichiers css compilés

Exemple d'utilisation

Javascript

{% javascripts
    '@AcmeFooBundle/Resources/public/coffee/*'
    '@SiteBundle/Resources/public/coffee/form.coffee'
    output='js/compiled/main.js'
    filter='coffee' %}
    <script src="{{ asset_url }}"></script>
{% endjavascripts %}
            

3. Configuration

Le fichier config.yml
# app/config/config.yml

# Assetic Configuration
assetic:
    debug:          "%kernel.debug%"
    use_controller: false
    bundles:        ['AcmeFooBundle', 'SiteBundle']
    filters:
        sass: ~
        compass: ~
            

4. Commandes

  • Copie les liens symboliques des assets référencées dans web
    $ php app/console asset:install web -symlink
                    
  • Génère automatiquement les ressources nécessaires
    $ php app/console assetic:dump --watch
                        
  • Génère les ressources pour la prod
    $ php app/console assetic:dump --env=prod --no-debug
                        
asset install : va copier les liens symboliques des asset référencées dans web.

B. Grunt

Grunt est un gestionnaire de tâches qui a été créé en 2011 et qui peut être écrit en Js ou CoffeeScript Need Node.JS

1. Installation

Grunt
$ npm install -g grunt
$ npm install -g grunt-cli
            

2. Fonctionnalités

  • Minifier vos fichiers
  • Compiler automatiquement
  • Gérer votre proxy livereload
  • Optimiser vos images
  • Concaténation de fichiers
  • Lancer des tâches automatiquement

3. Les principaux plugins

  • sass : permet la compilation de sass/scss en CSS
  • watch : permet de lancer des tâches automatiquement
  • jshint : permet de détecter les erreurs potentiels de son code JavaScript
  • clean : permet de supprimer des fichiers et dossiers
  • css-min : permet de concaténer et minifier les fichiers CSS
  • uglify : permet de concaténer et minifier les fichiers JS

4. Fichiers nécessaires

package.json : liste les dépendances Gruntfile.js (ou Gruntfile.coffee) : charge les modules et configure les tâches à exécuter

package.json

{
    "name": "My badass project",
    "description": "Des poneys et des petits coeurs partout partout"
    "version": "0.0.1",
    "author": "Claire COLOMA",

    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-jshint": "~0.6.4",
        "grunt-contrib-cssmin": "~0.4.1",
        "grunt-contrib-uglify": "~0.2.4",
        "grunt-contrib-watch": "~0.5.3",
    },
}
$ npm install
            
Le fichier package.json contient les données principales de votre projet Nom du projet + Auteur + Version + Description Et entre autre les dépendances, dont celle de grunt Il faut donc en informer ces informations : Une fois ces infos renseignées, il faut ensuite les installer avec le npm install

Créer un Gruntfile.js : Structure

module.exports = function(grunt) {
    // Importation des différents modules grunt
    grunt.loadNpmTasks('grunt-plugin');

    // Configuration des plugins
    grunt.initConfig({});

    // Déclaration des différentes tâches
    grunt.registerTask('default', ['tache1', 'tache2']);
};
            

Gruntfile.js : Importation des plugins

module.exports = function(grunt) {
    // Importation des différents modules grunt
    grunt.loadNpmTasks('grunt-contrib-compress');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
};
            
module.exports = function(grunt) {
    require('load-grunt-tasks')(grunt);
};
                

Gruntfile.js : Configuration des plugins

module.exports = function(grunt) {
    grunt.initConfig({
        coffee: {
            compile: {
                files: {
                  'js/output1.js': 'coffee/input1.coffee',
                  'js/output2.js': 'coffee/input2.coffee'
                }
            }
        },

        sass: {
            dist: {
                options : {
                    style: 'compressed'
                },
                files: {
                    'css/output1.css': 'scss/input1.scss',
                    'css/output2.css': 'scss/input2.scss'
                }
            }
        },

        watch: {
            css: {
                files: ['scss/*.scss'],
                tasks: ['sass'],
            }
            script: {
                files: ['coffee/*.coffee'],
                tasks: ['coffee']
            }
        }
    });
};

Gruntfile.js : Déclaration des tâches

module.exports = function(grunt) {
    // Déclaration des tâches
    grunt.registerTask('compress', ['sass', 'coffee']);
    grunt.registerTask('default', ['compress', 'watch']);
    grunt.registerTask( 'serve', [ 'connect', 'watch'] );
};
On tape ensuite la commande :
$ grunt [task-name]
            
Il doit toujours y avoir au moins la tâche "default"

5. Pourquoi Grunt est meilleur qu'Assetic ?

  • Découpler Application / Gestion des ressources
  • Plus de fonctionnalités
  • Lancement de tâches séparées
  • Plus simple

C. Gulp

Gulp permet aussi de gérer des tâches, le projet a commencé il y a moins d'un an (juillet 2013)

1. Différences Grunt / Gulp ?

  • Approche Code et pas Config
  • Asynchrone
  • Plus rigoureux avec les plugins

Différences Grunt/Gulp ?

Grunt
module.exports = function(grunt) {
    grunt.initConfig({
        sass: {
            dist: {
                files: [{
                    expand: true,
                    cwd: 'css/theme/source/',
                    src: ['*.scss'],
                    dest: 'css/theme/',
                    ext: '.css'
                }]
            }
        },

        watch: {
            css: {
                files: ['scss/*.scss'],
                tasks: ['sass'],
            }
        }
    });

    // Importation des différents modules grunt
    require('load-grunt-tasks')(grunt);

    // Déclaration des tâches
    grunt.registerTask('default', ['sass','watch']);
};
Gulp
var gulp = require ('gulp');

// Importation des plugins
var sass = require('gulp-sass');
var cssmin = require('gulp-cssmin');

// Traitement des tâches
gulp.task('sass', function () {
    gulp.src('scss/*.scss')
        .pipe(plumber())
        .pipe(sass())
        .pipe(gulp.dest('css/'))
        .pipe(rename({suffix:'.min'}))
        .pipe(cssmin())
        .pipe(gulp.dest('css/'))
});


gulp.task('watch', function() {
    gulp.watch('scss/*.scss', ['sass']);
})

gulp.task('default', ['sass', 'watch']);

Gulp est asynchrone

Gulp utilise des streams
Sans stream :
Avec stream :

2. Installation

  • Gulp sur la machine :
    $ npm install -g gulp
                        
  • Initialisation dans le projet :
    $ npm install gulp --save-dev
                        

3. Fichiers nécessaires

package.json : liste les dépendances gulpfile.js : charge les modules et traite les tâches à effectuer

package.json

{
    "devDependencies": {
        "gulp": "~3.6.2",
        "gulp-connect": "~2.0.5",
        "gulp-jshint": "~1.6.1",
        "gulp-sass": "~0.7.1",
        "gulp-plumber": "~0.6.2",
    },
}
$ npm install
            
Le fichier package.json contient les données principales de votre projet Nom du projet + Auteur + Version + Description Et entre autre les dépendances, dont celle de grunt Il faut donc en informer ces informations : Chaque fois qu'un plugins est installer sa version est renseignée ici

Créer un gulpfile.js : Structure

// Importation de l'API
var gulp = require ('gulp');

// Importation des plugins
var task = require('gulp-task');

// Traitement des tâches
gulp.task('default', ['task1', 'task2']);
            
On tape ensuite la commande :
$ gulp [task-name]
            

4. Les tasks

  • plumber : permet de réparer les pipes foireux
  • notify : permet d'être notifié sur son terminal
  • rename : permet de renommer un fichier
Node a un process qui fait que le streaming pète à la moindre erreur Ce qui n'est pas pratique quand on watch par ex.

D. Bower

Bower ça sert à quoi ?

  • Un gestionnaire de dépendances front-end
    • Underscore.js
    • AngularJS
    • Modernizr
    • jQuery
    • Backbone.js
    • etc...
  • = composer.phar

Installation d'un composant

  • $ bower install package-name
                        
  • D'une version précise
    $ bower install package-name#version
                        
  • D'un dépôt Git
    $ bower install https://github.com/user-name/package-name
                        

Fichiers nécessaires

.bowerrc : indique le dossier où seront stockés les composants bower.json : liste les dépendances

.bowerrc

Répertoire des composants
{
  "directory": "vendor/bower_components"
}
            

bower.json

Liste des versions des composants
{
    "name": "My badass project",
    "description": "Des poneys et des petits coeurs partout partout"
    "version": "0.0.1",
    "author": "Claire COLOMA",
    },

    "dependencies": {
        "angular": "~1.2.13",
        "modernizr": "~2.6.2",
        "moment": "~2.4.0",
        "underscore": "~1.4.4",
    },
} 

Utilisation d'un paquet

Ajouter le composant manuellement dans son code :
<script src="vendor/bower_components/jquery/jquery.js"></script>
            

Mettre à jour un paquet

$ bower update package-name
            

Conclusion

  • Utilisez les outils d'industrialisation nécessaires à vos projets
  • Regroupez vos tâches
  • Grunt va introduire les streams
  • Yeoman
  • Automatisez :)
Yeoman est un outil de scaffolding permettant de mettre en place rapidement vos projets. Yeoman s’occupe de créer l’architecture de votre projet, configurer Grunt avec les tâches essentielles, initialiser Bower, tout pour bien démarrer votre projet.

Merci !

Claire COLOMA / @_ClaireColoma http://jolicode.github.io/phptourconf-tools