Appearance
Props
Khai báo Props
Các thành phần Vue yêu cầu phải có khai báo props rõ ràng để Vue biết rằng những props bên ngoài được truyền vào thành phần nên được xử lý như các thuộc tính chuyển đổi (sẽ được thảo luận trong phần chuyên sâu của nó).
Trong các Single File Components (SFC) sử dụng <script setup>
, props có thể được khai báo bằng cách sử dụng macro defineProps()
:
vue
<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
:
js
export default {
props: ['foo'],
setup(props) {
// setup() nhận props làm đối số đầu tiên.
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 này.
Ngoài việc khai báo props bằng một mảng chuỗi, chúng ta cũng có thể sử dụng cú pháp đối tượng:
js
// trong <script setup>
defineProps({
title: String,
likes: Number
})
js
// trong không sử dụng <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 đối tượng, khóa 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òn cảnh báo cho những 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 loại sai. Chúng ta sẽ thảo luận thêm chi tiết về kiểm tra props phía dưới trang này.
Nếu bạn đang sử dụng TypeScript với <script setup>
, cũng có thể khai báo props bằng chính loại thông tin:
vue
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
Chi tiết hơn: Xác định kiểu cho Props của Thành phần
Luồng Dữ liệu Một Chiều
Tất cả props tạo thành một liên kết 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ập nhật, nó sẽ truyền xuống cho con, nhưng không có cách nào ngược lại. Điều này ngăn chặn các thành phần con từ việc tình cờ biến đổ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 biến đổi một prop bên trong một thành phần con. Nếu làm vậy, Vue sẽ cảnh báo bạn trong bảng điều khiển:
js
const props = defineProps(['foo'])
// ❌ cảnh báo, props chỉ đọc!
props.foo = 'bar'
Thường có hai trường hợp nơi mà rất cảm tempting để biến đổi một prop:
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 như là giá trị khởi tạo của nó:
jsconst props = defineProps(['initialCounter']) // counter chỉ sử dụng props.initialCounter như là giá trị khởi tạo; // nó không liên quan đến các cập nhật prop sau này. const counter = ref(props.initialCounter)
Prop được truyền vào dưới dạng giá trị nguyên thủy 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:
jsconst props = defineProps(['size']) // thuộc tính tính toán tự động cập nhật khi prop thay đổi const normalizedSize = computed(() => props.size.trim().toLowerCase())
Biến Đổi Props Là Đối Tượng / Mảng
Khi các đối tượng và mảng được truyền vào như là props, trong khi thành phần con không thể biến đổi liên kết prop, nó vẫn có thể biến đổi các thuộc tính lồng nhau của đối tượng hoặc mảng. Điều này là do trong JavaScript, đối tượng và mảng được truyền bằng tham chiếu, và nó là không hợp lý để Vue ngăn chặn các biến đổi như vậy.
Nhược điểm chính của các biến đổ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 đối với thành phần cha, có thể làm cho quá trình suy luận về luồng dữ liệu trở nên khó khăn hơn trong tương lai. Làm thế nào là một quy tắc hay, bạn nên tránh những biến đổi như vậy trừ khi cha và con chặt chẽ kết nối bởi thiết kế. Trong hầu hết các trường hợp, con nên kích hoạt sự kiện để cho phép cha thực hiện biến đổi.
Chi Tiết Truyền Props
Đặt Tên Props
Chúng ta khai báo tên props dài bằng camelCase vì điều này giúp tránh việc 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 truy cập trực tiếp chúng trong biểu thức mẫu vì chúng là các định danh JavaScript hợp lệ:
js
defineProps({
greetingMessage: String
})
template
<span>{{ greetingMessage }}</span>
Kỹ thuật lập trình sự kiện nào cũng có thể sử dụng camelCase khi truyền props vào một thành phần con (ngoại trừ trong in-DOM templates). 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 thuộc tính HTML:
template
<MyComponent greeting-message="hello" />
Chúng ta 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 rõ ràng giữa các thành phần Vue và các phần tử tự nhiên. Tuy nhiên, không có nhiều lợi ích thực tế khi sử dụng camelCase khi truyền props, vì vậy chúng ta chọn theo quy ước của từng ngôn ngữ.
Props Tĩnh và Động
Đến nay, bạn đã thấy props được truyền dưới dạng giá trị tĩnh, như trong:
template
<BlogPost title="My journey with Vue" />
Bạn cũng đã thấy props được gán động bằng v-bind
hoặc :
ngắn gọn của nó, như trong:
template
<!-- Gán động giá trị của một biến -->
<BlogPost :title="post.title" />
<!-- Gán động giá trị của một biểu thức phức tạp -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
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 giá trị chuỗi, nhưng bất kỳ loại giá trị nào cũng có thể được truyền vào một prop.
Số
template
<!-- Ngay cả khi `42` là tĩnh, chúng ta cần v-bind để cho Vue biết -->
<!-- rằng đây là một biểu thức JavaScript thay vì một chuỗi. -->
<BlogPost :likes="42" />
<!-- Gán động giá trị của một biến. -->
<BlogPost :likes="post.likes" />
Boolean
template
<!-- Bao gồm prop mà không có giá trị sẽ ngụ ý `true`. -->
<BlogPost is-published />
<!-- Ngay cả khi `false` là tĩnh, chúng ta cần v-bind để cho Vue biết -->
<!-- rằng đây là một biểu thức JavaScript thay vì một chuỗi. -->
<BlogPost :is-published="false" />
<!-- Gán động giá trị của một biến. -->
<BlogPost :is-published="post.isPublished" />
Mảng
template
<!-- Ngay cả khi mảng là tĩnh, chúng ta cần v-bind để cho Vue biết -->
<!-- rằng đây là một biểu thức
JavaScript thay vì một chuỗi. -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Gán động giá trị của một biến. -->
<BlogPost :comment-ids="post.commentIds" />
Đối Tượng
template
<!-- Ngay cả khi đối tượng là tĩnh, chúng ta cần v-bind để cho Vue biết -->
<!-- rằng đây là một biểu thức JavaScript thay vì một chuỗi. -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Gán động giá trị của một biến. -->
<BlogPost :author="post.author" />
Ràng Buộc Nhiều Thuộc Tính Sử Dụ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 làm props, bạn có thể sử dụng v-bind
mà không có đối số (v-bind
thay vì :prop-name
). Ví dụ, với một đối tượng post
:
js
const post = {
id: 1,
title: 'My Journey with Vue'
}
Mẫu sau:
template
<BlogPost v-bind="post" />
Sẽ tương đương với:
template
<BlogPost :id="post.id" :title="post.title" />
Kiểm Tra Props
Các thành phần có thể chỉ định yêu cầu cho props của mình, như các loại bạn đã thấy trước đó. Nếu 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 được thiết kế để được sử dụng bởi người khác.
Để chỉ định kiểm tra props, bạn có thể cung cấp một đối tượng với các yêu cầu kiểm tra cho defineProps()
macro , thay vì một mảng chuỗi. Ví dụ:
js
defineProps({
// Kiểm tra kiểu cơ bản
// (`null` và giá trị `undefined` sẽ cho phép bất kỳ kiểu nào)
propA: Number,
// Nhiều kiểu có thể
propB: [String, Number],
// Chuỗi bắt buộc
propC: {
type: String,
required: true
},
// Số với giá trị mặc định
propD: {
type: Number,
default: 100
},
// Đối tượng với giá trị mặc định
propE: {
type: Object,
// Giá trị mặc định của đối tượng hoặc mảng phải được trả về từ
// một hàm factory. Hàm này nhận giá trị props thô
// nhận được bởi thành phần làm đối số.
default(rawProps) {
return { message: 'hello' }
}
},
// Hàm kiểm tra tùy chỉnh
// props đầy đủ được chuyển như là đối số thứ hai trong 3.4+
propF: {
validator(value, props) {
// Giá trị phải phù hợp với một trong những chuỗi này
return ['success', 'warning', 'danger'].includes(value)
}
},
// Hàm với giá trị mặc định
propG: {
type: Function,
// Không giống như giá trị mặc định của đối tượng hoặc mảng, đây không phải là một hàm factory
// - đây là một hàm để phục vụ như một giá trị mặc định
default() {
return 'Hàm mặc định'
}
}
})
TIP
Mã trong đối số của defineProps()
không thể truy cập các biến khác được khai báo trong <script setup>
, vì toàn bộ biểu thức được chuyển đến phạm vi hàm bên ngoài khi được biên dịch.
Chi tiết bổ sung:
Tất cả các props mặc định là tùy chọn, trừ khi
required: true
được chỉ định.Một prop tùy chọn vắng mặt ngoại trừ
Boolean
sẽ có giá trịundefined
.Các props vắng mặt của kiểu
Boolean
sẽ được chuyển thànhfalse
. Bạn có thể thay đổi điều này bằng cách đặtdefault
cho nó - ví dụ:default: undefined
để hoạt động như một prop không phải kiểu Boolean.Nếu giá trị
default
được chỉ định, nó sẽ được sử dụng nếu giá trị prop đã giải quyết làundefined
- điều này bao gồm cả khi prop không xuất hiện hoặc giá trịundefined
được
truyền rõ ràng.
Khi kiểm tra prop thất bại, Vue sẽ tạo ra một cảnh báo trong bảng điều khiển (nếu sử dụng phiên bản phát triển).
Nếu sử dụng Khai báo kiểu dựa trên loại , Vue sẽ cố gắng hết sức để biên dịch các chú thích kiểu thành các khai báo prop runtime tương đương. Ví dụ, defineProps<{ msg: string }>
sẽ được biên dịch thành { msg: { type: String, required: true }}
.
Kiểm Tra Loại Runtime
type
có thể là một trong các hàm xây dựng cơ bản sau đây:
String
Number
Boolean
Array
Object
Date
Function
Symbol
Ngoài ra, type
cũng có thể là một lớp tùy chỉnh hoặc một hàm xây dựng và sự khẳng định sẽ được thực hiện với kiểm tra instanceof
. Ví dụ, với lớp sau:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
Bạn có thể sử dụng nó làm kiểu của prop:
js
defineProps({
author: Person
})
Vue sẽ sử dụng instanceof Person
để xác minh xem giá trị của prop author
có thực sự là một thể hiện của lớp Person
không.
Ép Kiểu Sang Boolean
Props với kiểu Boolean
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 bản. Cho một <MyComponent>
với khai báo sau:
js
defineProps({
disabled: Boolean
})
Thành phần có thể được sử dụng như sau:
template
<!-- tương đương với việc truyền :disabled="true" -->
<MyComponent disabled />
<!-- tương đương với việc truyền :disabled="false" -->
<MyComponent />
Khi một prop được khai báo để cho phép nhiều loại, 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ả String
và Boolean
đều được chấp nhận - quy tắc ép kiểu Boolean chỉ áp dụng nếu Boolean xuất hiện trước String:
js
// disabled sẽ được ép kiểu thành true
defineProps({
disabled: [Boolean, Number]
})
// disabled sẽ được ép kiểu thành true
defineProps({
disabled: [Boolean, String]
})
// disabled sẽ được ép kiểu thành true
defineProps({
disabled: [Number, Boolean]
})
// disabled sẽ được hiểu là chuỗi rỗng (disabled="")
defineProps({
disabled: [String, Boolean]
})