Trong quá trình phát triển ứng dụng Vue.js, việc chia sẻ dữ liệu giữa các thành phần có thể trở nên phức tạp, đặc biệt là khi có sự lồng ghép sâu của các thành phần con bên trong các thành phần cha. Để giải quyết vấn đề này một cách linh hoạt và hiệu quả, Vue.js cung cấp một cặp tính năng mạnh mẽ gọi là provide inject.

Provide là một tính năng cho phép các thành phần cha cung cấp dữ liệu cho tất cả các thành phần con của chúng mà không cần phải truyền dữ liệu qua props. Thay vì đó, dữ liệu được "cung cấp" và tự động truy cập được bởi các thành phần con.

Inject là một tính năng cho phép các thành phần con "tiêm" dữ liệu từ thành phần cha của chúng, mà không cần phải truyền props xuống từ thành phần cha. Các thành phần con chỉ cần khai báo các khóa mà họ muốn tiêm và Vue.js sẽ tự động tìm kiếm và tiêm dữ liệu phù hợp từ các thành phần cha.

Với cặp tính năng này, việc chia sẻ dữ liệu giữa các thành phần trở nên dễ dàng hơn và giảm thiểu sự phức tạp trong quản lý trạng thái của ứng dụng Vue.js. Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách sử dụng provide và inject để xây dựng các ứng dụng Vue.js linh hoạt và dễ bảo trì.

Cách Dùng:

1. Provide:

  • provide là một thuộc tính của thành phần cha, được sử dụng để cung cấp dữ liệu cho tất cả các thành phần con của nó.
  • Bạn có thể cung cấp một đối tượng hoặc một hàm trả về một đối tượng.
// ParentComponent.vue
<script>
export default {
  provide() {
    return {
      sharedData: 'Dữ liệu chia sẻ từ cha'
    };
  }
}
</script>​

2. Inject:

  • inject là một thuộc tính của thành phần con, được sử dụng để chứa các khóa mà thành phần con muốn lấy từ thành phần cha.
  • Vue sẽ tự động tìm kiếm các khóa đã được cung cấp trong cây thành phần cha.
// ChildComponent.vue
<script>
export default {
  inject: ['sharedData'],
  mounted() {
    console.log(this.sharedData); // In ra "Dữ liệu chia sẻ từ cha"
  }
}
</script>

3. Inject function + event:

  • Khi người dùng sử dụng event để truyền dữ liệu lên parent cha và cập nhập dữ liệu đó.
  • Vue sẽ tự động tìm kiếm các hàm đã được cung cấp trong cây thành phần cha và thực thi chúng.
<!-- ChildComponent.vue -->
<template>
  <div>
    <button @click="selectTopic(10)">Trigger Event</button>
  </div>
</template>

<script>

export default {
  inject: ["selectTopic"]
}
</script>
// ParentComponent.vue
<script>
export default {
  provide() {
    return {
      sharedData: 'Dữ liệu chia sẻ từ cha',
      selectTopic: this.activateTopic
    };
  },
  methods: {
    activateTopic(topicId) {
      // xử lý data ở đây
    },
  },
}
</script>​

Ví dụ trong trường hợp bạn không sử dụng Provide và Inject chúng ta phải truyền qua từng props và emit trong tất cả component con để chuyền qua lại dữ liệu.

<!-- ParentComponent.vue -->

<template>
  <div>
    <active-element
      :topic-title="activeTopic && activeTopic.title"
      :text="activeTopic && activeTopic.fullText"
    ></active-element>
    <knowledge-base :topics="topics" @select-topic="activateTopic"></knowledge-base>
  </div>
</template>

