Các vấn đề thường gặp khi phát triển ứng dụng Vue.js và cách khắc phục

1. Khi tích hợp các thư viện UI như Element Plus hoặc Mint UI, cần đảm bảo file CSS của thư viện được import toàn cục (thường trong main.js hoặc main.ts). Tuy nhiên, để các quy tắc CSS tùy chỉnh của bạn có hiệu lực đúng mức độ ưu tiên và phạm vi áp dụng, nên đặt file CSS cá nhân vào cuối chuỗi import — ví dụ: sau khi import UI framework nhưng trước khi khởi tạo instance Vue.

2. Nội dung được render qua thuộc tính v-html không kế thừa các style từ CSS module hoặc scoped styles do cơ chế cô lập phạm vi (scope isolation) của Vue. Giải pháp an toàn hơn là chuyển logic hiển thị nội dung động sang một component con với template đầy đủ, nơi các class và style được khai báo rõ ràng và có thể kiểm soát được.

3. Khi gửi yêu cầu POST dạng form-data bằng Axios, cần tuần tự thực hiện ba bước: (i) cài đặt gói qs để serialize dữ liệu, (ii) cấu hình header Content-Type thành application/x-www-form-urlencoded, và (iii) truyền dữ liệu đã được xử lý qua qs.stringify(). Ví dụ:

import { createApp } from 'vue';
import axios from 'axios';
import qs from 'qs';

const app = createApp(App);
app.config.globalProperties.$http = axios;
app.config.globalProperties.$serialize = qs;

// Trong component:
this.$http.post('/api/login', this.$serialize({ username: 'a', password: 'b' }), {
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
});

4. Mặc định, watch chỉ theo dõi sự thay đổi ở cấp độ thuộc tính trực tiếp (shallow). Để phản ứng với thay đổi trong sâu của đối tượng (ví dụ: thay đổi giá trị bên trong một mảng hoặc object lồng), cần bật tùy chọn deep: true. Đồng thời, nếu muốn hàm handler chạy ngay lần đầu khởi tạo, thiết lập immediate: true:

watch: {
  userProfile: {
    handler(newValue) {
      this.updateDisplay(newValue);
    },
    deep: true,
    immediate: true
  }
}

5. Khi khai báo props, nên sử dụng cú pháp object để khai báo kiểu dữ liệu, kiểm tra bắt buộc và giá trị mặc định — giúp tăng tính ổn định và dễ bảo trì:

props: {
  userName: {
    type: String,
    required: true,
    default: 'Guest'
  },
  isActive: {
    type: Boolean,
    default: false
  }
}

6. Vì Axios trả về Promise, mọi xử lý dữ liệu phải nằm trong khối .then() hoặc dùng async/await. Việc truy cập kết quả ngoài chuỗi xử lý bất đồng bộ sẽ dẫn đến giá trị undefined. Nên lưu trữ kết quả từ await hoặc truyền vào hàm callback để xử lý an toàn.

7. Với event bus (ví dụ: một instance Vue độc lập làm trung gian), thứ tự đăng ký ($on) và phát ra sự kiện ($emit) rất quan trọng. Nếu $emit được gọi trước khi $on được gắn, sự kiện sẽ bị bỏ lỡ. Do đó, nên đăng ký listener sớm (ví dụ trong created hoặc mounted) hoặc sử dụng cơ chế event emitter hỗ trợ phát lại (replay) như mitt hoặc tiny-emitter.

8. Lỗi đường dẫn tài nguyên hình ảnh sau khi build (ví dụ: static/css/static/img/logo.png) thường xuất phát từ cấu hình publicPath sai trong webpack. Đối với dự án Vue CLI phiên bản cũ (dùng webpack), cần điều chỉnh publicPath trong config/index.js hoặc vue.config.js — ví dụ: publicPath: './' hoặc publicPath: '' tùy vào cấu trúc thư mục đích. Với Vue CLI 5+, nên dùng vue.config.js với tùy chọn assetsDirpublicPath phù hợp.

9. Thư mục public (không phải static — lưu ý: static đã bị loại bỏ từ Vue CLI 3+) chứa các tài nguyên được sao chép nguyên bản vào thư mục build. Các file trong public có thể được tham chiếu trực tiếp bằng đường dẫn tuyệt đối (ví dụ: /logo.png) mà không cần import — rất hữu ích cho favicon, manifest.json hoặc ảnh nền tĩnh.

10. Computed property hỗ trợ cả dạng hàm ngắn gọn (chỉ getter) và dạng object đầy đủ (getter + setter). Setter cho phép "gán ngược" giá trị vào các thuộc tính nguồn khi người dùng thay đổi giá trị computed:

computed: {
  fullName: {
    get() {
      return `${this.firstName} ${this.lastName}`;
    },
    set(value) {
      const parts = value.trim().split(/\s+/);
      this.firstName = parts[0] || '';
      this.lastName = parts[1] || '';
    }
  }
}

11. Tên component và tên prop trong Vue phải tuân thủ quy tắc kebab-case khi khai báo trong template HTML (do trình duyệt phân biệt chữ hoa/thường). Việc dùng PascalCase (ví dụ: <MyComponent>) chỉ hợp lệ trong môi trường SFC với trình biên dịch runtime + template string hoặc JSX. Ngoài ra, tên prop truyền từ cha sang con cũng phải viết thường toàn bộ — nếu dùng userName trong JS thì trong template phải là user-name.

Thẻ: vuejs Axios Webpack vue-cli css-scoping

Đăng vào ngày 17 tháng 7 lúc 16:58