$30 off During Our Annual Pro Sale. View Details »

Deal with Laravel assets by Bower & Gulp

Deal with Laravel assets by Bower & Gulp

PHP 也有 Day #11

大澤木小鐵

October 16, 2014
Tweet

More Decks by 大澤木小鐵

Other Decks in Programming

Transcript

  1. DEAL WITH LARAVEL ASSETS
    BY BOWER & GULP
    Jace Ju @ KKBOX

    View Slide

  2. What problems in Laravel 4 assets?

    View Slide

  3. No Built-in assets manager

    View Slide

  4. No Dependency management

    View Slide

  5. Twitter Bootstrap -> jQuery
    Backbone.js -> underscore.js

    View Slide

  6. No Assets pre-compile & minify

    View Slide

  7. No Changes watching &
    Live reload

    View Slide

  8. No Packing & Revision

    View Slide

  9. Where is better
    solution?

    View Slide

  10. Bower + Gulp !!

    View Slide

  11. Bower
    A package manager for the web
    http://bower.io/

    View Slide

  12. Gulp
    The streaming build system
    http://gulpjs.com/

    View Slide

  13. Quick Demo
    http://y2u.be/iTkM1LJcLsU

    View Slide

  14. View Slide

  15. How to do this?

    View Slide

  16. Install IO.JS
    (by Platform)

    View Slide

  17. Install Bower & Gulp
    $ npm install -g bower gulp
    /usr/local/bin/gulp -> /usr/local/lib/node_modules/gulp/bin/gulp.js
    /usr/local/bin/bower -> /usr/local/lib/node_modules/bower/bin/bower
    ...

    View Slide

  18. Create Laravel Project (4.2)
    $ laravel new project-name
    Crafting application...
    Application ready! Build something amazing.

    View Slide

  19. Create custom assets

    View Slide

  20. Create assets folders
    $ cd project-name
    $ mkdir -p assets/{scripts,styles,images,fonts}
    project-name
    ├── ... (laravel folders)
    └── assets
    ├── scripts
    ├── styles
    ├── images
    └── fonts

    View Slide

  21. Create custom assets
    $ vi assets/styles/main.scss
    $ vi assets/scripts/index.js

    View Slide

  22. Add bower support

    View Slide

  23. Create bower.json
    $ bower init
    ? name: project-name
    ? version: 0.0.0
    ? description:
    ? main file:
    ? what types of modules does this package expose?:
    ? keywords:
    ...
    ? Looks good?: Yes

    View Slide

  24. bower.json
    $ cat bower.json
    {
    name: 'project-name',
    version: '0.0.0',
    authors: [
    'Author '
    ],
    license: 'MIT',
    private: true,
    ignore: [
    ...
    ]
    }

    View Slide

  25. Create .bowerrc
    $ echo "/public/bower_components" >> .gitignore
    $ vi .bowerrc
    {
    "directory": "public/bower_components"
    }

    View Slide

  26. Install package
    $ bower install --save bootstrap
    bower cached git://github.com/twbs/bootstrap.git#3.3.1
    bower validate 3.3.1 against git://github.com/twbs/bootstrap.git#*
    bower cached git://github.com/jquery/jquery.git#2.1.3
    ...
    bootstrap#3.3.1 bower_components/bootstrap
    └── jquery#2.1.3
    jquery#2.1.3 bower_components/jquery

    View Slide

  27. Inject assets

    View Slide

  28. Create blade template
    $ mv app/views app/templates
    $ echo "/app/views" >> .gitignore
    $ vi app/templates/index.blade.php

    View Slide

  29. Styles








    View Slide

  30. Scripts








    View Slide

  31. Rewrite route
    $ vi app/routes.php
    Route::get('/', function()
    {
    // render app/views/index.blade.php
    return View::make('index');
    });

    View Slide

  32. Node modules for tasks

    View Slide

  33. Create package.json
    $ vi package.json
    {
    "name": "project-name",
    "private": true,
    "devDependencies": {
    },
    "engines": {
    "node": ">=0.10.0"
    }
    }

    View Slide

  34. Install node modules
    $ echo "/node_modules" >> .gitignore
    $ npm install --save-dev

    View Slide

  35. Task tools
    gulp (3.8.10) The streaming build system
    gulp-load-plugins (0.8.0) Automatically load any gulp plugins
    in your package.json
    run-sequence (1.0.2) Run a series of dependent
    gulp tasks in order
    gulp-if (1.2.5) Conditionally run a task
    merge-stream (0.1.6) Create a stream that emits events
    from multiple other streams

    View Slide

  36. File tools
    gulp-flatten (0.0.4) remove or replace relative path for files
    gulp-replace (0.5.0) A string replace plugin for gulp
    del (1.1.1) Delete files/folders using globs

    View Slide

  37. Style tools
    gulp-ruby-sass (0.7.1) Compile Sass to CSS with Ruby Sass
    gulp-autoprefixer (1.0.1) Prefix CSS
    gulp-csso (0.2.9) Minify CSS with CSSO.

    View Slide

  38. Script tools
    gulp-jshint (1.9.0) JSHint plugin for gulp
    jshint-stylish (1.0.0) Stylish reporter for JSHint
    gulp-uglify (1.0.2) Minify files with UglifyJS.
    uglify-save-license (0.4.1) License detector for UglifyJS

    View Slide

  39. Image tools
    gulp-cache (0.1.11) A cache proxy task for Gulp
    gulp-imagemin (2.0.0) Minify PNG, JPEG, GIF and SVG images

    View Slide

  40. Server tools
    gulp-livereload (3.0.2) Gulp plugin for livereload.

    View Slide

  41. Assets tools
    gulp-useref (1.0.2) Parse build blocks in HTML files
    to replace references to non-optimized
    scripts or stylesheets.
    gulp-rev (2.0.1) Static asset revisioning by appending
    content hash to filenames
    gulp-rev-replace (0.3.1) Rewrite occurences of filenames
    which have been renamed by gulp-rev
    wiredep (2.2.2) Wire Bower dependencies to your source code.

    View Slide

  42. Add gulp support

    View Slide

  43. Create gulpfile.js
    $ vi gulpfile.js
    'use strict';
    // Load gulp and plug-ins
    var gulp = require('gulp');
    var del = require('del');
    var path = require('path');
    var runSequence = require('run-sequence');
    var $ = require('gulp-load-plugins')();

    View Slide

  44. Common tasks for
    assets

    View Slide

  45. Inject bower components
    gulp.task('wiredep', function() {
    var wiredep = require('wiredep').stream;
    return gulp.src('app/templates/**/*.blade.php')
    .pipe(wiredep({
    ignorePath: '../../public/'
    }))
    .pipe(gulp.dest('app/views'));
    });

    View Slide

  46. Before




    After






    View Slide

  47. Pre-compile Sass files
    assets/styles/*.scss ! public/styles/*.css
    $ echo "/.sass-cache" >> .gitignore
    gulp.task('styles', function() {
    return gulp.src('assets/styles/*.scss')
    .pipe($.rubySass({ ... }))
    .pipe($.autoprefixer({ ... }))
    .pipe(gulp.dest('public/styles'));
    });

    View Slide

  48. Check syntax and copy files
    assets/scripts/*.js ! public/scripts/*.js
    gulp.task('scripts', function() {
    return gulp.src('./assets/scripts/*.js')
    .pipe($.jshint('.jshintrc'))
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe(gulp.dest('public/scripts'));
    });

    View Slide

  49. .jshintrc
    $ vi .jshintrc
    {
    "node": true,
    "esnext": true,
    "bitwise": true,
    ...
    "trailing": true,
    "smarttabs": true,
    "white": true
    }
    http://jshint.com/docs/

    View Slide

  50. Optimize images
    assets/images/**/* ! public/images/
    gulp.task('images', function() {
    return gulp.src('assets/images/**/*')
    .pipe($.cache($.imagemin({ ... })))
    .pipe(gulp.dest('public/images'));
    });

    View Slide

  51. Copy fonts
    assets/fonts/**/*.* ! public/fonts/
    // Fonts
    gulp.task('fonts', function () {
    return gulp.src([
    'assets/fonts/*.{otf,eot,svg,ttf,woff}',
    'public/bower_components/**/fonts/**/*.{otf,eot,svg,ttf,woff}'
    ])
    .pipe($.flatten())
    .pipe(gulp.dest('public/fonts'));
    });

    View Slide

  52. Tasks for
    development

    View Slide

  53. Clean temporary files
    // Clean
    gulp.task('clean:develop', function(cb) {
    del([
    'app/views',
    'public/styles',
    'public/scripts',
    '.sass-cache'], cb);
    });

    View Slide

  54. Prepare for development
    gulp.task('prepare', function (cb) {
    runSequence(
    'clean:develop',
    ['wiredep', 'styles', 'scripts', 'images', 'fonts'],
    cb);
    });

    View Slide

  55. Start php built-in web server
    gulp.task('serve', function () {
    var spawn = require('child_process').spawn,
    child = spawn('php', [ 'artisan', 'serve' ], { cwd: process.cwd() }),
    log = function (data) { console.log(data.toString()) },
    kill = function () { child.kill(); }
    child.stdout.on('data', log);
    child.stderr.on('data', log);
    process.on('exit', kill);
    process.on('uncaughtException', kill);
    });

    View Slide

  56. Livereload when destination be changed
    gulp.task('livereload', function () {
    var server = $.livereload;
    server.listen();
    gulp.watch([
    'app/views/**/*',
    'public/**/*',
    '!public/bower_components/**/*',
    ]).on('change', server.changed);
    });

    View Slide

  57. Watching changes of source
    gulp.task('watch', ['prepare'], function() {
    gulp.start('serve');
    gulp.start('livereload');
    gulp.watch('app/templates/**/*.php', ['wiredep']);
    gulp.watch('assets/styles/**/*.scss', ['styles']);
    gulp.watch('assets/scripts/**/*.js', ['scripts']);
    gulp.watch('assets/images/**/*', ['images']);
    });

    View Slide

  58. Build tasks

    View Slide

  59. Minify & Packing & Revision
    gulp.task('build', ['wiredep', 'styles', 'scripts', 'images', 'fonts'], function() {
    var assets = $.useref.assets({ searchPath: 'public' });
    return gulp.src('app/views/**/*.blade.php')
    .pipe(assets)
    .pipe($.if('*.js', $.uglify()))
    .pipe($.if('*.css', $.csso()))
    .pipe($.rev())
    .pipe(gulp.dest('public'))
    .pipe(assets.restore())
    .pipe($.useref())
    .pipe($.revReplace({
    replaceInExtensions: ['.php']
    }))
    .pipe(gulp.dest('app/views'));
    });

    View Slide

  60. Packing
    Before






    After

    View Slide

  61. Revision
    Before

    After

    View Slide

  62. Default build
    gulp.task('clean', ['clean:develop', 'clean:cache'], function(cb) {
    del(['public/css', 'public/js', 'public/fonts', 'public/images'], cb);
    });
    gulp.task('clean:temporary', function (cb) {
    del([
    'app/views/js', 'app/views/css',
    'public/scripts', 'public/styles', '.sass-cache'
    ], cb);
    });
    gulp.task('default', function(cb) {
    runSequence('clean', 'build', 'clean:temporary', cb);
    });

    View Slide

  63. Ignore packages folder and compiled files
    $ vi .gitignore
    ...
    /app/views
    /public/bower_components
    /node_modules
    /public/styles
    /public/scripts
    /public/css
    /public/js
    /public/fonts
    /public/images
    /.sass-cache
    /.bundle
    Gemfile.lock

    View Slide

  64. Laravel 4 Skeleton with Gulp
    # Save your time
    composer create-project jaceju/laragulp project-name -s dev

    View Slide

  65. Laravel Elixir
    Beautiful Gulp tasks for your Laravel applications.
    https://github.com/laravel/elixir

    View Slide

  66. Laracast
    http://goo.gl/gdNrcc

    View Slide

  67. gulpfile.js
    elixir(function(mix) {
    mix.less('*.less')
    .stylesIn("public/css")
    .version('css/all.css');
    });

    View Slide

  68. In blade template

    from public/build/rev-manifest.json
    {
    "css/all.css": "css/all-602a38ff.css"
    }

    View Slide

  69. Summary

    View Slide

  70. • Automate all what machine can handle.
    • Separate develop flow and deploy flow.
    • DDIY, use third-party tools wisely and well.
    • This should be first step of your workflow.

    View Slide

  71. No best practice,
    only better solution.

    View Slide

  72. Q & A

    View Slide