Nâng cao chất lượng code sử dụng Eslint

Bắt đầu với Eslint

Mặc dù, bài viết sẽ không nói về khái niệm cơ bản của Eslint, nhưng tôi sẽ dành một chút thời gian để chỉ cho bạn làm thế nào để bắt đầu. Đầu tiên là cài đặt Eslint global. Hãy chắc rằng bạn đã cài Node.js trên máy của bạn và chạy câu lệnh sau đây từ terminal:

npm install eslint -g

Eslint đã được cài đặt. Nào bây giờ thì đi đến một thư mục bất kỳ mà bạn muốn đặt code và tạo một file tên là .eslintrc . Đặt cấu hình như sau vào trong file:

{
  "extends": "eslint:recommended",
  "parserOptions": {
    "ecmaVersion": 2016
  }
}

Hiện tại cấu hình như trên, chúng ta chỉ sử dụng rule mà Eslint đề xuất eslint:recommended. Tiếp theo tạo một file javascript  với tên là lint-me.js trong cùng thư mục và đặt đoạn code như sau vào trong file:

function shout(message) {
  return message.toUpperCase() + '!!!';
}

Cài đặt ecmaVersion là cần thiết để Eslint có thể nhận biết được cú pháp ES2016 ( Bạn không cần plugin của babel cho Eslint nữa)

Tiếp theo chạy Eslint với câu lệnh như sau:

eslint ./lint-me.js

Bạn sẽ nhìn thấy một lỗi thông báo như sau “shout is defined but never used ” . Các rule Eslint:recommended  kiểm tra hầu hết các lỗi thông dụng. Tất nhiên tạo một hàm nhưng không bao giờ sử dụng nó là vô giá trị ! Chúng ta sẽ thử sửa nó bằng cách thêm shout('Wecodetheweb is awesome'); Bây giờ chạy lệnh Eslint lại, nó sẽ không có lỗi nào xảy ra nữa.

Rule đề xuất

Khi bạn chỉ đang bắt đầu sử dụng, thì nên bắt đầu với các rule được đề xuất giống như chúng ta đã làm bên trên. Tại sao ? Bởi vì bạn sẽ bị lạc trong tất cả các rule có thể dùng khi bạn chọn một trong số chúng cho riêng bạn.

Nếu file cấu hình để trống, sẽ không có rule nào được áp dụng. Chỉ có cú pháp lỗi sẽ làm cho Eslint lỗi. Nếu bạn thêm một bộ rule, thì tất cả những rule mà bạn tự thêm sẽ bị ghi đè bởi bộ đó.

Các Rule đề xuất bắt hầu hết các lỗi thông dụng và các “bad practice” trong code của bạn như: các biến không xác định đang sử dụng,unreachable code (Không chạy) hoặc các key dùng hai lần trong object sẽ làm Eslint bị lỗi. Đó là những thứ không developer nào muốn. Tất cả các rule được định nghĩa tại đây được đưa vào trong bộ  Eslint:recommended

Giảm độ phức tạp

Hầu hết mọi người sử dụng linter( công cụ để phân tích code) bởi vì họ mong muốn tất cả thành viên team sử dụng style giống nhau như là dùng nháy đơn hoặc 2 spaces khi căn lề. Mặc dù nó chắc chắn là rất hữu ích và bạn nên sử dụng nó, nhưng trong bài viết này chúng ta sẽ chỉ tập trung vào chất lượng code. Nào chúng ta cùng bắt đầu bằng cách giảm ‘cyclomatic complexity’

Cyclomatic complexity được đo bằng số lượng rẽ nhánh của đoạn code (trong trường hợp của chúng ta là một hàm đơn). Một nhánh là một đường dẫn đơn và duy nhất mà bạn đặt một đoạn code vào trong. Càng nhiều nhánh phức tạp thì càng không tốt vì làm cho code trở nên khó đọc và khó bảo trì, kết quả là nó sẽ gây ra những bug mà chúng ta không kiểm soát được.

Hãy cùng xem đoạn code dưới đây:

function isHappy(user) {
  if (user.happiness > 6) {
    return true;
  }
  return false;
}

Hàm này có Cyclomatic complexity là 2 bởi vì nó có 2 đường dẫn khi chạy có thể xảy ra. 2 là tốt và mức độ phức tạp là thấp. Chúng ta cùng cấu hình Eslint đề đảm bảo hàm sẽ theo rule này

{
  "parserOptions": {
    "ecmaVersion": 2016
  },
  "rules": {
    "complexity": [2, 5]
  }
}

Giá trị 2 đầu tiên nghĩa là rule này nên đưa ra một error . Mặc định giá trị của độ phức tạp sẽ là 20, nó là quá cao, do đó chúng ta có thể truyền vào giá trị là 5. Giờ đây, khi một hàm có độ phức tạp cao hơn 5, thì nó sẽ  đưa ra một error.

Trong một vài chỗ khác thì giá trị trong khoảng từ 4 đến 6 là đẹp cho complexity. Để thử nó, hãy lấy một đoạn code phức tạp từ code hiện tại của bạn và chạy Eslint với cài đặt của complexity, bạn sẽ tìm ra những đoạn code naò cần refactor

Giảm số lượng câu lệnh

Các hàm nên ngắ gọn và rõ ràng. Nó giúp cho chúng ta dễ dàng khi đọc code, bảo trì và giảm số lượng bug.

function isHealthy(player, config) {
  const healthPercentage = player.health * 100;
  const isAboveInjuryLimit = healthPercentage > config.injuryLimit;
  const isSick = player.diseases.length > 0;
  return !isAboveInjuryLimit && !isSick;
}

