Vue.js 구성 요소 간 통신을 위한 디자인 패턴

WBOY
풀어 주다: 2023-09-02 11:45:11
원래의
1039명이 탐색했습니다.

개발자로서 우리는 관리 및 유지 관리가 가능한 코드를 생성하고 디버그 및 테스트도 더 쉽게 만들고 싶습니다. 이를 달성하기 위해 우리는 패턴이라는 모범 사례를 사용합니다. 패턴은 효율적이고 예측 가능한 방식으로 특정 작업을 수행하는 데 도움이 되는 입증된 알고리즘 및 아키텍처입니다.

이 튜토리얼에서는 가장 일반적인 Vue.js 구성 요소 통신 패턴과 우리가 피해야 할 몇 가지 함정을 살펴보겠습니다. 우리 모두는 실생활에서 모든 문제에 대한 단일 해결책이 없다는 것을 알고 있습니다. 마찬가지로 Vue.js 애플리케이션 개발에는 모든 프로그래밍 시나리오에 적용되는 범용 패턴이 없습니다. 각 모드에는 고유한 장점과 단점이 있으며 특정 사용 사례에 적합합니다.

Vue.js 개발자에게 가장 중요한 것은 주어진 프로젝트에 적합한 패턴을 선택할 수 있도록 가장 일반적인 패턴을 모두 이해하는 것입니다. 이를 통해 정확하고 효율적인 구성 요소 통신이 가능해집니다.

올바른 컴포넌트 통신이 왜 중요한가요?

Vue.js와 같은 구성 요소 기반 프레임워크를 사용하여 앱을 빌드할 때 우리의 목표는 앱의 구성 요소를 최대한 격리된 상태로 유지하는 것입니다. 이를 통해 재사용, 유지 관리 및 테스트가 가능해집니다. 구성 요소를 재사용 가능하게 만들려면 해당 구성 요소를 더 추상적이고 분리된(또는 느슨하게 결합된) 형태로 형성해야 합니다. 그러면 애플리케이션의 기능을 중단하지 않고 해당 구성 요소를 애플리케이션에 추가하거나 제거할 수 있습니다.

그러나 애플리케이션 구성 요소 간에 완전한 격리와 독립성을 달성할 수는 없습니다. 어떤 시점에서는 데이터 교환, 애플리케이션 상태 변경 등 서로 통신해야 합니다. 따라서 애플리케이션의 기능, 유연성 및 확장성을 유지하면서 이러한 통신을 올바르게 수행하는 방법을 배우는 것이 중요합니다.

Vue.js 구성요소 통신 개요

Vue.js에는 구성요소 간 통신의 두 가지 주요 유형이 있습니다.

  1. 엄격한 부모-자녀, 자식-부모 관계를 바탕으로 직접적인 부모-자녀 소통.
  2. 교차 구성 요소 통신, 하나의 구성 요소가 관계에 관계없이 다른 구성 요소와 "대화"할 수 있습니다.

다음 섹션에서는 적절한 예를 통해 두 가지 유형을 모두 살펴보겠습니다.

부모와 아이가 직접 소통

Vue.js가 기본적으로 지원하는 구성 요소 통신의 표준 모델은 소품과 사용자 정의 이벤트를 통해 구현되는 상위-하위 모델입니다. 아래 이미지에서 이 모델이 실제로 작동하는 모습을 시각적으로 볼 수 있습니다.

Vue.js 구성 요소 간 통신을 위한 디자인 패턴

보시다시피 부모는 직계 자식하고만 통신할 수 있고 자식은 부모하고만 직접 통신할 수 있습니다. 이 모델에서는 피어 또는 구성 요소 간 통신이 불가능합니다.

다음 섹션에서는 위 이미지의 구성 요소를 가져와 일련의 실제 예제로 구현해 보겠습니다.

부모와 자녀의 소통

