Cách dùng Provide/Inject để tránh Prop Drilling trong Vue

Giảm code soạn sẵn và làm app Vue của bạn dễ bảo trì hơn bằng cách sử dụng giải pháp thay thế tiện lợi này.

Lập trình Vue

Vue cung cấp một số cách quản lý luồng dữ liệu và giao tiếp giữa các thành phần. Một thử thách phổ biến với lập trình viên Vue là prop drilling, nơi bạn chuyển dữ liệu qua nhiều lớp thành phần khác nhau, dẫn tới một code base phức tạp, khó bảo trì hơn.

Vue cung cấp cơ chế provide/inject, một giải pháp gọn gàng cho prop drilling. Provide/inject giúp quản lý giao tiếp dữ liệu giữa các thành phần cha và con được lồng sâu với nhau.

Prop drilling là gì?

Trước khi đi sâu vào giải pháp provide/inject, hiểu vấn đề rất quan trọng. Prop drilling xảy ra khi bạn cần chuyển dữ liệu từ thành phần cha cấp cao nhất xuống thành phần con được lồng sâu.

Các thành phần trung gian trong hệ thống phân cấp này cần nhận và truyền dữ liệu, ngay cả khi bản thân chúng không sử dụng dữ liệu đó. Để chuyển dữ liệu từ thành phần cha tới thành phần con, bạn sẽ cần chuyển những dữ liệu này dưới dạng thuộc tín (props) tới các thành phần Vue.

Ví dụ cách phân cấp thành phần như sau:

  • App
    • ParentComponent
      • ChildComponent
        • GrandChildComponent

Giả sử dữ liệu từ thành phần App cần tới GrandChildComponent. Trong trường hợp đó, bạn cần chuyển nó qua 2 thành phần trung gian bằng props, ngay cả khi những thành phần này không cần tới dữ liệu đó để chạy đúng chức năng. Điều đó khiến code trở nên cồng kềnh, khó gỡ lỗi hơn.

Provide/Inject là gì?

Vue xử lý lỗi này bằng tính năng provide/inject, cho phép một thành phần cha cung cấp dữ liệu hoặc hàm tới thành phần con của nó mà không cần quan tâm tới cấp độ lồng nhau. Giải pháp này đơn giản hóa chia sẻ dữ liệu và cải thiện cách sắp xếp code.

Thành phần Provider

Một thành phần provider hướng tới chia sẻ dữ liệu hay phương thức xử lý các thành phần con của nó. Nó dùng tùy chọn provide để làm dữ liệu này có sẵn cho thành phần con. Đây là một ví dụ về thành phần provider:

<!-- App.vue -->
<template>
  <div>
    <!-- ... -->
    <ParentComponent/>
  </div>
</template>

<script setup>
import { provide } from 'vue';
import ParentComponent from './components/ParentComponent.vue';

const greeting = 'Hello from Provider';

provide('greeting', greeting);
</script>

Khối code này hiện một thành phần provider, App, cung cấp một biến greeting cho tất cả thành phần con của nó. Để cung cấp một biến, bạn cần đặt một key. Đặt key cùng tên với biến giúp bạn có thể bảo trì code.

Các thành phần con

Các thành phần con là những phần tử nằm trong một cấu trúc lồng nhau. Chúng có thể chèn và dùng dữ liệu được cung cấp trong phiên bản thành phần của chúng. Đây là cách nó được thực hiện:

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

const injectedData = inject('greeting');
</script>

Thành phần con chèn dữ liệu được cung cấp và có thể truy cập nó trong mẫu dưới dạng biến đã được xác định cục bộ:

Giờ hãy quan sát hình ảnh bên dưới:

Ảnh kết quả bên dưới

Ở ảnh này, bạn có thể thấy một hệ thống phân cấp bao gồm 4 thành phần, bắt đầu với thành phần gốc làm điểm khởi đầu. Các thành phần khác lồng vào bên trong hệ thống phân cấp, kết thúc ở thành phần GrandChild.

Thành phần GrandChild nhận dữ liệu mà App cung cấp. Với cơ chế này, bạn có thể tránh truyền dữ liệu qua thành phần ParentChild, vì những thành phần đó không cần dữ liệu để chạy đúng chức năng.

Cung cấp dữ liệu ở cấp độ App (Global)

Bạn có thể cung cấp dữ liệu ở cấp độ app với provide/inject của Vue. Đây là trường hợp sử dụng phổ biến để chia sẻ dữ liệu và cấu hình trên các thành phần khác nhau trong app Vue.

Ví dụ về cách bạn có thể cung cấp dữ liệu ở cấp độ ứng dụng:

// main.js

import { createApp } from 'vue'
import App from './App.vue'

const globalConfig = {
  apiUrl: 'https://example.com/api',
  authKey: 'my-secret-key',
  // Other configuration settings...
};

app.provide('globalConfig', globalConfig);

createApp(App).mount('#app')

Giả sử bạn có ứng dụng cần một đối tượng cấu hình toàn cục chứa các endpoint API, thông tin xác thực người dùng và cài đặt khác.

Bạn có thể đạt được điều này bằng cách cung cấp dữ liệu cấu hình ở thành phần cấp cao nhất, thường là trong tệp main.js, cho phép các thành phần khác chèn và sử dụng nó:

<template>
  <div>
    <h1>API Settings</h1>
    <p>API URL: {{ globalConfig.apiUrl }}</p>
    <p>Authentication Key: {{ globalConfig.authKey }}</p>
  </div>
</template>

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

const globalConfig = inject('globalConfig');
</script>

Thành phần trên dùng hàm inject để truy cập đối tượng globalConfig, app này cung cấp ở cấp độ toàn cục. Bạn có thể truy cập bất kỳ thuộc tính hay cài đặt từ globalConfig bằng cách nội suy hoặc liên kết những thuộc tính này với các kỹ thuật liên kết dự án khác trong Vue.

Lợi ích khi dùng Provide và Inject

  • Code gọn gàng và được tối ưu hóa hiệu suất tốt hơn.
  • Cải thiện đóng gói thành phần.
  • Chèn thành phần phụ thuộc.

Những điều cần cân nhắc khi chèn Provide và Inject

Dù mang tới nhiều lợi ích nhưng bạn nên dùng hai thành phần trên cẩn thận để tránh các tác dụng phụ không mong muốn.

  • Dùng provide/inject để chia sẻ dữ liệu quan trọng hoặc các hàm cần thiết trên hệ thống phân cấp như cấu hình hoặc key API. Sử dụng nó quá nhiều có thể khiến các mối quan hệ thành phần trở nên phức tạp.
  • Ghi lại những gì thành phần provider cung cấp và thành phần con chèn vào. Hành động này hỗ trợ bạn hiểu và bảo trì các thành phần, nhất là khi làm việc theo nhóm.
  • Hãy thận trọng khi tạo các vòng lặp phụ thuộc, nơi thành phần con cung cấp dữ liệu mà một thành phần gốc chèn vào. Điều này dẫn tới lỗi và hành vi đáng ngờ.
Thứ Hai, 23/10/2023 10:52
51 👨 233
0 Bình luận
Sắp xếp theo