Chào mừng đến với Cafedev! Trong chủ đề Vuejs with Provide / Inject, chúng ta sẽ tìm hiểu về cách sử dụng `provide` và `inject` để chia sẻ dữ liệu giữa các thành phần trong ứng dụng Vue của bạn một cách linh hoạt và hiệu quả. Thay vì truyền dữ liệu qua lại giữa các thành phần con và cha thông qua props, chúng ta có thể tận dụng provide / inject để giảm thiểu sự phụ thuộc và làm cho mã của chúng ta trở nên dễ bảo trì hơn. Hãy cùng khám phá chi tiết hơn về cách sử dụng tính năng này!

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

1. Truyền Props

Thông thường, khi chúng ta cần truyền dữ liệu từ thành phần cha đến một thành phần con, chúng ta sử dụng props. Tuy nhiên, hãy tưởng tượng trường hợp chúng ta có một cây thành phần lớn, và một thành phần lồng sâu cần một cái gì đó từ một thành phần tổ tiên xa xôi. Chỉ với props, chúng ta sẽ phải truyền cùng một prop qua toàn bộ chuỗi cha:

Lưu ý rằng mặc dù thành phần <Footer> có thể không quan tâm tới những props này hoàn toàn, nhưng vẫn cần phải khai báo và truyền chúng đi chỉ để <DeepChild> có thể truy cập. Nếu chuỗi cha dài hơn, nhiều thành phần khác sẽ bị ảnh hưởng trong quá trình này. Điều này được gọi là props drilling và chắc chắn không phải là điều dễ chịu để xử lý.

Chúng ta có thể giải quyết vấn đề props drilling bằng cách sử dụng provideinject. Một thành phần cha có thể đóng vai trò là một nhà cung cấp phụ thuộc cho tất cả các thành phần con của nó. Bất kỳ thành phần nào trong cây con cháu, bất kể nó lồng sâu đến đâu, đều có thể inject các phụ thuộc được cung cấp bởi các thành phần ở trên trong chuỗi cha của nó.

2. Cung cấp(Provide)

Để cung cấp dữ liệu cho các thành phần con của một thành phần, sử dụng hàm provide():

<script setup>
import { provide } from 'vue'

provide(/* key */ 'message', /* value */ 'hello!')
</script>

Nếu không sử dụng <script setup>, đảm bảo rằng provide() được gọi đồng bộ bên trong setup().

import { provide } from 'vue'

export default {
  setup() {
    provide(/* key */ 'message', /* value */ 'hello!')
  }
}

Hàm provide() chấp nhận hai đối số. Đối số đầu tiên được gọi là khóa inject, có thể là một chuỗi hoặc một Symbol. Khóa inject được sử dụng bởi các thành phần con để tìm giá trị mong muốn để inject. Một thành phần duy nhất có thể gọi provide() nhiều lần với các khóa inject khác nhau để cung cấp các giá trị khác nhau.
Đối số thứ hai là giá trị được cung cấp. Giá trị có thể là bất kỳ loại nào, bao gồm cả trạng thái phản ứng như refs:

import { ref, provide } from 'vue'

const count = ref(0)
provide('key', count)

Việc cung cấp các giá trị phản ứng cho phép các thành phần con sử dụng giá trị được cung cấp thiết lập một kết nối phản ứng với thành phần cung cấp.

2. Cung cấp ở Mức ứng dụng

Ngoài việc cung cấp dữ liệu trong một thành phần, chúng ta cũng có thể cung cấp ở mức ứng dụng:

import { createApp } from 'vue'

const app = createApp({})

app.provide(/* key */ 'message', /* value */ 'hello!')

Cung cấp ở mức ứng dụng có sẵn cho tất cả các thành phần được render trong ứng dụng. Điều này đặc biệt hữu ích khi viết plugins, vì các plugin thông thường sẽ không thể cung cấp các giá trị bằng cách sử dụng các thành phần.

3. Inject

Để inject dữ liệu được cung cấp bởi một thành phần tổ tiên, sử dụng hàm inject():

<script setup>
import { inject } from 'vue'

const message = inject('message')
</script>

Nếu giá trị được cung cấp là một ref, nó sẽ được inject như vậy và không tự động được mở ra. Điều này cho phép thành phần injector giữ kết nối phản ứng với thành phần cung cấp.
Ví dụ provider + inject đầy đủ với tính phản ứng

Một lần nữa, nếu không sử dụng <script setup>, inject() chỉ nên được gọi đồng bộ bên trong setup().

import { inject } from 'vue'

export default {
  setup() {
    const message = inject('message')
    return { message }
  }
}

Các injections được giải quyết trước trạng thái riêng của thành phần, vì vậy bạn có thể truy cập các thuộc tính được inject trong data():

Ví dụ cung cấp + inject đầy đủ

3.1 Đặt tên tùy chọn cho Injection

