Chào mừng đến với Cafedev! Trong loạt bài viết này, chúng ta sẽ khám phá về Props trong Vue.js. Props là một khái niệm quan trọng khi làm việc với các thành phần trong Vue, giúp chúng ta truyền dữ liệu từ thành phần cha sang con. Hãy cùng tìm hiểu chi tiết về Props và cách sử dụng chúng để tạo ra các ứng dụng Vue mạnh mẽ và linh hoạt hơn!

Trang này giả định rằng bạn đã đọc hiểu Cơ bản về Các thành phần. Đọc trước nếu bạn mới làm quen với các thành phần.

1. Khai báo Props

Các thành phần Vue yêu cầu phải khai báo props một cách rõ ràng để Vue biết những props bên ngoài được truyền vào thành phần sẽ được xử lý như các thuộc tính fallthrough (sẽ được thảo luận trong phần riêng biệt của nó).

Trong các SFC sử dụng <script setup>, props có thể được khai báo bằng cách sử dụng macro defineProps().

<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>

Trong các thành phần không sử dụng <script setup>, props được khai báo bằng cách sử dụng tùy chọn props:

export default {
  props: ['foo'],
  setup(props) {
    // setup() receives props as the first argument.
    console.log(props.foo)
  }
}

Lưu ý rằng đối số được truyền vào defineProps() giống với giá trị được cung cấp cho tùy chọn props: cùng một API tùy chọn props được chia sẻ giữa hai kiểu khai báo.

Ngoài việc khai báo props bằng cách sử dụng một mảng các chuỗi, chúng ta cũng có thể sử dụng cú pháp object:

// in <script setup>
defineProps({
  title: String,
  likes: Number
})
// in non-<script setup>
export default {
  props: {
    title: String,
    likes: Number
  }
}

Đối với mỗi thuộc tính trong cú pháp khai báo object, key là tên của prop, trong khi giá trị nên là hàm constructor của loại dữ liệu mong đợi.
Điều này không chỉ tài liệu hóa thành phần của bạn, mà cũng sẽ cảnh báo các nhà phát triển khác sử dụng thành phần của bạn trong bảng điều khiển trình duyệt nếu họ truyền sai loại dữ liệu. Chúng tôi sẽ thảo luận thêm về xác nhận prop ở phần dưới của trang.


Nếu bạn đang sử dụng TypeScript với <script setup>, cũng có thể khai báo props bằng các chú thích kiểu thuần túy:

<script setup lang="ts">
defineProps<{
  title?: string
  likes?: number
}>()
</script>

Thêm chi tiết: Kiểu các prop của thành phần

2. Chi Tiết Truyền Props

2.1 Kiểu Chữ của Props

Chúng tôi khai báo tên prop dài bằng cách sử dụng camelCase vì điều này tránh phải sử dụng dấu ngoặc kép khi sử dụng chúng làm khóa thuộc tính và cho phép chúng ta tham chiếu trực tiếp trong biểu thức mẫu vì chúng là các định danh JavaScript hợp lệ:

defineProps({
  greetingMessage: String
})
<span>{{ greetingMessage }}</span>

Kỹ thuật, bạn cũng có thể sử dụng camelCase khi truyền props cho một thành phần con (trừ trong mẫu trong DOM). Tuy nhiên, quy ước là sử dụng kebab-case trong tất cả các trường hợp để phù hợp với các thuộc tính HTML:

<MyComponent greeting-message="hello" />

Chúng tôi sử dụng PascalCase cho các thẻ thành phần khi có thể vì nó cải thiện tính đọc của mẫu bằng cách phân biệt các thành phần Vue với các phần tử nguyên mẫu. Tuy nhiên, không có nhiều lợi ích thực tiễn trong việc sử dụng camelCase khi truyền props, vì vậy chúng tôi chọn tuân theo quy ước của mỗi ngôn ngữ.

2.2 Props Tĩnh vs. Động

Cho đến nay, bạn đã thấy props được truyền như các giá trị tĩnh, như trong:

<BlogPost title="My journey with Vue" />

Bạn cũng đã thấy props được gán động với v-bind hoặc cách viết tắt : của nó, như trong:

<!-- Dynamically assign the value of a variable -->
<BlogPost :title="post.title" />

<!-- Dynamically assign the value of a complex expression -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

2.3 Truyền Các Loại Giá Trị Khác Nhau

Trong hai ví dụ trên, chúng ta tình cờ truyền các giá trị chuỗi, nhưng bất kỳ loại giá trị nào cũng có thể được truyền cho một prop.

2.4 Số

<!-- Even though `42` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.       -->
<BlogPost :likes="42" />

<!-- Dynamically assign to the value of a variable. -->
<BlogPost :likes="post.likes" />

