Skip to content

Provide / Inject

Truyền Thông Tin Bằng Props

Thường, khi chúng ta cần truyền dữ liệu từ 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 mà 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 thông tin 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:

sơ đồ truyền thông tin bằng props

Chú ý rằng mặc dù thành phần <Footer> có thể không quan tâm đến những prop này chút nào, nhưng nó vẫn cần khai báo và truyền chúng đi chỉ để <DeepChild> có thể truy cập. Nếu có một chuỗi cha dài hơn, nhiều thành phần khác nhau sẽ bị ảnh hưởng trên đường đi. Điều này được gọi là "truyền thông tin bằng props" 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 đề truyền thông tin bằng props bằng cách sử dụng provideinject. Một thành phần cha có thể là một nhà cung cấp phụ thuộc cho tất cả con cháu của nó. Bất kỳ thành phần nào trong cây con, bất kể có sâu đến đâu, đều có thể chèn (inject) các phụ thuộc được cung cấp bởi các thành phần ở trên nó trong chuỗi cha.

Giản đồ Provide/Inject

Provide

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

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

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

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

js
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 tiêm (injection key), có thể là một chuỗi hoặc một Symbol. Khóa tiêm được sử dụng bởi các thành phần con để tìm giá trị mong muốn để chèn. Một thành phần duy nhất có thể gọi provide() nhiều lần với các khóa tiêm chèn 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ỳ kiểu nào, bao gồm cả trạng thái phản ứng như refs:

js
import { ref, provide } from 'vue'

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

Việc cung cấp 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.

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

js
export default {
  provide: {
    message: 'hello!'
  }
}

Đối với mỗi thuộc tính trong đối tượng provide, khóa được sử dụng bởi các thành phần con để xác định giá trị chính xác cần chèn, trong khi giá trị là cái được chèn.

Nếu chúng ta cần cung cấp trạng thái theo phiên bản, ví dụ dữ liệu được khai báo qua data(), thì provide phải sử dụng giá trị hàm:

js
export default {
  data() {
    return {
      message: 'hello!'
    }
  },
  provide() {
    // sử dụng cú pháp hàm để chúng ta có thể truy cập `this`
    return {
      message: this.message
    }
  }
}

Tuy nhiên, hãy chú ý rằng điều này không làm cho việc chèn trở nên phản ứng. Chúng ta sẽ thảo luận về cách làm cho chèn trở nên phản ứng ở dưới.

Cung Cấp Ở Cấp Ứ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 ở cấp ứng dụng:

js
import { createApp } from 'vue'

const app = createApp({})

