Gulp: Vũ khí bí mật của một web developer (P.2)

Tiếp nối phần 1 cũng là phần cuối cùng trong series Gulp: Vũ khí bí mật của một web developer, chúng tôi sẽ hướng dẫn bạn về Optimizing Built Assets và Cache Busting.
Optimizing Built Assets
Bạn sẽ cần 2 plugin mới. Để thêm vào, bạn sẽ chạy lệnh sau:
npm install --save-dev gulp-clean-css gulp-minify
Plugin đầu tiên sẽ rút gọn CSS, sử dụng clean-css package và plugin thứ hai sẽ rút gọn JavaScript, sử dụng UglifyJS2 package. Đầu tiên, chúng ta sẽ load 2 package này gulpfile.js:
var minify = require('gulp-minify');
var cleanCss = require('gulp-clean-css');
Sau đó sử dụng khi thao tác trước khi viết output:
.pipe(minify())
.pipe(cleanCss())
gulpfile.js sẽ như thế này:
var gulp = require('gulp');
var concat = require('gulp-concat');
var minify = require('gulp-minify');
var cleanCss = require('gulp-clean-css');
 
gulp.task('pack-js', function () { 
 return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js'])
  .pipe(concat('bundle.js'))
  .pipe(minify())
  .pipe(gulp.dest('public/build/js'));
});
 
gulp.task('pack-css', function () { 
 return gulp.src(['assets/css/main.css', 'assets/css/custom.css'])
  .pipe(concat('stylesheet.css'))
  .pipe(cleanCss())
   .pipe(gulp.dest('public/build/css'));
});
 
