Đã bao giờ bạn thấy mình hì hục copy-paste một cụm UI từ trang này sang trang khác, rồi đến khi sếp yêu cầu đổi một cái màu chữ, bạn phải lặn ngụp trong cả chục file để sửa chưa? Mình đã từng trải qua cơn ác mộng đó trong những năm đầu đi làm. Chính vì vậy, bài viết này sẽ đúc kết 10 năm kinh nghiệm của mình để chỉ cho bạn cách dùng Vue Component tạo component tái sử dụng “chuẩn”, hoạt động hiệu quả ở mọi nơi. Mục tiêu cuối cùng là giúp code sạch sẽ, dễ bảo trì và quan trọng nhất là giúp bạn có thêm thời gian đi uống cà phê thay vì ngồi fix bug giao diện lặt vặt.
Props và Slots: Bộ đôi nền tảng để tạo component tái sử dụng
Props và Slots là hai khái niệm cốt lõi nhất định hình cách tạo component tái sử dụng trong Vue.js. Props cho phép bạn truyền dữ liệu từ ngoài vào, trong khi Slots giúp bạn linh hoạt thay đổi cấu trúc HTML bên trong component.
Khi mới bước chân vào thế giới front-end, việc hiểu rõ component Vue là gì là viên gạch đầu tiên. Về cơ bản, nó là một khối mã độc lập chứa giao diện và logic riêng. Nếu bạn hoặc đồng nghiệp còn mơ hồ về các khái niệm nền tảng này, việc tham khảo lộ trình Học Vue.js 3 từ đầu cho người mới sẽ là bước đệm cực kỳ vững chắc. Tại Phạm Hải, mình luôn yêu cầu các bạn thực tập sinh nắm thật chắc bộ đôi Props và Slots trước khi đụng đến các logic phức tạp hơn.
Dùng Props để “ra lệnh” cho component con: Truyền dữ liệu từ cha xuống con một cách tường minh
Props đóng vai trò như các tham số đầu vào của một hàm, giúp giải quyết bài toán truyền dữ liệu giữa các component Vue một cách an toàn, tuân thủ luồng dữ liệu một chiều từ cha xuống con.
Trong các Single File Components (SFC), bạn định nghĩa Props để nhận data từ bên ngoài. Hãy tưởng tượng bạn có một component <BaseButton>. Thay vì hardcode cứng chữ “Submit” vào thẻ HTML, bạn định nghĩa một prop có tên là label. Như vậy, ở bất kỳ đâu trong ứng dụng, bạn chỉ cần gọi <BaseButton label="Đăng ký" /> hoặc <BaseButton label="Hủy" />.
Việc thiết kế component nhận tham số động này đảm bảo nguyên tắc DRY (Don’t Repeat Yourself). Bạn chỉ cần viết CSS và logic click cho nút bấm một lần duy nhất, sau đó tái sử dụng nó ở hàng trăm form khác nhau trong dự án.
Dùng Slots để “nhét” nội dung vào component: Tùy biến cấu trúc linh hoạt đến không ngờ
Slots cho phép bạn truyền các template HTML hoặc các component khác vào bên trong một component con, tạo ra cấu trúc component Vue linh hoạt và không bị giới hạn bởi dữ liệu text đơn thuần.
Đôi khi, Props không đủ sức để xử lý các UI phức tạp. Giả sử bạn làm một component <AppModal>. Phần tiêu đề có thể dùng Props vì nó chỉ là chuỗi văn bản, nhưng phần nội dung (body) của Modal lại có thể chứa một form đăng nhập, một hình ảnh quảng cáo, hoặc cả một bảng dữ liệu chi tiết.
Lúc này, Slots chính là “vị cứu tinh”. Bằng cách đặt thẻ <slot></slot> trong component con, bạn đang giữ chỗ để component cha “nhét” bất cứ khối HTML nào vào đó. Bạn thậm chí có thể dùng Named Slots để định nghĩa nhiều vùng giữ chỗ khác nhau (như header, body, footer). Đây là bí quyết cốt lõi để tối ưu component Vue khi giao diện yêu cầu sự biến hóa đa dạng ở nhiều màn hình khác nhau.
Validate Props: Đừng bao giờ tin tưởng dữ liệu đầu vào, hãy xác thực nó!
Xác thực Props bằng cách khai báo rõ kiểu dữ liệu (type) và giá trị mặc định (default) là một trong những best practices Vue component bắt buộc để tránh lỗi runtime (lỗi khi chạy ứng dụng).
Nhiều lập trình viên mới có thói quen khai báo props dạng mảng đơn giản như props: ['title', 'count']. Đừng làm vậy nếu bạn muốn dự án sống thọ! Hãy luôn định nghĩa rõ ràng kiểu dữ liệu: title phải là String, count phải là Number.
Nếu dự án của bạn sử dụng TypeScript, sức mạnh của việc validate này càng trở nên tuyệt vời hơn nhờ cơ chế static typing. Việc xác thực chặt chẽ ngay từ đầu giúp tính bảo trì của dự án tăng lên gấp bội. Bất kỳ đồng nghiệp nào khi tái sử dụng component của bạn cũng sẽ nhận được cảnh báo ngay trên trình soạn thảo code nếu họ truyền sai kiểu dữ liệu.
Nâng tầm tái sử dụng với Composition API và Event Emitting
Để tạo ra các Vue 3 component tái sử dụng mức độ cao, việc kết hợp Composition API để xử lý logic độc lập và Event Emitting để giao tiếp ngược là điều kiện tiên quyết.
Hệ sinh thái JavaScript luôn thay đổi, và Vue 3 cũng không ngoại lệ. Để làm chủ các kỹ thuật nâng cao này, bạn cần một nền tảng JS hiện đại. Việc nắm rõ các ES6 JavaScript tính năng mới cần biết như destructuring, arrow function sẽ giúp code trong hàm setup của bạn gọn gàng hơn rất nhiều. Đồng thời, kiến thức về cách chia tách file qua bài JavaScript Module import export hướng dẫn là bắt buộc khi bạn muốn trích xuất logic ra khỏi component.
Composition API: Tách logic, tái sử dụng code mà không cần đến Mixins phiền phức
Composition API cho phép gom nhóm các methods và computed properties có chung mục đích nghiệp vụ vào các Composable (hàm tái sử dụng), loại bỏ hoàn toàn nhược điểm khó kiểm soát của Mixins.
Trước đây ở thời Vue 2, chúng ta thường dùng Mixins để chia sẻ logic giữa các component. Tuy nhiên, Mixins gây ra tình trạng xung đột tên biến và hiện tượng “magic properties” (không biết biến này từ file nào chui ra).
Với Composition API (đặc biệt là cú pháp <script setup>), bạn có thể tách các methods, state và computed properties ra thành các file .js hoặc .ts riêng biệt (gọi là Composables, ví dụ usePagination()). Cách làm này giúp code minh bạch, dễ viết unit test và có thể cắm vào (plug-in) bất kỳ component nào cần tính năng phân trang mà không lo đụng độ dữ liệu.
Giao tiếp ngược từ con lên cha với $emit: Khi component con cần “báo cáo” lại
Sử dụng $emit (hoặc defineEmits trong Vue 3) là chuẩn mực để thực hiện event emitting, giúp component con phát ra tín hiệu báo cho component cha biết khi có tương tác người dùng.
Nguyên tắc bất di bất dịch của luồng dữ liệu trong Vue là “Props down, Events up” (Dữ liệu đi xuống, Sự kiện đi lên). Component con tuyệt đối không được tự ý sửa dữ liệu mà cha truyền xuống. Thay vào đó, nó chỉ được phép “hét lên” (emit) rằng: “Ê cha, người dùng vừa click vào nút X nè!”.
Từ đó, component cha sẽ lắng nghe sự kiện này (thông qua @click hoặc @custom-event) và tự quyết định cập nhật dữ liệu của chính nó. Kỹ thuật này cực kỳ quan trọng khi bạn xây dựng các Renderless components – những component chỉ chuyên xử lý logic ngầm mà không trực tiếp render ra giao diện cụ thể.
Xây dựng component hỗ trợ v-model: Bí quyết tạo ra các custom input “xịn”
Tích hợp v-model vào custom component giúp đồng bộ hóa dữ liệu hai chiều một cách mượt mà, nâng cao khả năng mở rộng và trải nghiệm của developer khi xử lý các form phức tạp.
Thay vì bắt component cha phải truyền prop value rồi lại hì hục lắng nghe sự kiện @input thủ công, bạn hãy thiết kế component con sao cho nó hoạt động trơn tru với directive v-model.
Trong các phiên bản Vue 3.4 trở lên, macro defineModel() đã làm cho việc này dễ dàng hơn bao giờ hết chỉ với một dòng code. Tại Phạm Hải, các custom input (như DatePicker, SelectBox, ToggleSwitch) của bọn mình đều có quy định bắt buộc phải hỗ trợ v-model. Điều này giúp những người đồng nghiệp sử dụng lại component đó cảm thấy vô cùng nhàn hạ.
Những sai lầm chết người cần tránh khi tạo component
Trong quá trình tìm hiểu làm thế nào để tái sử dụng component Vue, nhiều lập trình viên thường mắc phải các lỗi thiết kế kiến trúc cơ bản, dẫn đến code bị “thắt cổ chai” và sinh ra vô số bug khi dự án phình to.
Việc lạm dụng state (trạng thái) cục bộ đôi khi gây ra rắc rối lớn. Nếu nhiều component ở các cây thư mục khác nhau cần chung một nguồn dữ liệu (như thông tin user đăng nhập), việc dùng Pinia state management Vue 3 thay Vuex là giải pháp tối ưu. Nó giúp quản lý trạng thái tập trung và tránh việc phải truyền props qua quá nhiều tầng component trung gian (prop drilling).
Sai lầm 1: Thay đổi trực tiếp Props của cha – “Tội đồ” gây ra những bug khó lường
Việc mutate (thay đổi) trực tiếp prop bên trong component con sẽ phá vỡ luồng dữ liệu một chiều của Vue, gây rối loạn quản lý trạng thái và làm sai lệch cơ chế render của Virtual DOM.
Vue sẽ ném ra một cảnh báo (warning) đỏ chót trên console ngay lập tức nếu bạn cố tình gán giá trị mới cho một prop. Lý do là mỗi khi component cha re-render, giá trị prop bạn vừa sửa ở component con sẽ bị ghi đè lại bằng giá trị gốc từ cha.
Nếu bạn cần thay đổi dữ liệu hiển thị dựa trên prop truyền vào, hãy gán prop đó vào một biến ref cục bộ để làm giá trị khởi tạo, hoặc dùng computed property để tạo ra một giá trị phái sinh. Đừng bao giờ đụng chạm trực tiếp vào “tài sản” của component cha.
Sai lầm 2: Component quá “thông minh” – Ôm đồm quá nhiều logic không liên quan
Một component tái sử dụng tốt nên là “dumb component” (component trình diễn), chỉ tập trung vào việc hiển thị UI dựa trên props truyền vào, thay vì tự ý gọi API hay xử lý logic nghiệp vụ phức tạp.
Nhiều bạn viết một component <UserCard> rất đẹp, nhưng lại “nhét” luôn cả đoạn code gọi Fetch API gọi REST API bằng JavaScript để lấy thông tin user từ server ngay bên trong hàm onMounted của nó.
Đây là một cách thiết kế tồi. Component <UserCard> đó sẽ vĩnh viễn bị trói buộc với endpoint API kia. Bạn sẽ không thể mang nó sang một trang khác để hiển thị dữ liệu giả (mock data) hoặc dữ liệu từ một nguồn khác. Hãy tách việc gọi API ra component cha (hoặc store), sau đó truyền cục data đã lấy được xuống <UserCard> thông qua props.
Sai lầm 3: Đặt tên component và props một cách khó hiểu, gây hại cho đồng nghiệp
Tuân thủ quy tắc đặt tên multi-word (nhiều từ) và sử dụng tiền tố rõ ràng là nền tảng của một kiến trúc component bền vững, giúp tránh xung đột thẻ HTML.
Đừng bao giờ đặt tên file component là Button.vue hay Table.vue. Hãy đặt là BaseButton.vue hoặc AppTable.vue. Việc này giúp tránh xung đột với các thẻ HTML tiêu chuẩn hiện tại và cả những thẻ HTML mới có thể xuất hiện trong tương lai.
Đối với props, hãy dùng các tên mang ý nghĩa mô tả rõ ràng trạng thái. Ví dụ: sử dụng isLoading, isDisabled, hasError thay vì chỉ dùng loading, disabled, error. Những chi tiết nhỏ nhặt này đóng góp rất lớn vào sự thành công của dự án khi team có nhiều thành viên cùng làm việc.
Tổ chức và quản lý component hiệu quả cho dự án lớn
Để quản lý component Vue hiệu quả, bạn cần một chiến lược phân chia cấu trúc thư mục rõ ràng và khoa học ngay từ ngày đầu tiên khởi tạo dự án.
Khi ứng dụng lớn lên với hàng chục trang khác nhau, việc điều hướng mượt mà giữa các trang là bắt buộc. Bạn có thể tham khảo cách thiết lập Vue Router điều hướng SPA Vue.js để kết nối các page component với nhau một cách chuẩn xác. Ngoài ra, việc tổ chức component tốt cũng là một tiêu chí quan trọng khi các CTO đánh giá công nghệ. Nếu team bạn đang trong giai đoạn phân vân lựa chọn framework cho dự án mới, bài viết So sánh React vs Vue vs Angular 2026 sẽ cung cấp cái nhìn toàn cảnh về triết lý kiến trúc của từng bên, từ đó thấy được điểm mạnh của Vue trong việc tổ chức file SFC.
Cấu trúc thư mục component sao cho khoa học: Nhìn vào là hiểu, không cần hỏi ai
Phân chia thư mục theo mức độ tái sử dụng (như components/base, components/layout, components/features) là cách tổ chức component Vue tối ưu nhất, áp dụng tốt từ thời dùng Vue CLI cho đến các công cụ build hiện đại như Vite.
Mình thường áp dụng mô hình thiết kế Atomic Design hoặc chia theo tính năng (Feature-based).
- Thư mục
components/base/sẽ chứa các UI components thuần túy, dùng ở mọi nơi (BaseButton, BaseInput). - Thư mục
components/layout/chứa các khối cấu trúc trang (TheHeader, TheSidebar). - Thư mục
components/features/sẽ chứa các cụm component đặc thù cho từng chức năng nghiệp vụ (nhưUserProfileCard,ProductList).
Cách phân chia rạch ròi này giúp một developer mới gia nhập dự án có thể tìm thấy file cần sửa chỉ trong vòng 5 giây mà không cần nhờ ai “chỉ điểm”.
Xây dựng một thư viện component “base” dùng chung cho toàn dự án
Việc xây dựng thư viện component Vue nội bộ (UI Kit) giúp đảm bảo tính nhất quán tuyệt đối về mặt giao diện và tiết kiệm hàng trăm giờ code cho cả công ty.
Thay vì mỗi dự án mới (project A, project B) lại phải viết lại từ đầu một cái nút bấm, một cái popup, tại Phạm Hải, bọn mình đóng gói tất cả các component cơ bản thành một thư viện npm nội bộ.
Mọi dự án mới khởi tạo chỉ cần install thư viện này. Nếu có một bug phát sinh ở component BaseSelectBox, mình chỉ cần sửa code ở thư viện gốc. Sau đó, tất cả các dự án khác chỉ việc cập nhật version thư viện là lỗi tự động biến mất. Đây chính là đỉnh cao của sự tái sử dụng và tối ưu hóa nguồn lực.
Tóm lại, việc tạo component tái sử dụng không phải là kỹ thuật gì cao siêu, mà là một tư duy về cách tổ chức và chia nhỏ vấn đề. Hãy bắt đầu từ những component đơn giản nhất như Button, Input, áp dụng nhuần nhuyễn Props, Slots và Composition API. Tin mình đi, nỗ lực bỏ ra hôm nay sẽ cứu bạn khỏi vô số đêm OT (overtime) và những lời than phiền từ đồng đội trong tương lai. Bạn có “tuyệt chiêu” nào để tạo component Vue tái sử dụng hiệu quả không? Hãy chia sẻ ngay ở phần bình luận nhé, chúng ta cùng nhau học hỏi!
Lưu ý: Thông tin trong bài viết này chỉ mang tính chất tham khảo. Để có lời khuyên tốt nhất, vui lòng liên hệ trực tiếp với chúng tôi để được tư vấn cụ thể dựa trên nhu cầu thực tế của bạn.