Tối ưu hóa các file CSS và JavaScript
Các lập trình viên có 2 task cần thực hiện khi cố gắng tối ưu hóa các file CSS và JavaScript cho phiên bản production: nối file và minification.
Một vấn đề các lập trình viên phải đối mặt là khó có thể nối các file js theo thứ tự chính xác.
Ví dụ chúng ta có 3 thẻ script trong file index.html.
<body> <!-- other stuff --> <script src="js/lib/a-library.js"></script> <script src="js/lib/another-library.js"></script> <script src="js/main.js"></script> </body>
Các script này nằm trong 2 thư mục khác nhau. Rất khó để nối chúng với những plugin kiểu như gulp-concatenate.
Thật may mắn, chúng ta có thể sử dụng gulp-useref để giải quyết vấn đề này.
Gulp-useref nối tất cả file CSS và JavaScript thành một file bằng cách tìm kiếm một comment bắt đầu với “<!–build:” và kết thúc với “<!– endbuild –>”. Giống như thế này:
<!-- build:<type> <path> --> ... HTML Markup, list of script / link tags. <!-- endbuild -->
<type> có thể là js, css hoặc remove. Thường thì type sẽ là kiểu file chúng ta muốn nối. Nếu bạn thiết lập type là removeGulp sẽ xóa toàn bộ block mà không phát sinh một file nào.
<path> là đường dẫn tới file đã được nối.
Ở đây, chúng ta muốn file JavaScript đã được nối sẽ nằm trong thư mục js và có tên là main.min.js. Cú pháp sẽ như sau:
<!--build:js js/main.min.js --> <script src="js/lib/a-library.js"></script> <script src="js/lib/another-library.js"></script> <script src="js/main.js"></script> <!-- endbuild -->
Bây giờ hãy cấu hình gulp-useref plugin trong gulpfile. Chúng sẽ cần cài đặt và require trước.
$ npm install gulp-useref --save-dev
var useref = require('gulp-useref');
Thiết lập task useref tương tự như các task chúng ta đã làm:
gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) .pipe(gulp.dest('dist')) });
Bây giờ nếu bạn chạy task useref, Gulp sẽ đọc các file trong 3 thẻ script và nối chúng thành thành một file main.min.jstrong thư mục dist/js
Tuy nhiên file kết quả chưa được minify. Chúng ta sẽ sử dụng gulp-uglify plugin để minifying các file JavaScript. Chúng ta cũng cần một plugin thứ 2 gọi là gulp-if để đảm bảo chỉ minify các file JavaScript.
npm install gulp-uglify --save-dev
// Other requires... var uglify = require('gulp-uglify'); var gulpIf = require('gulp-if'); gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) // Minifies only if it's a JavaScript file .pipe(gulpIf('*.js', uglify())) .pipe(gulp.dest('dist')) });
Gulp sẽ tự động minify “main.min.js” bất cứ khi nào bạn chạy useref task.
Gulp-useref cũng sẽ tự động thay thế tất cả các thẻ script trong “<!– build:” và “<!– endbuild –> thành một thẻ duy nhất trỏ tới “js/main.min.js”.
Chúng ta có thể sử dụng phương thức tương tự để nối các file CSS (nếu bạn có nhiều hơn một file). Chúng ta sẽ làm theo tiến trình tương tự như nối các file js và thêm một build comment.
<!--build:css css/styles.min.css--> <link rel="stylesheet" href="css/styles.css"> <link rel="stylesheet" href="css/another-stylesheet.css"> <!--endbuild-->
Để minify file CSS đã được nối. Chúng ta cần sử dụng package gulp-cssnano.
$ npm install gulp-cssnano
var cssnano = require('gulp-cssnano'); gulp.task('useref', function(){ return gulp.src('app/*.html') .pipe(useref()) .pipe(gulpIf('*.js', uglify())) // Minifies only if it's a CSS file .pipe(gulpIf('*.css', cssnano())) .pipe(gulp.dest('dist')) });
Bây giờ chúng ta đã tối ưu hóa các file CSS và JavaScript bất kì khi nào bạn chạy useref task.
Tiếp theo hãy tối ưu hình ảnh
Tối ưu hóa ảnh
Chúng ta sẽ cần sử dụng gulp-imagemin để tối ưu hóa hình ảnh.
$ npm install gulp-imagemin --save-dev
var imagemin = require('gulp-imagemin');
Chúng ta có thể tối ưu hóa ảnh png, jpg, gif và thậm chí là svg với gulp-imagemin. Hãy tạo một images task.
gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|gif|svg)') .pipe(imagemin()) .pipe(gulp.dest('dist/images')) });
Các kiểu file khác nhau có thể tối ưu hóa theo nhiều cách, bạn có thể thêm tùy chọn imagemin để tối ưu từng loại file.
Ví dụ, bạn có thể tạo interlaced GIFs bằng cách thiết lập tùy chọn interlaced là true.
gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)') .pipe(imagemin({ // Setting interlaced to true interlaced: true })) .pipe(gulp.dest('dist/images')) });
Bạn có thể thử nghiệm các tùy chọn khác nếu muốn.
Tuy nhiên tối ưu hóa các hình ảnh, là một tiến trình cực kỳ chậm bạn sẽ không muốn lặp lại trừ khi cần thiết. Để làm điều này chúng ta có thể sử dụng gulp-cache plugin.
$ npm install gulp-cache --save-dev
var cache = require('gulp-cache'); gulp.task('images', function(){ return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)') // Caching images that ran through imagemin .pipe(cache(imagemin({ interlaced: true }))) .pipe(gulp.dest('dist/images')) });
Chúng ta đã gần hoàn thành quá trình tối ưu hóa. Chỉ còn một thư mục chúng ta cần chuyển từ thư mục “app” tới thư mục “dist”, đó là thư mục fonts. Hãy làm điều đó ngay.
Copy Fonts tới Dist
Các file font đã được tối ưu hóa, vì vậy chúng ta không cần làm gì thêm. Tất cả những gì chúng ta cần làm là copy các font tới thư mục dist.
Chúng ta có thể copy các file với Gulp đơn giản bằng cách sử dụng gulp.src và gulp.dest mà không cần plugin nào.
gulp.task('fonts', function() { return gulp.src('app/fonts/**/*') .pipe(gulp.dest('dist/fonts')) });
Bây giờ Gulp sẽ copy các font từ thư mục “app” tới “dist” bất kỳ khi nào bạn chạy gulp fonts.
Chúng ta đã có 6 task khác nhau trong gulpfile, và mỗi task được gọi riêng rẽ trong command line, điều này khá phiền phức và chúng ta muốn gộp tất cả với trong một lệnh.
Trước khi làm điều này, chúng ta hãy xem cách xóa các file đã phát sinh một cách tự động.
Tự động xóa các file đã tạo ra
Khi tạo ra các file một cách tự động, chúng ta sẽ muốn đảm bảo rằng các file không còn sử dụng sẽ không tồn tại ở bất kỳ đâu mà chúng ta không biết.
Quá trình được gọi làm sạch (cleaning) (hay đơn giản là xóa các file)
Chúng ta sử dụng del để làm điều này.
$ npm install del --save-dev
var del = require('del');
Hàm del nhận một mảng node globs cái nói với nó thư mục nào cần xóa.
Thiết lập giống như hello task trong ví dụ đầu tiên.
gulp.task('clean:dist', function() { return del.sync('dist'); });
Bây giờ Gulp sẽ xóa thư mục “dist” bất kỳ khi nào lệnh gulp clean:dist chạy.
Chú ý: Chúng ta không cần lo lắng về việc xóa thư mục dist/images bởi vì gulp-cache đã lưu ảnh trên hệ thống local của bạn.
Để xóa cache trên hệ thống local, bạn có thể tạo một task tên là “cache:clear”.
gulp.task('cache:clear', function (callback) { return cache.clearAll(callback); });
Bây giờ hãy kết hợp các task với nhau!
Kết hợp các Gulp tasks
Hãy tổng kết cái chúng ta đã làm. Chúng ta đã tạo ra 2 tập hợp các Gulp task.
Đầu tiên là cho quá trình phát triển, chúng ta biên dịch Sass thành CSS, theo dõi sự thay đổi và reload lại trình duyệt khi có sự thay đổi.
Thứ 2 là quá trình tối ưu, nơi chúng ta chuẩn bị các file cho một website hoàn chỉnh. Chúng ta tối ưu hóa CSS, JavaScript, và hình ảnh trong tiến trình này và copy các font từ thư mục app tới thư mục dist.
Chúng ta đã gộp tập hợp các task đầu tiên vào một một lệnh gulp watch:
gulp.task('watch', ['browserSync', 'sass'], function (){ // ... watchers });
Tập hợp thứ 2 gồm các task chúng ta cần chạy để tạo ra một website hoàn chỉnh. Nó gồm: clean:dist, sass, useref, images và fonts. Chúng ta sẽ tạo ra một task build để kết hợp mọi thứ cùng nhau.
gulp.task('build', [`clean`, `sass`, `useref`, `images`, `fonts`], function (){ console.log('Building files'); });
Thật không may, chúng ta không thể viết task build theo cách này bởi vì Gulp chạy tất cả các task trong tham số thứ 2 đồng thời.
Nghĩa là useref, images, hoặc thậm chí là fonts sẽ hoàn thành trước khi clean kết thúc, và toàn bộ thư mục dist sẽ bị xóa.
Vì vậy, cần đảm bảo clean hoàn thành trước các task còn lại, chúng ta cần sử dụng một plugin là run-sequence.
$ npm install run-sequence --save-dev
Đây là cú pháp để chạy các task tuần tự
var runSequence = require('run-sequence'); gulp.task('task-name', function(callback) { runSequence('task-one', 'task-two', 'task-three', callback); });
Khi task-name được gọi, Gulp sẽ chạy task-one đầu tiên, Khi task-one kết thúc, Gulp sẽ tự động bắt đầu task-two. Cuối cùng, khi task-two hoàn thành, Gulp sẽ chạy task-three.
run-sequence cũng cho phép chạy các task đồng thời nếu bạn đặt chúng trong một mảng:
gulp.task('task-name', function(callback) { runSequence('task-one', ['tasks','two','run','in','parallel'], 'task-three', callback); });
Trong trường hợp này, Gulp sẽ chạy task-one. Khi task-one hoàn thành, Gulp sẽ chạy tất cả các task còn lại trong mảng tham số thứ 2 đồng thời. Tất cả các task trong tham số thứ 2 phải hoàn thành trước khi task-three có thể chạy.
Bây giờ chúng ta sẽ tạo ra một task cái đảm bảo clean:dist chạy đầu tiên, sau đó là các task khác:
gulp.task('build', function (callback) { runSequence( 'clean:dist', ['sass', 'useref', 'images', 'fonts'], callback ) });
Để tạo sự thống nhất, chúng ta có thể xây dựng một chuỗi tuần tự với group đầu tiên. Lần này hãy sử dụng default cho tên task:
gulp.task('default', function (callback) { runSequence(['sass','browserSync'], 'watch', callback ) });
Tại sao lại là default? Bởi vì khi bạn có một task tên là default, bạn có thể chạy nó đơn giản bằng cách nhập lệnh gulptrong command line.
Cuối cùng, đây là github repo cho tất cả những thứ chúng ta đã làm!
Kết luận
Chúng ta đã học những thứ cơ bản nhất về Gulp và tạo một workflow, có khả năng biên dịch Sass thành CSS trong khi theo dõi sự thay đổi các file HTML và JS tại cùng thời điểm. Chúng ta có thể chạy task này với lệnh gulp trong command line.
Chúng ta cũng xây dựng một task thứ hai, build, cái tạo ra thư mục dist cho phiên bản production. Chúng ta biên dịch Sass thành CSS, tối ưu hóa tất cả các tài nguyên, và copy các thư mục cần thiết vào thư mục dist. Để chạy task này chúng ta chỉ cần nhập gulp build trong command line.
Cuối cùng, chúng ta có một clean task cái xóa thư mục dist.
Chúng ta đã tạo ra một workflow có đủ khả năng đáp ứng cho hầu hết các web develper. Còn rất nhiều thứ về Gulp và workflow mà chúng ta có thể khám phá để làm cho quá trình này tốt hơn. Đây là một số gợi ý cho bạn:
Cho phát triển:
- Sử dụng Autoprefixer để tự động thêm prefix vào code CSS
- Thêm Sourcemaps để dễ dàng debug hơn
- Tạo ra Sprites với sprity
- Biên dịch chỉ các file có thay đổi với gulp-changed
- Viết ES6 với Babel hoặc Traceur
- Modular hóa các file JavaScript với Browserify, webpack, hoặc jspm
- Modular hóa HTML với các template engine như Handlebars hoặc Swig
- Chia gulpfile thành các file nhỏ hơn với require-dir
- Phát sinh một Modernizr script tự động với gulp-modernizr
Cho tối ưu hóa:
Ngoài các quá trình phát triển và tối ưu hóa, bạn cũng có thể viết unit tests với gulp-jasmine và thậm chí triển khai thư mục dist tới productions server tự động với gulp-rync.
Nguồn: TechMaster