gulp.task('default', ['pack-js', 'pack-css']);
Hãy chạy gulp một lần nữa. Bạn sẽ thấy rằng tập tin stylesheet.css được lưu ở định dạng rút gọn, và file bundle.js vẫn còn lưu như cũ. Lúc này, bạn cũng có bundle-min.js đã được lược giản. Bạn chỉ cần file rút gọn và lưu dưới định dạng bundle.js, vì vậy bạn sẽ modify code với các thông số sau:
.pipe(minify({
 ext:{
  min:'.js'
 },
 noSource: true
}))
Theo tài liệu gulp-minify plugin (https://www.npmjs.com/package/gulp-minify), nó sẽ đặt tên theo mong muốn cho các phiên bản rút gọn, và lệnh cho các plugin không tạo ra các phiên bản chứa original source. Nếu bạn xóa các nội dung của build directory và chạy gulp từ command line một lần nữa thì bạn sẽ kết thúc chỉ với hai tập tin được rút gọn. Bạn đã vừa triển khai xong minification phase của build process.
Cache Busting
Tiếp theo, chúng ta sẽ thêm cache busting và sẽ cần phải cài đặt một plugin như sau:
npm install --save-dev gulp-rev 
Require trong gulp file:
var rev = require('gulp-rev');
Sử dụng các plugin cần phải có sự khéo léo. Đầu tiên, chúng ta phải dẫn minified output thông qua plugin. Sau đó, bạn cần phải lệnh các plugin một lần nữa sau khi đã ghi các kết quả vào đĩa. Plugin đổi tên các file với  hash tag độc đáo, và nó cũng tạo ra manifest file. Manifest file là map được sử dụng bởi application để xác định filename mới nhất trong HTML code. Sau khi modify gulp file, dòng code kết thúc sẽ hiển thị như thế này:
var gulp = require('gulp');
var concat = require('gulp-concat');
var minify = require('gulp-minify');
var cleanCss = require('gulp-clean-css');
var rev = require('gulp-rev');
 
gulp.task('pack-js', function () { 
    return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js'])
        .pipe(concat('bundle.js'))
        .pipe(minify({
            ext:{
                min:'.js'
            },
            noSource: true
        }))
        .pipe(rev())
        .pipe(gulp.dest('public/build/js'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('public/build'));
});
 
gulp.task('pack-css', function () {
    return gulp.src(['assets/css/main.css', 'assets/css/custom.css'])
        .pipe(concat('stylesheet.css'))
        .pipe(cleanCss())
        .pipe(rev())
            .pipe(gulp.dest('public/build/css'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('public/build'));
});
 
gulp.task('default', ['pack-js', 'pack-css']);
Hãy xóa các nội dung của build directory và chạy gulp một lần nữa. Lúc này, chúng ta sẽ thấy có hai file với hash tags với mỗi filename, và manifest.json đã lưu vào public/build. Nếu mở manifest file, bạn sẽ thấy rằng nó chỉ có một reference đến một trong các minified và tagged file. Mỗi task viết một manifest file riêng biệt, và một trong số nó sẽ kết thúc việc ghi đè.
Bạn cần phải modify task với các thông số bổ sung để tìm kiếm các manifest file đang tồn tại, và để merge new data vào đó. Hơi phức tạp một chút, nên bạn có thể xem đoạn code bên dưới:
var gulp = require('gulp');
var concat = require('gulp-concat');
var minify = require('gulp-minify');
var cleanCss = require('gulp-clean-css');
var rev = require('gulp-rev');
 
gulp.task('pack-js', function () {
    return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js'])
        .pipe(concat('bundle.js'))
        .pipe(minify({
            ext:{
                min:'.js'
            },
            noSource: true
        }))
        .pipe(rev())
        .pipe(gulp.dest('public/build/js'))
        .pipe(rev.manifest('public/build/rev-manifest.json', {
            merge: true
        }))
        .pipe(gulp.dest(''));
    });
 
gulp.task('pack-css', function () { 
    return gulp.src(['assets/css/main.css', 'assets/css/custom.css'])
        .pipe(concat('stylesheet.css'))
        .pipe(cleanCss())
        .pipe(rev())
        .pipe(gulp.dest('public/build/css'))
        .pipe(rev.manifest('public/build/rev-manifest.json', {
            merge: true
        }))
        .pipe(gulp.dest(''));
});
 
gulp.task('default', ['pack-js', 'pack-css']);
Đầu tiên, chúng ta dẫn output đến rev.manifest (). Điều này tạo ra các tagged file đã có trước đó. Bạn đang cung cấp desired path của rev-manifest.json, và buộc rev.manifest () phải merge các file hiện có (nếu nó tồn tại). Sau đó, gulp nhận lệnh để viết manifest vào directory, tại thời public/build. Path issue là do bug và sẽ được mô tả chi tiết hơn trên GitHub.
Lúc này, bạn đã có automated minification, tagged file và manifest file. Tất cả những điều này sẽ cho phép bạn deliver file một cách nhanh chóng hơn cho người dùng, và cache busting sẽ xuất hiện bất cứ khi nào chúng ta thực hiện những sửa đổi (modifications). Tuy nhiên, chúng ta vẫn còn hai vấn đề.
Vấn đề đầu tiên là nếu chúng ta thực hiện bất kỳ sửa đổi nào trong các source file, tagged file mới sẽ được tạo ra, nhưng những tagged file cũ vẫn còn ở đó. Vì vậy, chúng ta cần một số cách để xóa tự động các minified file. Hãy giải quyết vấn đề này bằng cách sử dụng plugin cho phép xóa file:
npm install --save-dev del
Require trong đoạn code và xác định hai task mới, mỗi task phân định cho từng loại source file:
var del = require('del');
 
gulp.task('clean-js', function () {
 return del([
  'public/build/js/*.js'
 ]);
});
 
gulp.task('clean-css', function () {
 return del([
  'public/build/css/*.css'
 ]);
});
Sau đó, hãy đảm bảo các task đã chạy xong trước khi có 2 task chính:
gulp.task('pack-js', ['clean-js'], function () {
gulp.task('pack-css', ['clean-css'], function () {
Nếu bạn chạy gulp một lần nữa sau sửa đổi này, bạn sẽ chỉ có các minified file mới nhất.
Vấn đề thứ hai là chúng ta không muốn cứ phải tiếp tục chạy gulp mỗi khi có sự thay đổi. Để giải quyết điều này, bạn sẽ cần phải xác định watcher task:
gulp.task('watch', function() {
 gulp.watch('assets/js/**/*.js', ['pack-js']);
 gulp.watch('assets/css/**/*.css', ['pack-css']);
});
Thay đổi definition của default task:
gulp.task('default', ['watch']);
Nếu bây giờ bạn chạy gulp từ command line thì nó sẽ không còn hoạt động nữa. Lý do bởi vì gulp đã lệnh cho watcher task theo dõi các source file khi có bất kỳ thay đổi nào, và chỉ build khi nó phát hiện một sự thay đổi. Nếu bạn cố gắng thay đổi source file và xem lại console một lần nữa, bạn sẽ thấy task ack-js và pack-cs chạy tự động cùng với dependency.
Bây giờ, tất cả chúng ta phải làm là load flie manifest.json trong application và nhận tagged filename từ đó. Cách mà bạn làm phụ thuộc vào ngôn ngữ back-end và technology stack. Tuy nhiên, bạn có thể load manifest thành array hoặc object. Sau đó xác định helper function để lệnh cho versioned assets từ manner template:
gulp(‘bundle.js’)
Một khi làm được điều đó, bạn sẽ không phải lo lắng về sự thay đổi tagged filename nữa. Từ đó, bạn sẽ có thể tập trung vào viết code chất lượng hơn.
Kết luận
Hy vọng rằng điều này sẽ giúp bạn phát triển các build process trong các application của riêng mình.
Hãy nhớ rằng Gulp chỉ là một trong những công cụ có thể được sử dụng cho mục đích này, và vẫn có rất nhiều công cụ khác như Grunt, Browserify, và Webpack. Chúng khác nhau về mục đích và phạm vi của vấn đề mà chúng có thể giải quyết. Trong số đó có thể giải quyết các vấn đề mà Gulp không làm được, chẳng hạn như bundling JavaScript module with dependency loaded on demand.

No comments:

Post a Comment

The Ultimate XP Project

  (Bài chia sẻ của tác giả  Ryo Amano ) Trong  bài viết  số này, tôi muốn viết về dự án phát triển phần mềm có áp dụng nguyên tắc phát triển...