Khi sử dụng cú pháp mảng cho inject, các thuộc tính được inject được tiết lộ trên phiên bản thành phần sử dụng cùng một khóa. Trong ví dụ trên, thuộc tính được cung cấp dưới khóa "message", và được inject như this.message. Khóa cục bộ giống như khóa inject.
Nếu chúng ta muốn inject thuộc tính bằng một khóa cục bộ khác nhau, chúng ta cần sử dụng cú pháp đối tượng cho tùy chọn inject:

export default {
  inject: {
    /* local key */ localMessage: {
      from: /* injection key */ 'message'
    }
  }
}

Ở đây, thành phần sẽ định vị một thuộc tính được cung cấp với khóa "message", và sau đó tiết lộ nó như this.localMessage.

3.2 Giá trị mặc định cho Injection

Theo mặc định, inject giả định rằng khóa được inject được cung cấp ở đâu đó trong chuỗi cha. Trong trường hợp khóa không được cung cấp, sẽ có một cảnh báo thời gian chạy.

Nếu chúng ta muốn làm cho một thuộc tính được inject hoạt động với các nhà cung cấp tùy chọn, chúng ta cần khai báo một giá trị mặc định, tương tự như props:

// `value` will be "default value"
// if no data matching "message" was provided
const value = inject('message', 'default value')

Trong một số trường hợp, giá trị mặc định có thể cần phải được tạo bằng cách gọi một hàm hoặc khởi tạo một lớp mới. Để tránh tính toán không cần thiết hoặc các tác động phụ trong trường hợp giá trị tùy chọn không được sử dụng, chúng ta có thể sử dụng một hàm factory để tạo ra giá trị mặc định:

const value = inject('key', () => new ExpensiveClass(), true)

Tham số thứ ba chỉ ra rằng giá trị mặc định nên được xem như một hàm factory.

4. Làm việc với Phản ứng

Khi sử dụng các giá trị cung cấp / inject phản ứng, nên giữ bất kỳ thay đổi nào đối với trạng thái phản ứng bên trong provider nếu có thể. Điều này đảm bảo rằng trạng thái được cung cấp và các thay đổi có thể của nó được đặt cạnh nhau trong cùng một thành phần, làm cho việc duy trì trong tương lai dễ dàng hơn.

Có thể có những lúc khi chúng ta cần cập nhật dữ liệu từ một thành phần injector. Trong những trường hợp như vậy, chúng tôi khuyến nghị cung cấp một hàm chịu trách nhiệm cho việc thay đổi trạng thái:


<!-- inside provider component -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>

<!-- in injector component -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>
<template>
  <button @click="updateLocation">{{ location }}</button>
</template>

Cuối cùng, bạn có thể bao bọc giá trị được cung cấp bằng readonly() nếu bạn muốn đảm bảo rằng dữ liệu được truyền qua provide không thể bị thay đổi bởi thành phần injector.

<script setup>
import { ref, provide, readonly } from 'vue'

const count = ref(0)
provide('read-only-count', readonly(count))
</script>

Để làm cho các injections liên kết phản ứng với provider, chúng ta cần cung cấp một thuộc tính tính toán bằng cách sử dụng hàm computed():


import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    return {
      // explicitly provide a computed property
      message: computed(() => this.message)
    }
  }
}

Ví dụ cung cấp + inject đầy đủ với tính phản ứng
Hàm computed() thường được sử dụng trong các thành phần API Hợp thành, nhưng cũng có thể được sử dụng để bổ sung cho một số trường hợp sử dụng cụ thể trong API Tùy chọn. Bạn có thể tìm hiểu thêm về cách sử dụng của nó bằng cách đọc Cơ bản về Phản ứngThuộc tính tính toán với API Preference được đặt thành API Hợp thành.

5. Làm việc với Key Symbol

Cho đến nay, chúng ta đã sử dụng các key injection dưới dạng chuỗi trong các ví dụ. Nếu bạn đang làm việc trong một ứng dụng lớn với nhiều nhà cung cấp phụ thuộc, hoặc bạn đang tạo ra các thành phần sẽ được sử dụng bởi các nhà phát triển khác, thì tốt nhất là sử dụng key injection dưới dạng Symbol để tránh xung đột tiềm ẩn.
Đề xuất xuất Symbol trong một tệp riêng:

// keys.js
export const myInjectionKey = Symbol()
// in provider component
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* data to provide */
})
// in injector component
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

Trên đây là một cái nhìn tổng quan về cách sử dụng `provide` và `inject` trong Vue.js để chia sẻ dữ liệu giữa các thành phần. Hy vọng rằng bài viết này đã giúp bạn hiểu rõ hơn về tính năng này và cách áp dụng nó trong các dự án của bạn trên nền tảng Vue.js. Đừng quên theo dõi Cafedev để cập nhật thêm nhiều kiến thức và bài viết thú vị khác về lập trình và công nghệ!

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!