Pro Yearly is on sale from $80 to $50! »

Life After Assetic: State of the Art Symfony 2 Frontend Dev

Life After Assetic: State of the Art Symfony 2 Frontend Dev

Assetic is a great tool. It helps us build our SASS and LESS files and minimises our JavaScript. But with Assetic we still have to do a lot of manual work. There are better ways to automate our frontend workflow. With Gulp and Bower, we can ditch Assetic and get to grips with the latest and greatest. We will go through how to automate and optimise our Symfony2 frontend workflow in a more efficient manner than we have ever done before.

01da6d807a29ad6d49801c0157518148?s=128

Michelle Sanver

November 28, 2014
Tweet

Transcript

  1. @michellesanver SymfonyCon Madrid 2014 Life After Assetic State of the

    Art Symfony 2 Frontend Dev
  2. @michellesanver

  3. @michellesanver Intro A simple Symfony2 application

  4. @michellesanver A simple Symfony2 Application JavaScript

  5. @michellesanver A simple Symfony2 Application jQuery / Angular.js

  6. @michellesanver A simple Symfony2 Application Custom Files

  7. @michellesanver A simple Symfony2 Application SASS / LESS

  8. @michellesanver A simple Symfony2 Application Minimise for performance

  9. @michellesanver Audience Do you use assetic?

  10. @michellesanver Audience Do you use grunt or gulp?

  11. @michellesanver Audience Do you use bower?

  12. @michellesanver Audience Manually!? (Or not at all)

  13. @michellesanver

  14. @michellesanver

  15. @michellesanver Michelle Sanver

  16. @michellesanver Michelle Sanver Body Text Co-President of PHPWomen

  17. @michellesanver Michelle Sanver Accent(s)!?

  18. @michellesanver Michelle Sanver

  19. @michellesanver Table Of Contents • Assetic - Symfony2 front-end manager

    • Bower - Front-end package manager • Gulp - A modular task runner • Gulp + Bower in Symfony2 • Life After Assetic
  20. @michellesanver Disclaimer There are several good solutions.

  21. @michellesanver Assetic

  22. @michellesanver Life *before* assetic <script src="{{ asset('js/script.js') }}" type=“text/javascript"> </script>

  23. @michellesanver Life *before* assetic Minify manually

  24. @michellesanver Life *before* assetic Compile SASS/LESS The “old” way

  25. @michellesanver Life *before* assetic Or use third party tools, still

    not well integrated.
  26. @michellesanver Assetic An awesome tool for asset management!

  27. @michellesanver Assetic Minify and combine CSS and JS files

  28. @michellesanver Assetic Your files can live anywhere!

  29. @michellesanver Assetic Compilers LESS, SASS, CoffeeScript

  30. @michellesanver Assetic Image Optimisation

  31. @michellesanver Assetic Assets and filters

  32. @michellesanver Assetic: Assets {% javascripts '@AcmeFooBundle/Resources/public/js/*' %} <script type="text/javascript" src="{{

    asset_url }}”></script> {% endjavascripts %}
  33. @michellesanver Assetic: Assets {% stylesheets 'bundles/acme_foo/css/*' filter='cssrewrite' %} <link rel="stylesheet"

    href="{{ asset_url }}" /> {% endstylesheets %}
  34. @michellesanver Assetic: Filters {% stylesheets 'bundles/acme_foo/css/*' filter='cssrewrite' %} <link rel="stylesheet"

    href="{{ asset_url }}" /> {% endstylesheets %}
  35. @michellesanver Assetic: Compilers Define them in the config

  36. @michellesanver Bower

  37. @michellesanver Before Bower Downloading .zip files

  38. @michellesanver Before Bower Dependencies: Trial and error

  39. @michellesanver Bower Bower solved this!

  40. @michellesanver Bower The Composer of JavaScript

  41. @michellesanver Installing Bower npm init

  42. @michellesanver Installing Bower npm install -g bower

  43. @michellesanver Installing Bower: Locally npm install --save-dev bower

  44. @michellesanver Installing Bower: Locally bower command available at ./ node_modules/.bin/bower

  45. @michellesanver Installing Bower: Locally alias npm-exec='PATH=$(npm bin): $PATH'

  46. @michellesanver Using Bower Every project is an installable library

  47. @michellesanver Using Bower bower init

  48. @michellesanver Using Bower bower.init main file

  49. @michellesanver Using Bower bower.init what types of modules does this

    package expose?
  50. @michellesanver Using Bower bower.init set currently installed components as dependencies?

  51. @michellesanver Using Bower bower.init add commonly ignored files to ignore

    list?
  52. @michellesanver Using Bower bower.init would you like to mark this

    package as private which prevents it from being accidentally published to the registry?
  53. @michellesanver Using Bower bower.json

  54. @michellesanver bower.json $ bower install --save jquery bootstrap

  55. @michellesanver bower.json "dependencies": { "jquery": "~2.1.1", "bootstrap": "~3.1.1" }

  56. @michellesanver bower.json bower install

  57. @michellesanver bower.json { "directory": "public/bower_components" }

  58. @michellesanver bower.json rm -Rf bower_components bower install

  59. @michellesanver bower.json /bower_components/<package>/dist/ <files>

  60. @michellesanver bower.json <link href= "/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
 <link href= "/bower_components/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet">


    
 <script type="text/javascript" src="/bower_components/bootstrap/dist/js/bootstrap.min.js">
  61. @michellesanver Updating Libraries bower update bootstrap

  62. @michellesanver No more .zip files \o/

  63. @michellesanver Gulp

  64. @michellesanver Gulp Task Runner

  65. @michellesanver Gulp Gulp vs. Grunt

  66. @michellesanver Gulp vs .Grunt Grunt is config based

  67. @michellesanver Gulp. vs Grunt Gulp is module based

  68. @michellesanver Gulp: Out of the box Task, Watch, Src, Dest.

  69. @michellesanver Gulp: Out of the box gulp.task()

  70. @michellesanver Gulp: Out of the box gulp.watch()

  71. @michellesanver Gulp: Out of the box gulp.src()

  72. @michellesanver Gulp: Out of the box gulp.dest()

  73. @michellesanver Default Task is automatic

  74. @michellesanver Gulp pipe()

  75. @michellesanver Virtual file system and streaming

  76. @michellesanver Virtual file system and streaming vinylfs

  77. @michellesanver Virtual file system and streaming Multi-pipe without temporary files!

  78. @michellesanver Installation is just like bower Gulp

  79. @michellesanver Gulp npm install -g gulp

  80. @michellesanver An example workflow

  81. @michellesanver An example workflow A simple PHP application with generated

    templates
  82. @michellesanver An example workflow • Find all the files being

    used • Minify them • Concat them • Save them • Replace the references in our templates
  83. @michellesanver Plugins gulp-uglifyjs gulp-minify-css gulp-usemin

  84. @michellesanver Installing Plugins npm install --save-dev gulp-usemin gulp-uglify gulp-minify-css

  85. @michellesanver An example workflow gulp.src()

  86. @michellesanver An example workflow uglifyjs and minify-css with concat options

  87. @michellesanver An example workflow gulp.dest()

  88. @michellesanver An example workflow usemin (replace)

  89. @michellesanver Add directives <!-- build:css /css/site.css -->
 <link href="/bower_components/bootstrap/dist/ css/bootstrap.css"

    rel="stylesheet">
 <link href="/bower_components/bootstrap/dist/ css/bootstrap-theme.css" rel="stylesheet">
 <!-- endbuild -->
  90. @michellesanver Add directives <!-- build:css /css/site.css -->
 …
 <!-- endbuild

    --> usemin (replace)
  91. @michellesanver Add directives <!-- build:js /js/site.js -->
 <script type="text/javascript" src=“/

    bower_components/jquery/dist/jquery.js"> </script>
 <script type="text/javascript" src="/ bower_components/bootstrap/dist/js/ bootstrap.js"></script>
 <!-- endbuild -->
  92. @michellesanver Pull in modules var gulp = require('gulp');
 var usemin

    = require('gulp-usemin');
 var uglify = require('gulp-uglify');
 var minifyCss = require('gulp-minify-css');
  93. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  94. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  95. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  96. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  97. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  98. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  99. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  100. @michellesanver Define our default task gulp.task('default', function() {
 gulp.src('src/templates/layout.src.tpl')
 .pipe(usemin({


    assetsDir: 'public',
 css: [minifyCss(), 'concat'],
 js: [uglify(), 'concat']
 }))
 .pipe(gulp.dest('public'));
 });
  101. @michellesanver Running gulp $ gulp [09:11:05] Using gulpfile /path/to/gulpfile.js [09:11:05]

    Starting 'default'... [09:11:07] Finished 'default' after 2.01 s
  102. @michellesanver Let’s dig deeper!

  103. @michellesanver Cleaning up our template fix-template task

  104. @michellesanver Cleaning up our template var rename = require('gulp-rename');
 var

    rimraf = require('del');
 
 gulp.task('fix-template', ['minify'], function() { return gulp.src('public/layout.src.tpl')
 pipe(del())
 pipe(rename("layout.tpl"))
 pipe(gulp.dest('src/templates'));
 });
  105. @michellesanver Task Dependency Rename default to minify

  106. @michellesanver Task Dependency gulp.task('fix-template', ['minify'], function() {

  107. @michellesanver Task Dependency $ gulp fix-template [16:48:29] Using gulp file

    /path/to/gulpfile.js [16:48:29] Starting 'minify'... [16:48:29] Finished 'minify' after 44 ms [16:48:29] Starting 'fix-template'... [16:48:29] Finished 'fix-template' after 6.14 ms
  108. @michellesanver Task Dependency Still doesn’t work

  109. @michellesanver Task Dependency gulp.task('minify', function() {
 return gulp.src('src/templates/layout.src.tpl')

  110. @michellesanver Task Dependency gulp.task('default', ['fix-template']);

  111. @michellesanver Task Dependency gulp.task('default', ['minify', 'fix- template']);

  112. @michellesanver Moving Stuff Bootstrap Fonts

  113. @michellesanver Moving Stuff ../fonts.

  114. @michellesanver Moving Stuff gulp-replace

  115. @michellesanver Moving Stuff var replace = require('gulp-replace');
 
 gulp.task('fix-paths', ['minify'],

    function() {
 gulp.src('public/css/site.css')
 .pipe(replace('../', '../bower_components/ bootstrap/dist/'))
 .pipe(gulp.dest('public/css'));
 });
  116. @michellesanver Moving Stuff Add the task to default

  117. @michellesanver Moving Stuff gulp.task('default', ['minify', 'fix- template', 'fix-paths']);

  118. @michellesanver Automatisation: Watch gulp.watch

  119. @michellesanver Automatisation: Watch gulp.task('watch', ['default'], function() {
 var watchFiles =

    [
 'src/templates/layout.src.tpl',
 'public/bower_components/*/dist/js/*.js',
 '!public/bower_components/*/dist/js/*.min.js',
 'public/bower_components/*/dist/*.js',
 'public/bower_components/*/dist/css/*.css',
 '!public/bower_components/*/dist/css/*.min.css',
 'public/bower_components/*/dist/font/*'
 ];
 
 gulp.watch(watchFiles, ['default']);
 });
  120. @michellesanver Automatisation: Watch $ gulp watch [23:05:01] Using gulpfile /path/to/gulpfile.js

    [23:05:01] Starting 'watch'... [23:05:01] Finished 'watch' after 30 ms
  121. @michellesanver Automatisation: Watch Run on startup

  122. @michellesanver Automatisation: Watch gulp.task('watch', ['default'], function() {

  123. @michellesanver Automating the Automation Too much requires!

  124. @michellesanver Automating the Automation gulp-load-plugins

  125. @michellesanver Automating the Automation $ = require('gulp-load-plugins')();

  126. @michellesanver Automating the Automation $.<plugin>

  127. @michellesanver Automating the Automation gulp-usemin -> $.usemin

  128. @michellesanver Gulp + Bower in Symfony

  129. @michellesanver OmNomHub: A real example

  130. @michellesanver Bower.json in OmNomHub { "name": "OmNomHub", "dependencies": { "bootstrap-sass":

    "~3.0.2" }
  131. @michellesanver Let’s look at our gulp file! Requiring modules

  132. @michellesanver Let’s look at our gulp file! var gulp =

    require('gulp'); var sass = require('gulp-sass'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var cssmin = require('gulp-cssmin');
  133. @michellesanver Let’s look at our gulp file! SASS Task

  134. @michellesanver Let’s look at our gulp file! gulp.task('sass', function() {

    gulp.src('src/Omnomhub/Bundle/ MainBundle/Resources/sass/*.scss') .pipe(sass()) .pipe(cssmin()) .pipe(gulp.dest('web/css/')); });
  135. @michellesanver Let’s look at our gulp file! Move compiled .CSS

    files
  136. @michellesanver Let’s look at our gulp file! .pipe(gulp.dest('web/css/'));

  137. @michellesanver Let’s look at our gulp file! JavaScript

  138. @michellesanver Gulp: JavaScript gulp.task('bootstrapjs', function() { gulp.src([ 'bower_components/bootstrap-sass/js/transition.js', … 'bootstrap-sass/js/affix.js'

    ]) .pipe(concat('bootstrap.js')) .pipe(uglify()) .pipe(gulp.dest('web/js/')); });
  139. @michellesanver Let’s look at our gulp file! List all the

    files
  140. @michellesanver Let’s look at our gulp file! gulp.src([ ‘bower_components/bootstrap-sass/js/transition.js', 'bower_components/bootstrap-sass/js/alert.js',

    'bower_components/bootstrap-sass/js/button.js', 'bower_components/bootstrap-sass/js/carousel.js', … 'bootstrap-sass/js/affix.js' ])
  141. @michellesanver Let’s look at our gulp file! Concatenate them

  142. @michellesanver Let’s look at our gulp file! .pipe(concat('bootstrap.js'))

  143. @michellesanver Let’s look at our gulp file! Uglify them

  144. @michellesanver Let’s look at our gulp file! .pipe(uglify())

  145. @michellesanver Let’s look at our gulp file! Put the result

    in web/js
  146. @michellesanver Let’s look at our gulp file! .pipe(gulp.dest('web/js/'));

  147. @michellesanver Let’s look at our gulp file! When you run

    “gulp” gulp.task('default', ['sass', 'bootstrapjs']);
  148. Life After Assetic @michellesanver

  149. @michellesanver Cleaner Twig No more tasks in the twig files

  150. @michellesanver Faster workflow Node.js tools == asynchronous

  151. @michellesanver More features Gulp and Node.JS have a lot of

    possibilities and modules.
  152. @michellesanver No more manual .ZIP downloads Can’t believe it took

    this long.
  153. @michellesanver Life After Assetic Third party assetic bundles?

  154. @michellesanver Life After Assetic Will they use bower?

  155. @michellesanver Life After Assetic Other bundles are popping up!

  156. @michellesanver Life After Assetic Gassetic

  157. Limitations and Wrapup @michellesanver

  158. Assetic is dead @michellesanver Or at least is should be…

  159. @michellesanver Thank you for listening! https://joind.in/12950

  160. @michellesanver @frankdejonge is still not famous.

  161. @michellesanver Bonus

  162. @michellesanver BowerPHP

  163. @michellesanver BowerPHP Alpha!

  164. @michellesanver BowerPHP http://bowerphp.org