우리가 가지고 있는 구성 요소가 게임의 일부라고 가정해 보겠습니다. 대부분의 게임은 인터페이스 어딘가에 게임 점수를 표시합니다. Parent A 구성 요소에서 score 변수를 선언하고 이를 Child A 구성 요소에 표시한다고 가정해 보겠습니다. 그러면 어떻게 해야 할까요?

부모에서 자식으로 데이터를 전송하기 위해 Vue.js는 props를 사용합니다. 속성을 전달하려면 세 가지 필수 단계가 필요합니다.

  1. 다음과 같이 자녀의 속성을 등록하세요. props: ["score"]
  2. 하위 템플릿에 등록된 속성을 다음과 같이 사용하세요. <span>分数:{{ Score }}</span>
  3. 다음과 같이 속성을 score 变量(在父级模板中),如下所示:<child-a :score="score"></child-a> 변수(상위 템플릿의)에 바인딩합니다. <child-a :score="score"></child-a>

실제로 무슨 일이 일어나고 있는지 더 잘 이해하기 위해 전체 예를 살펴보겠습니다.

으아악

CodePen 예제

인증 소품

간결함과 명확성을 위해 속기 변형을 사용하여 소품을 등록합니다. 하지만 실제 개발에서는 props를 검증하는 것이 좋습니다. 이렇게 하면 prop이 올바른 유형의 값을 받게 됩니다. 예를 들어 score 속성은 다음과 같이 확인할 수 있습니다.

으아악

props로 작업할 때는 리터럴 변형과 동적 변형의 차이점을 이해해야 합니다. prop을 변수에 바인딩하면 v-bind:score="score" 或其简写 :score="score") ,因此,prop 的值将根据变量的值而变化。如果我们只是输入一个没有绑定的值,那么该值将按字面意思解释,并且结果将是静态的。在我们的例子中,如果我们编写 score="score"100 대신 score가 표시됩니다. 이 미묘한 차이에 주의해야 합니다. 하위 속성 업데이트

지금까지 게임 점수를 성공적으로 표시했지만 어느 시점에서는 업데이트해야 합니다. 이것을 시도해 봅시다.

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, props: ["score"], methods: { changeScore() { this.score = 200; } } })

我们创建了一个 changeScore() 方法,该方法应该在我们按下更改分数按钮后更新分数。当我们这样做时,似乎分数已正确更新,但我们在控制台中收到以下 Vue 警告:

[Vue warn]:避免直接改变 prop,因为只要父组件重新渲染,该值就会被覆盖。相反,根据 prop 的值使用数据或计算属性。正在变异的道具:“score”

正如你所看到的,Vue 告诉我们,如果父级重新渲染,该 prop 将被覆盖。让我们通过使用内置 $forceUpdate() 方法模拟此类行为来测试这一点:

Vue.component('ParentA',{
  template:`
    <div id="parent-a">
      <h2>Parent A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, data() { return { score: 100 } }, methods: { reRender() { this.$forceUpdate(); } } })

CodePen 示例

现在,当我们更改分数,然后按重新渲染父级按钮时,我们可以看到分数从父级返回到其初始值。所以 Vue 说的是实话!

但请记住,数组和对象影响它们的父对象,因为它们不是被复制,而是通过引用传递。

因此,当我们需要改变子级中的 prop 时,有两种方法可以解决这种重新渲染的副作用。

使用本地数据属性改变 Prop

第一种方法是将 score 属性转换为本地数据属性 (localScore),我们可以在 changeScore( )方法和模板中:

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ localScore }} `, props: ["score"], data() { return { localScore: this.score } }, methods: { changeScore() { this.localScore = 200; } } })

CodePen 示例

现在,如果我们在更改分数后再次按渲染父项按钮,我们会看到这次分数保持不变。

使用计算属性改变 Prop

