Skip to content

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.

Props được khai báo bằng tùy chọn props:

js
export default {
  props: ['foo'],
  created() {
    // props được truy cập qua `this`
    console.log(this.foo)
  }
}

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
export default {
  props: {
    title: String,
    likes: Number
  }
}
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'
js
export default {
  props: ['foo'],
  created() {
    // ❌ cảnh báo, props chỉ đọc!
    this.foo = 'bar'
  }
}

Thường có hai trường hợp nơi mà rất cảm tempting để biến đổ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 như là giá trị khởi tạo của nó:

    js
    const 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)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
          // counter chỉ sử dụng this.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.
          counter: this.initialCounter
        }
      }
    }
  2. 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:

    js
    const 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())
    js
    export default {
      props: ['size'],
      computed: {
        // thuộc tính tính toán tự động cập nhật khi prop thay đổi
        normalizedSize() {
          return this.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
})
js
export default {
  props: {
    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
export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'My Journey with Vue'
      }
    }
  }
}
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() macroprops option, 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.

js
export default {
  props: {
    // 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'
      }
    }
  }
}

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ành false. Bạn có thể thay đổi điều này bằng cách đặt default 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 }}.

Lưu ý

Lưu ý rằng props được kiểm tra trước khi một thể hiện thành phần được tạo, do đó các thuộc tính thể hiện (ví dụ: data, computed, v.v.) sẽ không có sẵn trong các hàm default hoặc validator.

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
})
js
export default {
  props: {
    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
})
js
export default {
  props: {
    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ả StringBoolean đề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]
})
js
// disabled sẽ được ép kiểu thành true
export default {
  props: {
    disabled: [Boolean, Number]
  }
}

// disabled sẽ được ép kiểu thành true
export default {
  props: {
    disabled: [Boolean, String]
  }
}

// disabled sẽ được ép kiểu thành true
export default {
  props: {
    disabled: [Number, Boolean]
  }
}

// disabled sẽ được hiểu là chuỗi rỗng (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}
Props has loaded