app.provide(/* key */ '

message', /* value */ 'hello!')

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

Chèn Dữ Liệu

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

vue
<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 chèn như vậy và không tự động được mở gói. Điều này cho phép thành phần chèn giữ kết nối phản ứng với thành phần cung cấp.

Ví dụ đầy đủ về provide + inject với Reactivity

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():

js
import { inject } from 'vue'

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

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

js
export default {
  inject: ['message'],
  created() {
    console.log(this.message) // giá trị được chèn
  }
}

Các chèn đượ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 chèn trong data():

js
export default {
  inject: ['message'],
  data() {
    return {
      // dữ liệu ban đầu dựa trên giá trị được chèn
      fullMessage: this.message
    }
  }
}

[Ví dụ đầy đủ về provide + inject](https://play.vuejs.org/#eNqNkcFqwzAQRH9l0EUthOhuRKH00FO/oO7B2JtERZaEvA4F43+vZCdOTAIJCImRdpi32kG8h7A99iQKobs6msBvpTNt8JHxcTC2wS76FnKrJpVLZelKR39TSUO7qreMoXRA7ZPPkeOuwHByj5v8EqI/moZeXudCIBL30G0VNBS11VeyogD2StTwvldbZH0Bv0pBvKi3FIKlvFGcxEJ8r7nPWLlm3h5e6ik9yrrU/MFR8cdYrRno2xrk88/77KInS8xR8T8yt37jqD+aVebA7qHsFb+fPz3H8Xy

Đặt Tên Chèn

Khi sử dụng cú pháp mảng cho inject, các thuộc tính được chèn được hiển thị trên thể hiện thành phần bằ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 chèn là this.message. Khóa cục bộ là giống như khóa chèn.

Nếu chúng ta muốn chèn thuộc tính bằng cách sử dụng một khóa cục bộ khác, chúng ta cần sử dụng cú pháp đối tượng cho tùy chọn inject:

js
export default {
  inject: {
    /* khóa cục bộ */ localMessage: {
      from: /* khóa chèn */ 'message'
    }
  }
}

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

Giá Trị Mặc Định Cho Chèn

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

Nếu chúng ta muốn làm cho thuộc tính được chèn 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:

js
// `value` sẽ là "default value"
// nếu không có dữ liệu nào khớp với "message" được cung cấp
const value = inject('message', 'default value')

Trong m

ột số trường hợp, giá trị mặc định có thể cần đượ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 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 nhà máy để tạo giá trị mặc định:

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

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

js
export default {
  // cú pháp đối tượng là bắt buộc
  // khi khai báo giá trị mặc định cho chèn
  inject: {
    message: {
      from: 'message', // điều này là tùy chọn nếu sử dụng cùng một khóa cho chèn
      default: 'default value'
    },
    user: {
      // sử dụng một hàm nhà máy cho các giá trị không nguyên thủy mà tốn kém
      // để tạo ra, hoặc những giá trị nên là duy nhất cho mỗi thể hiện thành phần.
      default: () => ({ name: 'John' })
    }
  }
}

Làm Việc với Phản Ứng

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

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

vue
<!-- bên trong thành phần cung cấp -->
<script setup>
import { provide, ref } from 'vue'

const location = ref('Bắc Cực')

function updateLocation() {
  location.value = 'Nam Cực'
}

provide('location', {
  location,
  updateLocation
})
</script>
vue
<!-- trong thành phần chèn -->
<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ể 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ị biến đổi bởi thành phần chèn.

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

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

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

js
import { computed } from 'vue'

export default {
  data() {
    return {
      message: 'Xin chào!'
    }
  },
  provide() {
    return {
      // cung cấp một thuộc tính tính toán một cách rõ ràng
      message: computed(() => this.message)
    }
  }
}

Ví dụ đầy đủ về provide + inject với Phản Ứng

Hàm computed() thường được sử dụng trong các thành phần Composition API, nhưng cũng có thể được sử dụng để bổ sung một số trường hợp sử dụng cụ thể trong Options API. Bạn có thể tìm hiểu thêm về cách sử dụng nó bằng cách đọc Cơ Bản về Phản ỨngThuộc Tính Tính Toán với Ưu Tiên API được thiết lập thành Composition API.

Làm Việc với Khóa Symbol

Cho đến nay, chúng ta đã sử dụng các khóa chèn 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 người cung cấp phụ thuộc, hoặc bạn đang tạo thành phần mà sẽ được sử dụng bởi các nhà phát triển khác, thì việc sử dụng các khóa chèn Symbol là tốt nhất để tránh xung đột tiềm ẩn.

Nên khuyến nghị xuất các Symbols trong một tệp riêng:

js
// keys.js
export const myInjectionKey = Symbol()
js
// trong thành phần cung cấp
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'

provide(myInjectionKey, {
  /* dữ liệu cung cấp */
})
js
// trong thành phần chèn
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'

const injected = inject(myInjectionKey)

Xem thêm: Gõ Provide / Inject

js
// trong thành phần cung cấp
import { myInjectionKey } from './keys.js'

export default {
  provide() {
    return {
      [myInjectionKey]: {
        /* dữ liệu cung cấp */
      }
    }
  }
}
js
// trong thành phần chèn
import { myInjectionKey } from './keys.js'

export default {
  inject: {
    injected: { from: myInjectionKey }
  }
}
Provide / Inject has loaded