第二种方法是在计算属性中使用 score 属性,它将被转换为新值:

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ doubleScore }} `, props: ["score"], computed: { doubleScore() { return this.score * 2 } } })

CodePen 示例

在这里,我们创建了一个计算的 doubleScore(),它将父级的 score 乘以 2,然后将结果显示在模板中。显然,按渲染父级按钮不会产生任何副作用。

孩子与家长的沟通

现在,让我们看看组件如何以相反的方式进行通信。

我们刚刚了解了如何改变子组件中的某个 prop,但是如果我们需要在多个子组件中使用该 prop 该怎么办?在这种情况下,我们需要从父级中的源中改变 prop,这样所有使用该 prop 的组件都将被正确更新。为了满足这一要求,Vue 引入了自定义事件。

这里的原则是,我们通知父级我们想要做的更改,父级执行该更改,并且该更改通过传递的 prop 反映。以下是此操作的必要步骤:

  1. 在子进程中,我们发出一个事件来描述我们想要执行的更改,如下所示:this.$emit('updatingScore', 200)
  2. 在父级中,我们为发出的事件注册一个事件监听器,如下所示:@updatingScore="updateScore"
  3. 当事件发出时,分配的方法将更新属性,如下所示:this.score = newValue

让我们探索一个完整的示例,以更好地理解这是如何发生的:

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, props: ["score"], methods: { changeScore() { this.$emit('updatingScore', 200) // 1. Emitting } } }) ... Vue.component('ParentA',{ template:` <div id="parent-a"> <h2>Parent A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

// 2.Registering `, data() { return { score: 100 } }, methods: { reRender() { this.$forceUpdate() }, updateScore(newValue) { this.score = newValue // 3.Updating } } })

CodePen 示例

我们使用内置的 $emit() 方法来发出事件。该方法有两个参数。第一个参数是我们要发出的事件,第二个参数是新值。

.sync 修饰符