Hàm ở bên trê có 4 câu lệnh ( có 4 dòng). Nhiều câu lệnh nghĩa là có nhiều dòng code, để đảm bảo điều này là đúng bằng cách cấu hình câu lệnh tối đa trên mỗi dòng quy định. Chúng ta mong muốn các hàm đủ nhỏ như trên. Chúng ta hãy cùng cấu hình Eslint để giúp kiểm tra rule này cho chúng ta

{
  "parserOptions": {
    "ecmaVersion": 2016
  },
  "rules": {
    "complexity": [2, 5],
    "max-statements": [2, 7],
    "max-statements-per-line": [2, {
      "max": 1
    }]
  }
}

Số lượng xung quanh 7 là đẹp cho việc cấu hình số câu lệnh tối đa, bạn thậm trí có thể để thấp hơn. Số câu lệnh tối đa trên một dòng nên là 1, nó giúp cho câu lệnh dễ phát hiện hơn.

Giảm nesting

Hãy nhìn đoạn nesting code bên dưới đây

doSomethingAsync(result => {
  doSomethingElse(result.thing, err => {
    if (!err) {
      if (result.success) {
        if (true) {
          doLastAsyncThing(() => {
            console.log(result);
          });
        }
      }
    }
  });
});

Đừng code như thế này! Bạn có thể tách các thành phần trong hàm ra hoặc sử dụng một vài cách khác như là Promise để tránh làm như này. Nhưng để đảm bảo rằng nó sẽ không xảy ra nữa thì có thể dùng 2 rule sau:

{
  "parserOptions": {
    "ecmaVersion": 2016
  },
  "rules": {
    "complexity": [2, 5],
    "max-statements": [2, 7],
    "max-statements-per-line": [2, {
      "max": 1
    }],
    "max-nested-callbacks": [2, 2],
    "max-depth": [2, {
      "max": 2
    }]
  }
}

Nó giúp ta chắc chắn rằng không có các đoạn nesting và callback nhiều hơn 2 cấp độ sâu, giúp chúng ta tránh được đoạn code phức tạp, khó đọc và debug với kiểu code dạng cây

Các rule khác

Có một vài rule quan trọng mà có thể giúp bạn viết code tốt hơn. Sau đây là một vài rule yêu thích của tôi:

  • eqeqeq": 2 – Đảm bảo rằng bạn sẽ không bao giờ dùng == hoặc != khi so sành bằng nhau, bởi vì bạn biết rằng, khi so sánh như thế nó sẽ không so sánh dựa vào kiểu dữ liệu nên 2 dạng number bằng “2” dạng string
  • "no-eval": 2 – chúng ta đều biết rằng sử dụng eval là không tốt..
  • "no-var": 2, "prefer-const": 2 – khi sử dụng es2015 hoặc mới hơn ( bạn nên sử dụng rule naỳ nếu bạn có thể). Rule này khuyến khích bạn không bao giờ sử dụng var và sử dụng const thay cho let trong mọi trường hợp có thể. Điều này bắt buộc bạn nghĩ kỹ khi gán lại giá trị cho một biến và không sử dụng var trong hàm
  • "max-lines": [2, 90] – Giới hạn số lượng dòng của file. Không quan trọng là giá trị của nó đem lại là gì, nó chỉ đảm bảo rằng bạn không tạo ra những file khủng khiếp với hơn 200 dòng code.
  • "no-return-assign": 2, "no-param-reassign": 2, "array-callback-return": 2 – Giúp bạn tránh đoạn code bẩn thỉu như là return foo = bar + 2;

Dành cho các rule mà bạn không muốn / cần đặt giá trị , bạn chỉ có thể  đặt mức lỗi: "rule": 2. Ngược lại, bạn cần truyền một mảng với giá trị của rule là phần tử thứ 2 : "rule": [2, 'Giá trị']. Thông tin về các kiểu giá trị khác nhau theo rule và bạn có thể tìm hiểu trong website của Eslint

Chúng ta đã tạo được một cấu hình tương đối tốt cho Eslint, hãy cùng xem kết quả như bên dưới:

{
  "extends": "eslint:recommended",
  "parserOptions": {
    "ecmaVersion": 2016
  },
  "rules": {
    "array-callback-return": 2,
    "complexity": [2, 5],
    "eqeqeq": 2,
    "max-statements": [2, 7],
    "max-statements-per-line": [2, {
      "max": 1
    }],
    "max-nested-callbacks": [2, 2],
    "max-depth": [2, {
      "max": 2
    }],
    "max-lines": [2, 90],
    "no-eval": 2,
    "no-return-assign": 2,
    "no-param-reassign": 2,
    "no-var": 2,
    "prefer-const": 2
  }
}

Tắt cấu hình shelf

Nếu bạn không muốn kiểm tra tất cả các Rule mà Eslint đã đề xuất chúng ta dùng, nhưng lại muốn dùng các cài đặt tốt. Có một vài cấu hình được định nghĩa sẵn ở bên ngoài có thể dùng. Ví dụ như bạn có thể dùng cấu hình sau:

npm install eslint-config-ngerritsen -g
{
  "extends": "ngerritsen"
}

 Kết luận

Eslint có thể được sử dụng để bạn tạo một phong cách code nhất quán, ngăn chặn lỗi nhưng cũng giúp cải thiện chất lượng code. Việc đầu tiên tôi làm khi tạo một dự án Javascript là tạo một Eslint. Có nhều Rule và plugin để lựa chọn, khả năng là bất tận. Bạn chỉ nên bắt đầu với các rule được đề xuất sẵn hoặc cấu hình của ai đó và sau đó điều chỉnh các rule cho phù hợp với dự án của bạn

 

 

 

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.