2.5 Boolean

<!-- Including the prop with no value will imply `true`. -->
<BlogPost is-published />

<!-- Even though `false` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.          -->
<BlogPost :is-published="false" />

<!-- Dynamically assign to the value of a variable. -->
<BlogPost :is-published="post.isPublished" />

2.6 Mảng

<!-- Even though the array is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.            -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- Dynamically assign to the value of a variable. -->
<BlogPost :comment-ids="post.commentIds" />

2.7 Đối tượng

<!-- Even though the object is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.             -->
<BlogPost
  :author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
 />

<!-- Dynamically assign to the value of a variable. -->
<BlogPost :author="post.author" />

2.8 Ràng Buộc Nhiều Thuộc Tính Bằng Một Đối Tượng

Nếu bạn muốn truyền tất cả các thuộc tính của một đối tượng như các props, bạn có thể sử dụng v-bind mà không có đối số (v-bind thay vì :prop-name). Ví dụ, cho một đối tượng post:

const post = {
  id: 1,
  title: 'My Journey with Vue'
}

Mẫu sau:

<BlogPost v-bind="post" />

Sẽ tương đương với:

<BlogPost :id="post.id" :title="post.title" />

3. Luồng Dữ Liệu Một Chiều

Tất cả props tạo thành một ràng buộc một chiều từ trên xuống giữa thuộc tính con và thuộc tính cha: khi thuộc tính cha được cập nhật, nó sẽ lan tỏa xuống con, nhưng không ngược lại. Điều này ngăn các thành phần con vô tình thay đổi trạng thái của cha, điều này có thể làm cho luồng dữ liệu của ứng dụng của bạn khó hiểu hơn.

Ngoài ra, mỗi khi thành phần cha được cập nhật, tất cả props trong thành phần con sẽ được làm mới với giá trị mới nhất. Điều này có nghĩa là bạn không nên cố gắng thay đổi một prop bên trong một thành phần con. Nếu bạn làm như vậy, Vue sẽ cảnh báo bạn trong bảng điều khiển:

const props = defineProps(['foo'])

// ❌ warning, props are readonly!
props.foo = 'bar'

Thông thường có hai trường hợp khiến bạn muốn thay đổi một prop:
1. Prop được sử dụng để truyền vào một giá trị ban đầu; thành phần con muốn sử dụng nó như một thuộc tính dữ liệu cục bộ sau đó. Trong trường hợp này, tốt nhất là xác định một thuộc tính dữ liệu cục bộ sử dụng prop làm giá trị ban đầu:

const props = defineProps(['initialCounter'])

   // counter only uses props.initialCounter as the initial value;
   // it is disconnected from future prop updates.
   const counter = ref(props.initialCounter)

2. Prop được truyền vào dưới dạng một giá trị thô cần được biến đổi. Trong trường hợp này, tốt nhất là xác định một thuộc tính tính toán sử dụng giá trị của prop:

const props = defineProps(['size'])

   // computed property that auto-updates when the prop changes
   const normalizedSize = computed(() => props.size.trim().toLowerCase())

3.1 Thay Đổi Props Đối Tượng / Mảng

Khi các đối tượng và mảng được truyền làm props, trong khi thành phần con không thể thay đổi ràng buộc của prop, nó sẽ có thể thay đổi các thuộc tính lồng trong đối tượng hoặc mảng. Điều này là vì trong JavaScript, các đối tượng và mảng được truyền theo tham chiếu, và việc ngăn Vue ngăn chặn các thay đổi như vậy là không hợp lý.

Hạn chế chính của các thay đổi như vậy là nó cho phép thành phần con ảnh hưởng đến trạng thái của cha một cách không rõ ràng với cha, có thể làm cho việc suy luận về luồng dữ liệu trong tương lai trở nên khó khăn hơn. Là một thực hành tốt, bạn nên tránh các thay đổi như vậy trừ khi cha và con được liên kết chặt chẽ theo thiết kế. Trong hầu hết các trường hợp con nên kích hoạt một sự kiện để cho phép cha thực hiện thay đổi.

4. Xác nhận Props

Các thành phần có thể chỉ định yêu cầu cho props của họ, như các loại bạn đã thấy trước đó. Nếu một yêu cầu không được đáp ứng, Vue sẽ cảnh báo bạn trong bảng điều khiển JavaScript của trình duyệt. Điều này đặc biệt hữu ích khi phát triển một thành phần dự định được sử dụng bởi người khác.

Để chỉ định xác nhận props, bạn có thể cung cấp một đối tượng với yêu cầu xác nhận cho macro defineProps()props option, thay vì một mảng chuỗi. Ví dụ:

