正確的方式來實作元件解耦
P粉476475551
P粉476475551 2023-09-14 10:57:11
0
1
477

我正在處理兩個元件之間的通訊(使用Vue 2),其中一個是按鈕,可以具有初始、載入和完成(成功或失敗)等狀態,對於按鈕的每個狀態,我可能會顯示不同的文本,不同的圖標(加載:旋轉圖標,成功完成:勾號,錯誤完成:x),我還有一個表單,將使用按鈕組件。我不確定如何根據表單提交的當前狀態來更改按鈕的狀態。請查看下面的程式碼。

我的按鈕元件:

<template>
 <button
    class="ui-button"
    @click="clicked"
    :data-status-type="status_type"
    :disabled="is_disabled"
    :type="type"
  >
    <i :class="icon" v-if="is_disabled || concluded"></i>
    {{ title }}
  </button>         
</template>

<script>
export default {
  props: {
    title: {
      type: String,
    },
    type: {
      default: "button",
      type: String,
    },
  },
  data() {
    return {
      concluded: false,
      icon: "fa fa-spin ",
      is_disabled: false,
      status_type: "success",
    };
  },
  methods: {
    clicked() {
      if (!this.is_disabled) {
        this.$emit(
          "clicked",
          () => {
            this.is_disabled = true;
            this.icon = "fa fa-spin fas fa-spinner";
          },
          (succeeded) => {
            this.is_disabled = false;
            this.concluded = true;
            this.icon = succeeded ? "fas fa-check" : "fas fa-xmark";
            this.status_type = succeeded ? "success" : "error";
            setTimeout(() => {
              this.concluded = false;
              this.icon = "";
              this.status_type = "";
            }, 1500);
          }
        );
      }
    },
  },
};
</script>

我的表單元件:

<template>
  <div>
    <ThePages :parents="accompaniments">
       <!--  ... some reactive stuff  -->
      <template #extra_button>
        <TheButton @clicked="sendItemToCart" :title="button_text" :disabled="button_disabled" />
      </template>
    </ThePages>
  </div>
</template>

<script>
import axios from 'axios'
import FormatHelper from '../helpers/FormatHelper'
import SwalHelper from '../helpers/SwalHelper'
import TheButton from './TheButton.vue'
import ThePages from './ThePages.vue'
import TheQuantityPicker from './TheQuantityPicker.vue'

export default {
  props: ['product'],
  components: {
    TheButton,
    ThePages,
    TheQuantityPicker,
  },
  data() {
    return {
      accompaniments: this.product.accompaniment_categories,
      button_text: '',
      button_disabled: false,
      format_helper: FormatHelper.toBRCurrency,
      observation: '',
      quantity: 1,
      success: false,
    }
  },
  created() {
    this.addQuantityPropToAccompaniments()
    this.availability()
  },
  methods: {
    // ... some other methods
    async sendItemToCart(startLoading, concludedSuccessfully) {
      startLoading()  // This will change the button state
      this.button_text = 'Adicionando...'
      await axios
        .post(route('cart.add'), {
          accompaniments: this.buildAccompanimentsArray(),
          id: this.product.id,
          quantity: this.quantity,
          observation: this.observation,
        })
        .then(() => {
          concludedSuccessfully(true)  // This will change the button state
          this.button_text = 'Adicionado'
          SwalHelper.productAddedSuccessfully()
        })
        .catch((error) => {
          concludedSuccessfully(false)  // This will change the button state
          if (
            error?.response?.data?.message ==
            'Este produto atingiu a quantidade máxima para este pedido.'
          ) {
            SwalHelper.genericError(error?.response?.data?.message)
          } else {
            SwalHelper.genericError()
          }
          this.button_text = 'Adicionar ao carrinho'
        })
    },
  },
}
</script>

在上面的程式碼中,您可以看到我如何根據表單的狀態來更改按鈕的狀態:我的按鈕在點擊時發出兩個函數(startLoading,concludedSuccessfully),然後我在sendItemToCart中使用這兩個函數。

這似乎將兩個元件耦合得有點太多了,因為我必須將這些函數作為參數傳遞給父元件的方法。另外,我對如何做到這一點還有另一個想法,那就是給每個按鈕一個ref,然後在父元件中使用ref呼叫其方法。這個想法聽起來有點像物件導向程式設計中的“組合而非繼承”,我只需要求物件/元件做某事,但在這種情況下,沒有將函數作為參數。

嗯,上面的兩種情況似乎比我可能擁有的每個按鈕都創建變數更好,但它們似乎可以改進。所以我正在尋找的是:如何更好地解耦我的組件?

P粉476475551
P粉476475551

全部回覆(1)
P粉023650014

如果我們討論的是Vue 3(你沒有指定Vue的版本,所以不確定是Vue 2),你可能正在尋找provide/inject

https://vuejs.org/guide/components/provide-inject.html

所以,如果你有一個父元件和一堆子元件,這些子元件只能作為父元件的後代出現(例如表單和輸入方塊),你可以provide表單的狀態:

OP評論說按鈕也可能出​​現在其他地方,所以我們應該同時使用props和provide。在沒有表單的情況下,我們可以使用props作為預設的注入值。

在表單元件中:

<script setup>

import {reactive, provide} from 'vue';

const form = reactive({button_disabled: false, button_text: '', sendItemToCart});
provide('form', form);

function sendItemToCart(){
  // your logic here
}

</script>

<template>
  <div>
    <ThePages :parents="accompaniments">
       <!--  ... some reactive stuff  -->
      <template #extra_button>
        <TheButton />
      </template>
    </ThePages>
  </div>
</template>

在按鈕元件中:

<script setup>

import {inject} from 'vue';

const props = defineProps('button_disabled button_text'.split(' '));
const form = inject('form', props); // use either provide of props

</setup>

<template>
 <button
    class="ui-button"
    @click="() => form.sendItemToCart ? form.sendItemToCart() : $emit('clicked')"
    :data-status-type="status_type"
    :disabled="form.button_disabled"
    :type="type"
  >
    <i :class="icon" v-if="form.button_disabled || concluded"></i>
    {{ form.button_text }}
  </button>         
</template>

將程式碼調整為Options API。

使用Vue 2進行更新

OP更正了答案,使用的是Vue 2。所以...

幸運的是,Vue 2也支援provide/inject!唯一的問題是如何使provide具有響應性,我猜在這裡得到了解決:

How do I make Vue 2 Provide / Inject API reactive?

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!