Front-end xoay quanh

Gulp getting started!

Gulp getting started!

1. Mở đầu

Chúng ta có thể hiểu GULP là một tool giúp cho việc phát triển Web app trở lên dễ dàng hơn bằng cách giúp bên phần Front-end một số việc như là:

  • Tạo ra một Server ảo để chúng ta phát triển app trên đó.
  • Tự động reload lại Server ảo này mỗi khi chúng ta changed một file và Save nó lại.
  • Giúp cho chúng ta có thể dùng Preprocessors CSS như là SASS hay LESS.
  • Optimize lại phần CSS, JS hay thậm chí là cả ảnh nữa.

Có bạn lại đặt câu hỏi: Ơ nhưng mà nếu mình tạo app bằng CLI của framework như Angular-cli, Vue-cli, … nó cũng tự làm 4 cái kia rồi mà sao lại phải dùng GULP?

Mình xin trả lời là đúng là nếu dùng lệnh CLI thì chắc không cần config Gulp đâu. Nhưng nếu các bạn không dùng CLI mà lại tự tạo bằng tay, hay dự án cũ không dùng framework mà lại muốn có cảm giác như Framework thì hãy dùng thêm GULP thôi. Nó sẽ là trợ thủ đắc lực của các bạn đó (Như 4 lợi ích bên trên mình đã giới thiệu nhé).

2. Chi tiết

Setup

Đầu tiên, chúng ta phải cài NodeJS, ở đây nếu bạn nào chưa cài thì có thể download nó nhé, mình sẽ không hướng dẫn vì nó quá dễ rồi ^^.

Ở phần sau mình sẽ tạo ra nhiều phần theo đề mục là Api của Gulp để các bạn dễ hình dung nhé. Mỗi phần mình cũng sẽ chỉnh lại một chút file package.json để phù hợp hơn nhé.

Bây giờ hãy init một project dự án nhé với câu lệnh npm init -y. Như vậy thì các bạn đã có phần khởi tạo là file package.json ban đầu rồi đó.

{
    "name": "gulp",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "repository": {
        "type": "git",
        "url": "git+https://github.com/Nguyen-Xuan-Son/gulp.git"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "bugs": {
        "url": "https://github.com/Nguyen-Xuan-Son/gulp/issues"
    },
    "homepage": "https://github.com/Nguyen-Xuan-Son/gulp#readme"
}

Cài đặt

Ta hãy cùng cài đặt GULP bằng cách chạy lệnh.

yarn add gulp -D
// or
npm install -D gulp

Vì đang không cài GULP ở global nên các bạn config tiếp cho mình một chút nữa nhé.

{
    "name": "gulp-getting-started",
    "version": "1.0.0",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "gulp": ".\\node_modules\\.bin\\gulp"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "gulp": "^4.0.2"
    }
}

Hãy để ý đến phần scripts trong kia và giờ hãy test một chút nào.

Như vậy, chúng ta đã cài đặt thành công và tiếp theo hãy tạo ra file gulpfile.js để chứa config GULP nhé. Cùng viết một hàm ngắn xem nó log ra nào Hello GULP.

function build() {
    console.log("Hello GULP");
}

exports.default = build;

Sau đó chạy lệnh đã config ta sẽ được kết quả như sau.

Thực hành và xem kết quả

Config task

Đối với version trước đây, chúng ta sẽ dùng task properties của GULP để định nghĩa các task khác nhau. Nhưng từ version 4 thì chúng ta sẽ dùng những function để thể hiện các task như là: Chuyển scss thành css, hay chuyển ES6+ thành ES5, …

Task sẽ có hai loại: Một loại là default và đặt theo tên, ở đây chỉ khác nhau ở cách set properties ơ Exports object.

function firstTask(cb) {
    // body omitted
    console.log("clean");
    cb();
}

exports.build = firstTask;
function firstTask(cb) {
    // body omitted
    console.log("clean");
    cb();
}

exports.default = firstTask;

Khi chạy ra chúng ta sẽ cho ra 2 kết quả khác nhau như thế này, các bạn hãy để ý lệnh mình chạy nhé. Đối với loại default sẽ không cần thêm tên, nhưng đối với loại có tên thì phải gọi cả tên vào nữa nhé.

Convert and optimize CSS preprocessor

