Những ngày đen tối nhất của Vue

3721

Vừa qua cộng đồng VueJS đã có khoảng thời gian khá dậy sóng. Mới đây, người tạo ra Vue, Evan You đã phát hành RFC (Request for Comment) để viết component dựa trên chức năng trong phiên bản Vue 3.0 sắp tới.

Trên Reddit Thread và cả Hacker News xuất hiện rất nhiều bình luận phê bình và chỉ trích, hàng loạt các developer đổ xô vào RFC và bày tỏ sự phẫn nộ của họ, một số còn đi quá giới hạn với nhiều lời bình luận gay gắt.

  Unit testing các component Vue.js bằng các tool Vue testing và Jest (P2): Test Vue.js Components deep render trong Jest
  API Authentication trong Laravel-Vue SPA sử dụng Jwt-auth

Cộng đồng Vue dậy sóng với nhiều comment như:

  • Toàn bộ code Vue phải được biết lại theo cách hoàn toàn mới vì cách viết hiện tại đã bị xoá và thay thế bằng một cái gì đó mới;
  • Thời gian người ta dành ra để học Vue đã thành tro bụi vì mọi thứ sắp thay đổi;
  • Cách viết mới tệ hơn cách cũ, không thực thi cấu trúc, và sẽ dẫn đến spaghetti code;
  • Team Vue Core đã bất ngờ thực hiện một thay đổi lớn mà không hề có tham vấn;
  • Vue đang biến thành React!
  • Không, Vue đang biến thành AngularJS/Angular!
  • Giờ thì HTML phải được viết dưới dạng một string khổng lồ rồi!

Mặc dù có nhiều comment tiêu cực trên Reddit Thread, tỉ lệ reaction tích cực và tiêu cực cho RFC của You rất cao trên trang RFC, và đa phần ý kiến ban đầu khá tích cực.

Và thực tế, phiên bản RFC mới có rất nhiều ưu điểm. Nhưng trước tiên, cùng phân tích và giải quyết những lời chỉ trích tiêu cực trước đã.

Có khá nhiều người đã vội vàng bình luận tiêu cực và gay gắt trên Hacker News hoặc Reddit Thread mà không đọc kĩ các đề xuất ban đầu. Evan You cũng đã có đăng Q&A nhằm giải đáp nhiều vấn đề mọi người gặp phải:

  • Bạn không cần viết lại code nếu bạn không muốn. Cách viết mới chỉ là “thêm mắm thêm muối”, cách viết cũ vẫn có thể được sử dụng trong Vue 3.0 và miễn là còn nhiều người sử dụng cách cũ đó. Thậm chí nếu nó có bị gỡ ra khỏi Core code, các plugin có thể dễ dàng cho phép cách viết cũ còn hiệu lực 100%.
  • Thời gian học Vue của bạn không hề lãng phí. Cú pháp component mới sử dụng chung hình thức mà bạn học, và những hình thức khác như Component single file, template, và phạm vi hoạt động cũng như nhau.
  • Sự thay đổi này không phải là không có tham vấn. RFC là tham vấn. Cú pháp mới vẫn còn lâu mới được phát hành.
  • Và chắc chắn không, HTML không cần viết theo kiểu string khổng lồ rồi.

Một nhận định khá chủ quan là cách viết mới sẽ kém hơn cách viết cũ, và ít code cấu trúc hơn. Cùng xem 1 ví dụ tại saoRFC mới lại tuyệt vời hơn và có cấu trúc code tốt hơn nhé.

Ví dụ component vui, cho phép người dùng nhập thông tin về thú cưng của họ.

Lưu ý:

  • Một thông báo sẽ hiện ra khi người dùng nhập xong tên thú cưng của họ;
  • Sẽ có thông báo khác hiện lên sau khi họ chọn kích cỡ của thú cưng;

VÍ dụ RFC

Ví dụ RFC

Bạn có thể thử demo của component này tại đây và có thể xem toàn bộ code dùng Vue 2.x tại đây. (see components/Vue2.vue)

Một chút JavaScript nào:

export default {
  data() {
    return {
      petName: "",
      petNameTouched: false,
      petSize: "",
      petSizeTouched: false
    };
  },
  computed: {
    petNameComment: function() {
      if (this.petNameTouched) {
        return "Hello " + this.petName;
      }
      return null;
    },
    petSizeComment: function() {
      if (this.petSizeTouched) {
        switch (this.petSize) {
          case "Small":
            return "I can barely see your pet!";
          case "Medium":
            return "Your pet is pretty average.";
          case "Large":
            return "Wow, your pet is huge!";
          default:
            return null;
        }
      }
      return null;
    }
  },
  methods: {
    onPetNameBlur: function() {
      this.petNameTouched = true;
    },
    onPetSizeChange: function() {
      this.petSizeTouched = true;
    }
  }
};

Về cơ bản, chúng ta có một số dữ liệu, các thuộc tính được tính toán từ các dữ liệu đó và các phương thức sẽ thao túng dữ liệu đó. Chú ý là Vue 2.x không có cách nào để giữ những thứ liên quan lại với nhau. Chúng ta không thể để petName khai báo dữ liệu cạnh petNameComment hay phương thức onPetNamBlur được vì trong Vue 2.x nhóm theo loại.

Tất nhiên là điều này không quá quan trọng đối với một ví dụ nhỏ như vậy. Nhưng bạn hãy tưởng tượng với những ví dụ lớn hơn, có nhiều chức năng cần data, computed, methods và thậm chí một hoặc hai watcher. Hiện tại không có cách nào khả quan để giữ những thứ liên quan cùng một chỗ.