defineProps({
  // Basic type check
  //  (`null` and `undefined` values will allow any type)
  propA: Number,
  // Multiple possible types
  propB: [String, Number],
  // Required string
  propC: {
    type: String,
    required: true
  },
  // Number with a default value
  propD: {
    type: Number,
    default: 100
  },
  // Object with a default value
  propE: {
    type: Object,
    // Object or array defaults must be returned from
    // a factory function. The function receives the raw
    // props received by the component as the argument.
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // Custom validator function
  // full props passed as 2nd argument in 3.4+
  propF: {
    validator(value, props) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // Function with a default value
  propG: {
    type: Function,
    // Unlike object or array default, this is not a factory 
    // function - this is a function to serve as a default value
    default() {
      return 'Default function'
    }
  }
})

Thêm thông tin:
– Tất cả props mặc định là tùy chọn, trừ khi required: true được chỉ định.
– Một prop tùy chọn bị thiếu ngoại trừ Boolean sẽ có giá trị undefined.
– Các props Boolean bị thiếu sẽ được chuyển đổi thành false. Bạn có thể thay đổi điều này bằng cách đặt một mặc định cho nó — ví dụ: mặc định: undefined để hoạt động như một prop không phải là Boolean.
– Nếu một giá trị mặc định được chỉ định, nó sẽ được sử dụng nếu giá trị prop được giải quyết là undefined – điều này bao gồm cả khi prop bị thiếu, hoặc một giá trị undefined rõ ràng được truyền.

Khi xác nhận prop thất bại, Vue sẽ tạo ra một cảnh báo trên console (nếu sử dụng phiên bản phát triển).

Nếu sử dụng Các khai báo props dựa trên loại , Vue sẽ cố gắng biên dịch các chú thích loại thành các khai báo prop tương đương vào thời gian chạy. Ví dụ, defineProps<{ msg: string }> sẽ được biên dịch thành { msg: { type: String, required: true }}.

tip Lưu ý rằng props được xác nhận trước khi một phiên bản thành phần được tạo, vì vậy các thuộc tính của phiên bản (ví dụ: defineProps<{ msg: string }> sẽ không có sẵn trong các hàm mặc định hoặc validator.

4.1 Kiểm tra kiểu trong Thời Gian Chạy

loại/kiểu có thể là một trong các hàm tạo nguyên thủy sau:
String
Number
Boolean
Array
Object
Date
Function
Symbol
Error

Ngoài ra, loại cũng có thể là một lớp tùy chỉnh hoặc hàm tạo và phát biểu sẽ được thực hiện với kiểm tra instanceof. Ví dụ, với lớp sau đây:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

Bạn có thể sử dụng nó như một loại prop:

defineProps({
  author: Person
})

Vue sẽ sử dụng instanceof Person để xác nhận xem giá trị của prop author thực sự là một phiên bản của lớp Person.

5. Ép kiểu Boolean

Props với kiểu Boolean có các quy tắc ép kiểu đặc biệt để mô phỏng hành vi của các thuộc tính boolean nguyên thủy. Với có khai báo sau:

defineProps({
  disabled: Boolean
})

Có thể sử dụng thành phần như sau:

<!-- equivalent of passing :disabled="true" -->
<MyComponent disabled />

<!-- equivalent of passing :disabled="false" -->
<MyComponent />

Khi một prop được khai báo để cho phép nhiều loại, các quy tắc ép kiểu cho Boolean cũng sẽ được áp dụng. Tuy nhiên, có một trường hợp đặc biệt khi cả StringBoolean được cho phép – quy tắc ép kiểu Boolean chỉ được áp dụng nếu Boolean xuất hiện trước String:

// disabled will be casted to true
defineProps({
  disabled: [Boolean, Number]
})
  
// disabled will be casted to true
defineProps({
  disabled: [Boolean, String]
})
  
// disabled will be casted to true
defineProps({
  disabled: [Number, Boolean]
})
  
// disabled will be parsed as an empty string (disabled="")
defineProps({
  disabled: [String, Boolean]
})

Trên đây là một cái nhìn tổng quan về Props trong Vue.js, một khía cạnh quan trọng trong phát triển ứng dụng Vue. Bằng cách hiểu và sử dụng Props một cách hiệu quả, chúng ta có thể tạo ra các thành phần linh hoạt và tái sử dụng được trong ứng dụng của mình. Hy vọng rằng thông tin này sẽ giúp bạn nắm bắt được cách sử dụng Props và tận dụng được tiềm năng của Vue.js trong quá trình phát triển phần mềm. Hãy tiếp tục theo dõi các bài viết tiếp theo trên Cafedev để cập nhật thêm kiến thức hữu ích!

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Vuejs từ cơ bản tới nâng cao

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!