Theo mình thấy thì các Web hiện đại bây giờ sẽ chủ yếu là dùng các CSS preprocessor như là SCSS, LESS, … nhưng trình duyệt không thể hiểu được những loại này lên chúng ta bắt buộc phải chuyển nó thành các CSS thông thường để Browser có thể hiểu được.

Hãy cài đặt thêm một Package để làm việc này nhé: gulp-sass, khi cài đặt xong hãy config lại file gulpfile.js để làm việc này ^^.

const { src, dest } = require("gulp");
const sass = require("gulp-sass");

function optimizePreprocessorCSS() {
    return src('*.scss')
        .pipe(sass()) // Using gulp-sass
        .pipe(dest('output'));
}

exports.default = optimizePreprocessorCSS;

Sau đó chúng ta sẽ có kết quả như sau:

Để chuyển code css bên trên thành một dòng duy nhất hãy cài thêm gulp-cananao và sau đó config lại gulpfile.js bạn sẽ thấy sự khác biệt rõ rệt sau khi config.

const { src, dest } = require("gulp");
const sass = require("gulp-sass");
const cssnano = require('gulp-cssnano');

function optimizePreprocessorCSS() {
    return src('*.scss')
        .pipe(sass())
        .pipe(cssnano())
        .pipe(dest('output'));
}

exports.default = optimizePreprocessorCSS;

Optimizing Images

Ở đây các bạn có thể hiểu là một cái ảnh dùng điện thoại chụp có thể đến mấy MB hay mấy chục MB nếu dùng máy ảnh. Nhưng sau khi gửi bằng Message hay skype thì nó chỉ còn mấy trăm KB, vì những ứng dụng này đã xóa đi các thông tin không cần thiết trong ảnh rồi.

Cũng như vậy, trong ảnh không chỉ có hình mà còn có rất nhiều thông tin không cần thiết khác cho việc hiển thị nên chúng ta cũng có thể Optimize một phần nhỏ này góp phần làm cho App của chúng ta nhẹ hơn, load nhanh hơn.

Hãy cài thêm gulp-image và chỉnh lại file gulpfile.js thành như sau:

const { src, dest } = require("gulp");
const gulpImage = require("gulp-image");

function optimize() {
    return src('*.jpg')
        .pipe(gulpImage())
        .pipe(dest('output'));
}

exports.default = optimize;

Khi chạy lệnh các bạn sẽ thấy nó log ra như sau, hãy để ý đến một phần nhỏ kích thước ảnh mà các bạn có thể rút ngắn nhé. Mỗi thứ một chút, app của bạn sẽ nhanh hơn rất nhiều đó ^^.

Sau khi Optimize, ảnh sẽ vẫn hiển thị bình thường nhưng sẽ có một số thông tin bên trong ảnh bị xóa đi và như vậy ảnh sẽ nhẹ hơn. Còn nó là những thông tin gì thì mình sẽ giải thích chi tiết hơn sau nhé.

Optimize and covert version JavaScript files

Ở đây chúng ta sẽ có hay phần chính đó chính là Optimize code và convert JS code từ ES6+ thành version nhỏ hơn.

Đối với Optimize code, bạn hãy cài thêmgulp-uglify để chuyển file JS thành 1 file duy nhất và nối chúng lại thành một dòng nhé.

const { src, dest } = require("gulp");
const uglify = require("gulp-uglify");

function optimize() {
    return src('*.js')
        .pipe(uglify())
        .pipe(dest('output'));
}

exports.default = optimize;

Chạy lệnh ta sẽ để ý được chúng ta sẽ nối lại cái file js được thành 1 file :D.

Đối với phần chuyển code Es6+ về version cũ hơn, thì hãy hiểu là như phần babel trước mình trình bày thôi. Ở đây các bạn cần cài thêm một số plugin nhé.

npm install --save-dev gulp-babel @babel/core @babel/preset-env
// or
yarn add gulp-babel @babel/core @babel/preset-env -D

Sau đó hãy cùng config lại file gulpfile.js rồi từ đây code thể code ES6+ được rồi nhé.

const { src, dest } = require("gulp");
const useref = require("gulp-useref");
const uglify = require("gulp-uglify");
const gulpIf = require("gulp-if");
const gulpBabel = require("gulp-babel");

function optimize() {
    return src('*.js')
        .pipe(gulpBabel({
            presets: ['@babel/env']
        }))
        .pipe(uglify())
        .pipe(dest('output'));
}

exports.default = optimize;

Từ đây các bạn có thể code ES6 được rồi và sau khi chạy chúng ta sẽ thu được kết quả ví dụ như sau nhé.