<script>
export default {
  data() {
    return {
      topics: [
        {
          id: 'basics',
          title: 'The Basics',
          description: 'Core Vue basics you have to know',
          fullText:
            'Vue is a great framework and it has a couple of key concepts: Data binding, events, components and reactivity - that should tell you something!',
        },
        {
          id: 'components',
          title: 'Components',
          description:
            'Components are a core concept for building Vue UIs and apps',
          fullText:
            'With components, you can split logic (and markup) into separate building blocks and then combine those building blocks (and re-use them) to build powerful user interfaces.',
        },
      ],
      activeTopic: null,
    };
  },
  methods: {
    activateTopic(topicId) {
      this.activeTopic = this.topics.find((topic) => topic.id === topicId);
    },
  },
};
</script>
<!--  ChildComponent.vue (knowledge-base)  -->
<template>
  <section>
    <h2>Select a Topic</h2>
    <knowledge-grid :topics="topics" @select-topic="$emit('select-topic', $event)"></knowledge-grid>
  </section>
</template>

<script>
export default {
  props: ['topics'],
  emits: ['select-topic'],
};
</script>
<!--  ChildComponent.vue (knowledge-grid)  -->
<template>
  <ul>
    <knowledge-element
      v-for="topic in topics"
      :key="topic.id"
      :id="topic.id"
      :topic-name="topic.title"
      :description="topic.description"
      @select-topic="$emit('select-topic', $event)"
    ></knowledge-element>
  </ul>
</template>

<script>
export default {
  props: ['topics'],
  emits: ['select-topic']
};
</script>
<!--  ChildComponent.vue (knowledge-element)  -->
<template>
  <li>
    <h3>{{ topicName }}</h3>
    <p>{{ description }}</p>
    <button @click="$emit('select-topic', id)">Learn More</button>
  </li>
</template>

<script>
export default {
  props: ['id', 'topicName', 'description'],
  emits: ['select-topic'],
};
</script>

Nhìn ví dụ trên các bạn thấy khá là phức tạp đúng không nếu trong trường hợp mà nó còn có nhiều component con hơn nữa thì đúng là khó có thể kiểm soát được đúng không chính vì thế Provide và Inject đã giúp chúng ta xử lý vấn đề này một cách đơn giản hơn. 

Để sử dụng Provide và Inject chó ví dụ trên tà làm như sau:

<!-- ParentComponent.vue -->

<template>
  <div>
    <active-element
      :topic-title="activeTopic && activeTopic.title"
      :text="activeTopic && activeTopic.fullText"
    ></active-element>
    <knowledge-base :topics="topics" @select-topic="activateTopic"></knowledge-base>
  </div>
</template>

<script>
export default {
  data() {
    return {
      topics: {...}
      activeTopic: null,
    };
  },
  provide() {
    return {
      topics: this.topics,
      selectTopic: this.activateTopic
    }
  },
  methods: {
    activateTopic(topicId) {
      this.activeTopic = this.topics.find((topic) => topic.id === topicId);
    },
  },
};
</script>
<!--  ChildComponent.vue (knowledge-base)  -->
<template>
  <section>
    <h2>Select a Topic</h2>
    <knowledge-grid></knowledge-grid>
  </section>
</template>

<script>
export default {

};
</script>
<!--  ChildComponent.vue (knowledge-grid)  -->
<template>
  <ul>
    <knowledge-element
      v-for="topic in topics"
      :key="topic.id"
      :id="topic.id"
      :topic-name="topic.title"
      :description="topic.description"
    ></knowledge-element>
  </ul>
</template>

<script>
export default {
  inject: ['topics'],
};
</script>
<!--  ChildComponent.vue (knowledge-element)  -->
<template>
  <li>
    <h3>{{ topicName }}</h3>
    <p>{{ description }}</p>
    <button @click="selectTopic(id)">Learn More</button>
  </li>
</template>

<script>
export default {
  props: ['id', 'topicName', 'description'],
  inject: ['selectTopic']
};
</script>

Lưu Ý:

  • Khi sử dụng provide inject, hãy cẩn thận với việc quản lý trạng thái và đảm bảo rằng dữ liệu được chia sẻ theo cách an toàn và đúng đắn.
  • Nên sử dụng provide và inject cho những trường hợp cần thiết, như chia sẻ dữ liệu toàn cục hoặc cấu hình ứng dụng, nhưng hãy cân nhắc sử dụng props cho các trường hợp cần chia sẻ dữ liệu giữa các thành phần cụ thể.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Learning English Everyday