Bạn có thể sử dụng một vài thứ như Mixins hay Higher Order Components nhưng có một vấn đề là, rất khó để thấy các thuộc tính đến từ đâu và có những vấn đề gì với xung đột namespace. (Và chắc chắn rồi, trong trường hợp này chúng ta có thể chia ra nhiều component, nhưng hãy xem một ví dụ tương tự không làm cách đó.)

Thay vì sắp xếp các component theo từng loại, đề xuất mới cho phép ta sắp xếp component theo chức năng thực tế. Cũng tương tự cách bạn sắp xếp file cá nhân trên máy tính của mình vậy. Bạn không cần có folder “spreadsheets” hay folder “word documents”, thay vào đó bạn có một folder “work” và một folder “kế hoạch đi chơi”.

Hãy xem component ở trên được viết theo cách mới:

import { state, computed } from "vue";
export default {
  setup() {
    // Pet name
    const petNameState = state({ name: "", touched: false });
    const petNameComment = computed(() => {
      if (petNameState.touched) {
        return "Hello " + petNameState.name;
      }
      return null;
    });
    const onPetNameBlur = () => {
      petNameState.touched = true;
    };

    // Pet size
    const petSizeState = state({ size: "", touched: false });
    const petSizeComment = computed(() => {
      if (petSizeState.touched) {
        switch (this.petSize) {
          case "Small":
            return "I can barely see your pet!";
          case "Medium":
            return "Your pet is pretty average.";
          case "Large":
            return "Wow, your pet is huge!";
          default:
            return null;
        }
      }
      return null;
    });
    const onPetSizeChange = () => {
      petSizeState.touched = true;
    };

    // All properties we can bind to in our template
    return {
      petName: petNameState.name,
      petNameComment,
      onPetNameBlur,
      petSize: petSizeState.size,
      petSizeComment,
      onPetSizeChange
    };
  }
};

Lưu ý là:

  • Rất dễ để nhóm mấy thứ liên quan lại với nhau;
  • Bằng cách xem những gì được trả về bởi chức năng thiết lập, chúng ta có thể dễ dàng thấy những gì được quyền truy cập trong template của mình;
  • Thậm chí ta có thể tránh lộ state nội bộ (“touched”) mà template không cần truy cập;

Trên hết là cách mới dễ dàng cho phép hỗ trợ đầy đủ TypeScript, điều mà khó có thể làm được trong cách cũ ở Vue 2.x. Giờ chúng ta có thể dễ dàng trích xuất logic tái sử dụng thành các chức năng tái sử dụng.

Ví dụ:

import { state, computed } from "vue";

function usePetName() {
  const petNameState = state({ name: "", touched: false });
  const petNameComment = computed(() => {
    if (petNameState.touched) {
      return "Hello " + petNameState.name;
    }
    return null;
  });
  const onPetNameBlur = () => {
    petNameState.touched = true;
  };
  return {
    petName: petNameState.name,
    petNameComment,
    onPetNameBlur
  };
}

function usePetSize() {
  const petSizeState = state({ size: "", touched: false });
  const petSizeComment = computed(() => {
    if (petSizeState.touched) {
      switch (this.petSize) {
        case "Small":
          return "I can barely see your pet!";
        case "Medium":
          return "Your pet is pretty average.";
        case "Large":
          return "Wow, your pet is huge!";
        default:
          return null;
      }
    }
    return null;
  });
  const onPetSizeChange = () => {
    petSizeState.touched = true;
  };
  return {
    petSize: petSizeState.size,
    petSizeComment,
    onPetSizeChange
  };
}

export default {
  setup() {
    const { petName, petNameComment, onPetNameBlur } = usePetName();
    const { petSize, petSizeComment, onPetSizeChange } = usePetSize();
    return {
      petName,
      petNameComment,
      onPetNameBlur,
      petSize,
      petSizeComment,
      onPetSizeChange
    };
  }
};

Trong Vue 2.x, component thường rất khó để chia thành nhiều phần nhỏ. Không thể phân tách thành các component khác vì sẽ có rất nhiều thứ xảy ra trên một lượng nhỏ state.

Tuy nhiên sử dụng phương pháp mới sẽ dễ dàng để thấy cách các component lớn được chia tách thành các phần nhỏ hơn, chuyển chúng vào các file riêng biệt nếu cần thiết, và cho bạn những chức năng cũng như component nhỏ và dễ hiểu hơn.

Liệu đây có phải là ngày đen tối nhất của Vue? Có vẻ đúng vậy đấy. Đến bây giờ, cả một cộng đồng đã hiểu sai định hướng của project mới này. Hy vọng người ta sẽ có cái nhìn khác về phiên bản mới.

RFC mới vẫn cho phép họ nhóm mọi thứ theo loại tuỳ chọn nếu họ thích, nhưng nó còn cho phép nhiều hơn thế nữa. Code clear hơn, clean hơn, thư viện có nhiều khả năng thú vị hơn, và hỗ trợ TypeScript đầy đủ.

Cuối cùng, khi bạn dùng phần mềm mã nguồn mở, bạn nên biết ơn những người maintain đang nỗ lực rất nhiều vào một thứ gì đó cho bạn sử dụng free. Một số lời chỉ trích gay gắt thật sự không có lý. Rất may là những comment tiêu cực đó vẫn còn ít và đã có nhiều người thể hiện ý kiến tích cực và tôn trọng hơn cho tác giả và cả phiên bản mới này.

Cảm ơn các bạn đã theo dõi bài viết!

Có thể bạn quan tâm:

Xem thêm việc làm Software Developers hot nhất trên TopDev

  Unit testing các component Vue.js bằng các tool Vue testing và Jest (P3): Test các Style and cấu trúc của các Vue.js Component trong Jest
  Call API trong VueJS theo cách thông minh nhất