"use strict";var random=function(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)};console.log(random());

Watching files for changes

Ở phần này chúng ta sẽ config để khi mà change file và save thì gulp sẽ chạy lại các file, convert lại thành các định dạng mà ta muốn.

Đầu tiên hãy đổi lại file gulpfile.js thành như sau:

const { src, dest, watch } = require("gulp");
const uglify = require("gulp-uglify");
const gulpBabel = require("gulp-babel");
const sass = require("gulp-sass");
const cssnano = require("gulp-cssnano");

function scripts() {
    return src('./js/*.js')
        .pipe(gulpBabel({
            presets: ['@babel/env']
        }))
        .pipe(uglify())
        .pipe(dest('./output/js'));
}

function styles() {
    return src('./css/*.scss')
        .pipe(sass())
        .pipe(cssnano())
        .pipe(dest('./output/css'));
}

function watchFiles() {
    scripts();
    styles();
    watch("./js", scripts);
    watch("./css", styles);
}

exports.default = watchFiles;

Ở đây sẽ chia config các file JS hay Css thành các phần khác nhau và sau đó watch chúng. Sau đó export nó như default :D. Sau khi config xong chúng ta sẽ được như sau.

Live-reloading with Browser Sync

Ở phần này mình sẽ tạo một server ảo và mỗi khi thay đổi file thì server này sẽ load lại một lần. Nó sẽ đỡ việc cho các bạn rất nhiều, ở phần này mình cũng tích hợp thêm Optimize css và Optimize Js để cho các bạn xem nếu kết hợp cùng nó sẽ hoạt động như thế nào nhé.

Hãy cùng thay đổi lại file gulpfile.js thành như sau:

const { src, dest, watch } = require("gulp");
const uglify = require("gulp-uglify");
const gulpBabel = require("gulp-babel");
const sass = require("gulp-sass");
const browserSync = require('browser-sync').create();
const cssnano = require("gulp-cssnano");

function browserSyncInit() {
    return browserSync.init({
        server: {
            baseDir: './'
        },
    })
}

function scripts() {
    return src('./app/js/*.js')
        .pipe(gulpBabel({
            presets: ['@babel/env']
        }))
        .pipe(uglify())
        .pipe(dest('./output/js'))
        .pipe(browserSync.reload({
            stream: true
        }));
}

function styles() {
    return src('./app/css/*.scss')
        .pipe(sass())
        .pipe(cssnano())
        .pipe(dest('./output/css'))
        .pipe(browserSync.reload({
            stream: true
        }));
}

function watchFiles() {
    scripts();
    styles();
    browserSyncInit();
    watch("./app/js", scripts);
    watch("./app/css", styles);
}

exports.default = watchFiles;

Như các bạn thấy trong hàm mà mình exports dafult, đầu tiền mình sẽ cho tạo các file trong folder output bằng cách cách chạy scripts() và styles(). Sau đó mình tạo ra browser để gắn phần ở output này vào, tiếp đó mình watch xem file nào thay đổi rồi sẽ cho chạy lại browser bằng cách cho các tasks: scripts và styles vào watch với điều kiện là mình đã config reload: true bên trong mỗi task rồi nhé. ^^

Khi config xong các bạn sẽ được như sau, ở đây mình chỉ add css vào thôi nhé:

Processing in Gulp

Ở phần này mình sẽ giới thiệu đến các bạn parallel()series(), đây là 2 cách để chạy các task tuần tự hoặc song song trong gulp.

Đối với series() ở đây nghĩa là các task sẽ chạy theo thứ tự task a xong rồi mới đến task b được chạy tiếp. Hãy cùng thay đổi gulpfile.js và quan sát sự thay đổi nhé.

const { src, dest, series } = require("gulp");
const uglify = require("gulp-uglify");
const gulpBabel = require("gulp-babel");
const sass = require("gulp-sass");
const cssnano = require("gulp-cssnano");

function scripts() {
    return src('./app/*.js')
        .pipe(gulpBabel({
            presets: ['@babel/env']
        }))
        .pipe(uglify())
        .pipe(dest('./output'));
}

function styles() {
    return src('./app/*.scss')
        .pipe(sass())
        .pipe(cssnano())
        .pipe(dest('./output'));
}

exports.default = series(scripts, styles);

Và hãy chạy lệnh nào, các bạn hãy chú ý phần Starting và Finished ở bên dưới dòng Terminal nhé.

