Sử dụng Grunt trong ASP.NET Core

Grunt là một JavaScript task runner nhằm mục đích tự động hóa script minification, biên dịch TypeScript, đảm bảo chất lượng code với tools "lint", CSS pre-processors, và bất kì công việc gì lập đi lập lại cần để hỗ trợ quá trình phát triền phía client. Trong bài này tôi sẽ giới thiệu đến các bạn cách cấu hình Grunt trong một dự án Asp.Net Core. Trước tiên các bạn cần cài đặt .Net Core, để download và cài đặt cho từng phiên bản và môi trường hệ điều hành các bạn có thể xem chi tiết tại đây. Các công việc mà tôi thực hiện trong ví dụ sau bao gồm: Xóa thư mục deployment, combines javascript files, kiểm tra chất lượng code, tối ưu hóa dung lượng của nội dung trong file javascript và triển khai code tới thư mục deployment. Chúng ta sẽ sử dụng những packages bên dưới:

  • grunt: The Grunt task runner package.
  • grunt-contrib-clean: Plugin để xóa files hoặc thư mục.
  • grunt-contrib-jshint: Plugin để kiểm tra chất lượng code JavaScript.
  • grunt-contrib-concat: Plugin để nối nhiều files tới một file duy nhất.
  • grunt-contrib-uglify: Plugin để tối ưu hóa dung lượng nội dung JavaScript.
  • grunt-contrib-watch: Plugin để theo dõi những thay đổi của file.
  • typescript: Module để biên dịch ngôn ngữ typescript..

1.Cài đặt ứng dụng

Để bắt đầu, cài đặt một dự án web trống và thêm file ví dụ TypeScript. Dùng TypeScript Compiler (tsc) để biên dịch ra file .js. Trong bài viết này tôi sẽ demo trên Ubuntu.

1.1 Tạo một empty web (Asp.Net Core)

  • Tạo thư mục dự án đặt tên là grunt-aspnet-core
  • Chạy cd grunt-aspnet-core
  • Chạy dotnet new web để tạo empty asp.net core application

Mở dự án với visual studio code, chúng ta sẽ thấy cấu trúc thư mục dự án như hình bên dưới:

1.2 Code typescript

  • Thêm thư mục typescript
  • Thêm file Tastes.tsFood.ts

Tastes.ts

enum Tastes { Sweet, Sour, Salty, Bitter }

Food.ts

class Food {
  constructor(name: string, calories: number) {
    this._name = name;
    this._calories = calories;
  }

  private _name: string;
  get Name() {
    return this._name;
  }

  private _calories: number;
  get Calories() {
    return this._calories;
  }

  private _taste: Tastes;
  get Taste(): Tastes { return this._taste }
  set Taste(value: Tastes) {
    this._taste = value;
  }
}

1.3 Thêm tsconfig file để build typescript

tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "module": "es6",
        "moduleResolution": "node",
        "declaration": false,
        "sourceMap": false,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": true,
        "noLib": false,
        "noImplicitAny": true,
        "noUnusedLocals": false,
        "strictNullChecks": true,
        "newLine": "LF"
    },
    "exclude": [
        "node_modules"
    ]
}

2. Cấu hình package module (NPM)

  • Tạo file package.json và thêm những node module cần thiết cho ứng dụng demo của chúng ta như bên dưới:
{
    "name": "grunt-aspnet-core",
    "version": "1.0.0",
    "description": "Using grunt in asp.net core",
    "author": "HiepHV",
    "devDependencies": {
        "grunt": "0.4.5",
        "grunt-contrib-clean": "0.6.0",
        "grunt-contrib-jshint": "0.11.0",
        "grunt-contrib-concat": "0.5.1",
        "grunt-contrib-uglify": "0.8.0",
        "grunt-contrib-watch": "0.6.1",
        "typescript": "^2.1.6"
    }
}
  • Chạy npm install để cài các node module trong package.json

3. Cấu hình Grunt

Grunt được cấu hình trong một file được đặt tên là Gruntfile.js, dùng để định nghĩa, loads và register tasks, những công việc mà có thể chạy tự động.

module.exports = function (grunt) {
    grunt.initConfig({
        clean: ["wwwroot/lib/*", "temp/"],
        concat: {
            all: {
                src: ['typescript/Tastes.js', 'typescript/Food.js'],
                dest: 'temp/combined.js'
            }
        },
        jshint: {
            files: ['temp/*.js'],
            options: {
                '-W069': false,
                reporterOutput: ""
            }
        },
        uglify: {
            all: {
                src: ['temp/combined.js'],
                dest: 'wwwroot/lib/combined.min.js'
            }
        },
        watch: {
            files: ["typescript/*.js"],
            tasks: ["all"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask("all", ['clean', 'concat', 'jshint', 'uglify']);
    grunt.registerTask("default", ['clean', 'concat', 'jshint', 'uglify', 'watch']);
};
  • Task clean
clean: ["wwwroot/lib/*", "temp/"]

Dùng để xóa tất cả các file và thư mục trong 2 thư mục wwwroot/lib/ và temp thông qua plugin grunt-contrib-clean

  • Task concat
 concat: {
    all: {
        src: ['typescript/Tastes.js', 'typescript/Food.js'],
        dest: 'temp/combined.js'
    }
  },

Thực hiện việc nối nhiều file .js thành 1 file duy nhất thông qua plugin grunt-contrib-concat

  • Task jshint
jshint: {
    files: ['temp/*.js'],
    options: {
        '-W069': false,
        reporterOutput: ""
    }
},

Dùng để kiểm tra chất lượng code, vấn đề của code. Option -W069 là một lỗi được sinh ra bởi jshint khi code sử dụng cú pháp như Tastes["Sweet"] thay vì Tastes.Sweet để gán thuộc tính. Để pass qua lỗi này chúng ta set nó là false. Option reporterOutput: "" để fix Warning: Path must be a string. của jshint trong quá trình chạy grunt (cái mà dẫn đến grunt không thể tiếp tục làm việc) .

  • Task uglify
uglify: {
    all: {
        src: ['temp/combined.js'],
        dest: 'wwwroot/lib/combined.min.js'
    }
},

Task này thực hiện việc tối ưu hóa, giảm dung lượng của nội dung file javascript

  • Task watch
 watch: {
    files: ["typescript/*.js"],
    tasks: ["all"]
}

Thực hiện việc theo dõi các thay đổi trong các file .js

  • Đăng kí một loạt task thông qua một alias Để có thể chạy một series các tasks cùng nhau, chúng ta cần đăng kí nó với một alias. Ví dụ, tôi muốn chạy tất cả các task trên với tên chung là all, tôi cần làm như sau:
grunt.registerTask("all", ['clean', 'concat', 'jshint', 'uglify']);

Trong trường hợp đặc biệt, khi chạy grunt mà không cần chỉ rõ tên alias nào thì chúng ta cần đăng kí với alias là default

grunt.registerTask("default", ['clean', 'concat', 'jshint', 'uglify', 'watch']);
  • Chạy tsc để compile .ts tới file .js
  • Cuối cùng run command grunt, các bạn có thể thấy kết quả như bên dưới:

4. Kết

Grunt thực sự đã mang lại rất nhiều lợi ích trong phát triển ứng dụng web và thực tế nó đã được sử dụng rất phổ biến. Asp.Net Core là khá mới, vì vậy với bài viết này tôi hy vọng sẽ giúp ích cho các bạn để sử dụng công cụ tuyệt vời này trong ASP.NET Core.

Các bạn có xem link demo code: https://github.com/quanghiepth86/grunt-aspnet-core-example


All Rights Reserved