Vue 提供了 .sync 修饰符,其工作原理类似,在某些情况下我们可能希望将其用作快捷方式。在这种情况下,我们以稍微不同的方式使用 $emit() 方法。作为事件参数,我们将 update:score 如下所示:this.$emit('update:score', 200)。然后,当我们绑定 score 属性时,我们添加 .sync 修饰符,如下所示: <child-a :score.sync="score "/>.在 Parent A 组件中,我们删除了 updateScore() 方法和事件注册 (@updatingScore="updateScore"),因为它们不再需要了。

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, props: ["score"], methods: { changeScore() { this.$emit('update:score', 200) } } }) ... Vue.component('ParentA',{ template:` <div id="parent-a"> <h2>Parent A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, data() { return { score: 100 } }, methods: { reRender() { this.$forceUpdate() } } })

CodePen 示例

为什么不使用 this.$parentthis.$children 进行直接父子通信?

Vue 提供了两种 API 方法,使我们可以直接访问父组件和子组件:this.$parentthis.$children。一开始,可能很想将它们用作道具和事件的更快、更容易的替代品,但我们不应该这样做。这被认为是一种不好的做法或反模式,因为它在父组件和子组件之间形成了紧密耦合。后者会导致组件不灵活且易于损坏,难以调试和推理。这些 API 方法很少使用,根据经验,我们应该避免或谨慎使用它们。

双向组件通信

道具和事件是单向的。道具下降,事件上升。但是通过一起使用 props 和 events,我们可以在组件树上有效地进行上下通信,从而实现双向数据绑定。这实际上是 v-model 指令在内部执行的操作。

跨组件通信

随着我们的应用程序复杂性的增加,亲子沟通模式很快就会变得不方便且不切实际。 props-events 系统的问题在于它直接工作,并且与组件树紧密绑定。与原生事件相比,Vue 事件不会冒泡,这就是为什么我们需要重复发出它们直到达到目标。结果,我们的代码因过多的事件侦听器和发射器而变得臃肿。因此,在更复杂的应用程序中,我们应该考虑使用跨组件通信模式。

我们看一下下图:

Vue.js 구성 요소 간 통신을 위한 디자인 패턴

如您所见,在这种任意类型的通信中,每个组件都可以发送和/或接收数据来自任何其他组件,无需中间步骤和中间组件。

在以下部分中,我们将探讨跨组件通信的最常见实现。

全局事件总线

全局事件总线是一个 Vue 实例,我们用它来发出和监听事件。让我们在实践中看看它。

const eventBus = new Vue () // 1.Declaring

...

Vue.component('ChildA',{
  template:`
    <div id="child-a">
      <h2>Child A</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, props: ["score"], methods: { changeScore() { eventBus.$emit('updatingScore', 200) // 2.Emitting } } }) ... Vue.component('ParentA',{ template:` <div id="parent-a"> <h2>Parent A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, data() { return { score: 100 } }, created () { eventBus.$on('updatingScore', this.updateScore) // 3.Listening }, methods: { reRender() { this.$forceUpdate() }, updateScore(newValue) { this.score = newValue } } })

CodePen 示例

以下是创建和使用事件总线的步骤:

  1. 将我们的事件总线声明为一个新的 Vue 实例,如下所示:const eventBus = new Vue ()
  2. 从源组件发出事件,如下所示:eventBus.$emit('updatingScore', 200)
  3. 监听目标组件中发出的事件,如下所示:eventBus.$on('updatingScore', this.updateScore)

在上面的代码示例中,我们从子级中删除 @updatingScore="updateScore",并使用 created() 生命周期挂钩来监听对于 updatingScore 事件。当事件发出时,将执行 updateScore() 方法。我们还可以将更新方法作为匿名函数传递:

created () {
  eventBus.$on('updatingScore', newValue => {this.score = newValue})
}
로그인 후 복사

全局事件总线模式可以在一定程度上解决事件膨胀问题,但它会带来其他问题。可以从应用程序的任何部分更改应用程序的数据,而不会留下痕迹。这使得应用程序更难调试和测试。

对于更复杂的应用程序,事情可能很快就会失控,我们应该考虑专用的状态管理模式,例如 Vuex,它将为我们提供更细粒度的控制、更好的代码结构和组织以及有用的更改跟踪和调试功能。

Vuex

Vuex 是一个状态管理库,专为构建复杂且可扩展的 Vue.js 应用程序而定制。使用 Vuex 编写的代码更加冗长,但从长远来看这是值得的。它对应用程序中的所有组件使用集中存储,使我们的应用程序更有组织、透明且易于跟踪和调试。商店是完全响应式的,因此我们所做的更改会立即反映出来。

在这里,我将向您简要解释什么是 Vuex,并提供一个上下文示例。如果您想更深入地了解 Vuex,我建议您看一下我关于使用 Vuex 构建复杂应用程序的专门教程。

现在让我们研究一下下面的图表:

Vue.js 구성 요소 간 통신을 위한 디자인 패턴

如您所见,Vuex 应用程序由四个不同的部分组成:

  • 状态是我们保存应用程序数据的位置。
  • Getter 是访问存储状态并将其呈现给组件的方法。
  • 突变是实际且唯一允许改变状态的方法。
  • 操作是执行异步代码和触发突变的方法。

让我们创建一个简单的商店,看看这一切是如何运作的。

const store = new Vuex.Store({
  state: {
    score: 100
  },
  mutations: {
    incrementScore (state, payload) {
      state.score += payload
    }
  },
  getters: {
    score (state){
      return state.score
    }
  },
  actions: {
    incrementScoreAsync: ({commit}, payload) => {
      setTimeout(() => {
        commit('incrementScore', 100)
      }, payload)
    }
  }
})

Vue.component('ChildB',{
  template:`
    <div id="child-b">
      <h2>Child B</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, }) Vue.component('ChildA',{ template:` <div id="child-a"> <h2>Child A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, computed: { score () { return store.getters.score; } }, methods: { changeScore (){ store.commit('incrementScore', 100) } } }) Vue.component('ParentB',{ template:`

Parent B

data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }}
`, computed: { score () { return store.getters.score; } }, methods: { changeScore (){ store.dispatch('incrementScoreAsync', 3000); } } }) Vue.component('ParentA',{ template:` <div id="parent-a"> <h2>Parent A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, }) Vue.component('GrandParent',{ template:`

Grand Parent

data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, }) new Vue ({ el: '#app', })

CodePen 示例

在商店里,我们有以下产品:

  • 状态对象中设置的 score 变量。
  • incrementScore() 突变,它将按给定值增加分数。
  • 一个 score() getter,它将从状态访问 score 变量并将其呈现在组件中。
  • incrementScoreAsync() 操作,该操作将使用 incrementScore() 突变在给定时间段后增加分数。

在 Vue 实例中,我们不使用 props,而是使用计算属性通过 getter 来获取分数值。然后,为了更改分数,在 Child A 组件中,我们使用突变 store.commit('incrementScore', 100)。在Parent B组件中,我们使用操作 store.dispatch('incrementScoreAsync', 3000)

依赖注入

在结束之前,让我们探讨另一种模式。它的用例主要用于共享组件库和插件,但为了完整性值得一提。

依赖注入允许我们通过 provide 属性定义服务,该属性应该是一个对象或返回对象的函数,并使其可供组件的所有后代使用,而不仅仅是其组件直接孩子。然后,我们可以通过 inject 属性使用该服务。

让我们看看实际效果:

Vue.component('ChildB',{
  template:`
    <div id="child-b">
      <h2>Child B</h2>
      <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, inject: ['score'] }) Vue.component('ChildA',{ template:` <div id="child-a"> <h2>Child A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, inject: ['score'], }) Vue.component('ParentB',{ template:`

Parent B

data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }}
`, inject: ['score'] }) Vue.component('ParentA',{ template:` <div id="parent-a"> <h2>Parent A</h2> <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Score: {{ score }} `, inject: ['score'], methods: { reRender() { this.$forceUpdate() } } }) Vue.component('GrandParent',{ template:`

Grand Parent

data {{ this.$data }}
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

`, provide: function () { return { score: 100 } } }) new Vue ({ el: '#app', })

CodePen 示例

通过使用祖父组件中的 provide 选项,我们将 score 变量设置为可供其所有后代使用。他们每个人都可以通过声明 inject: ['score'] 属性来访问它。而且,正如您所看到的,分数显示在所有组件中。

注意:依赖注入创建的绑定不是反应性的。因此,如果我们希望提供程序组件中所做的更改反映在其后代中,我们必须将一个对象分配给数据属性并在提供的服务中使用该对象。

为什么不使用 this.$root 进行跨组件通信?

我们不应该使用 this.$root 的原因与之前描述的 this.$parentthis.$children 的原因类似——它创建了太多的依赖关系。必须避免依赖任何这些方法进行组件通信。

如何选择正确的模式

所以你已经了解了组件通信的所有常用方法。但您如何决定哪一个最适合您的场景呢?

选择正确的模式取决于您参与的项目或您想要构建的应用程序。这取决于您的应用程序的复杂性和类型。让我们探讨一下最常见的场景:

  • 简单应用中,道具和事件将满足您的所有需求。
  • 中端应用将需要更灵活的通信方式,例如事件总线和依赖项注入。
  • 对于复杂的大型应用,您肯定需要 Vuex 作为功能齐全的状态管理系统的强大功能。

最后一件事。您不必仅仅因为别人告诉您这样做就需要使用任何已探索的模式。您可以自由选择和使用您想要的任何模式,只要您设法保持应用程序正常运行并且易于维护和扩展。

结论

在本教程中,我们学习了最常见的 Vue.js 组件通信模式。我们了解了如何在实践中实施它们以及如何选择最适合我们项目的正确方案。这将确保我们构建的应用程序使用正确类型的组件通信,使其完全正常工作、可维护、可测试和可扩展。

위 내용은 Vue.js 구성 요소 간 통신을 위한 디자인 패턴의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!