Đối với parallel() nghĩa là các task ở đây sẽ được chạy song song, nó sẽ không có thứ tự nào và cũng không cần đợi task trước chạy xong rồi mới đến task sau, hãy cũng đổi lại file gulpfile.js và xem lại sự thay đổi nào.

const { src, dest, parallel } = require("gulp");
const uglify = require("gulp-uglify");
const gulpBabel = require("gulp-babel");
const sass = require("gulp-sass");
const cssnano = require("gulp-cssnano");

function scripts() {
    return src('./app/*.js')
        .pipe(gulpBabel({
            presets: ['@babel/env']
        }))
        .pipe(uglify())
        .pipe(dest('./output'));
}

function styles() {
    return src('./app/*.scss')
        .pipe(sass())
        .pipe(cssnano())
        .pipe(dest('./output'));
}

exports.default = parallel(scripts, styles);

Và như vậy là các task của chúng ta đã chạy song song rồi nhé.

Example

Dưới đây là một file config hoàn chỉnh mà mình đã làm nhé, các bạn có thể tham khảo thêm và tất cả code mình làm đều được lưu trong Github nhé.

const { src, dest, watch, series, parallel } = require("gulp");
const uglify = require("gulp-uglify");
const gulpBabel = require("gulp-babel");
const gulpImage = require("gulp-image");
const useref = require('gulp-useref');
const cssnano = require("gulp-cssnano");
const gulpIf = require('gulp-if');
const sass = require("gulp-sass");
const browserSync = require("browser-sync").create();
const del = require('del');

const paths = {
    fonts: {
        src: 'app/fonts/*.otf',
        dest: 'output/fonts/'
    },
    images: {
        src: 'app/images/*.jpg',
        dest: 'output/images/'
    },
    stylesConvert: {
        src: 'app/scss/*.scss',
        dest: 'app/css/'
    },
    styles: {
        src: 'app/css/*.css',
        dest: 'output/css/'
    },
    scripts: {
        src: 'app/js/*.js',
        dest: 'output/js/'
    },
    output: 'output'
};

function clean() {
    return del([paths.output]);
}

function browserSyncInit() {
    console.log("browserSyncInit");
    return browserSync.init({
        server: {
            baseDir: 'app'
        },
    })
}

function stylesConvert() {
    return src(paths.stylesConvert.src)
        .pipe(sass())
        .pipe(dest(paths.stylesConvert.dest));
}

function dev() {
    clean();
    series(stylesConvert, parallel(browserSyncInit, userefTasks))();
    watch(paths.stylesConvert.src, series(stylesConvert, userefTasks));
    watch(paths.scripts.src, userefTasks);
}

function userefTasks() {
    return src('app/*.html')
        .pipe(useref())
        .pipe(browserSync.reload({
            stream: true
        }));
};

function optimizeJSAndCss() {
    return src('app/*.html')
        .pipe(useref())
        .pipe(gulpIf('*.js', gulpBabel({
            presets: ['@babel/env']
        })))
        .pipe(gulpIf('*.js', uglify()))
        .pipe(gulpIf('*.css', cssnano()))
        .pipe(dest(paths.output));
}

function fonts() {
    return src(paths.fonts.src)
        .pipe(dest(paths.fonts.dest));
}

function optimizeImages() {
    return src(paths.images.src)
        .pipe(gulpImage())
        .pipe(dest(paths.images.dest))
        .pipe(browserSync.reload({
            stream: true
        }));
}

const pro = series(clean, stylesConvert, parallel(optimizeJSAndCss, fonts, optimizeImages));

exports.clean = clean;
exports.dev = dev;
exports.pro = pro;

3. Kết luận

Ở bên trên chỉ là những phần hay dùng thôi nhé, còn rất nhiều phần khác xoay quanh nữa, các bạn có thể xem thêm ở phần link tham khảo nhé.

Mình định chia ra cho ngắn hơn để các bạn đỡ ngại đọc, nhưng lại thấy nếu vậy các bạn sẽ bị mất mạch hiểu, nên thôi ^^ mình cứ để hết ở một bài nhé.

Khi implement phần này nếu các bạn có thắc mắc gì thì đừng ngại mà comment xuống bên dưới cho mình biết nhé.

Các bạn có thể clone lại phần mình đã làm ở đây: Github

4. Tham khảo

# Gulp for Beginners
# Quick Start

Tagged , , ,
0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments