Vue 折騰記 - (18) 用Vue的Inject Provide結合Event Bus來實現區域性的狀態維護

CRPER發表於2019-04-18

前言

原型有個東西,看著是幾個功能元件的組合體;

想拆分成對應的元件(全部寫在一起是賊恐怖的事情),又不想用Vuex這類來實現。

那最終的方案就是Vueeventbus


效果圖

只展示部分功能,實際原型要複雜的多;

Vue 折騰記 - (18) 用Vue的Inject Provide結合Event Bus來實現區域性的狀態維護

原型大體是這樣的

Vue 折騰記 - (18) 用Vue的Inject Provide結合Event Bus來實現區域性的狀態維護


實現原理

其實就是各個元件獨立維護自己的狀態,元件的預設值從外部傳入;

而內部通過watchimmediate立即觸發複製一份到data,

watch data回撥$emit,而對於聚攏所有資料,我們就用event bus來實現;

如何區域性狀態化,就用到了inject provide了,在當前元件下provide,該分支的所有子元件都能inject;

ng有這個概念,reactcontext也是差不多的玩意


程式碼參考

依舊如前兩篇文章,基於antd design vue來實現的,當然還有部分自定義元件是自己封裝的

所以呢,看看用法就好,一般來說你們跑步起來

  • eventbus.js
import Vue from 'vue';
export const eventBus = new Vue();
複製程式碼
  • BasicSetting.vue(父元件)

記得在元件生命週期銷燬!!這是個好習慣!!!

<template>
  <a-card :bodyStyle="{ position: 'relative' }">
    <template #extra>
      <btn-popconfirm
        size="default"
        :text="isEdit ? '關閉編輯' : '開啟編輯'"
        :message="`確定要${isEdit ? '關閉編輯' : '開啟編輯'}續期配置?`"
        @change="onEdit"
      />
      <btn-popconfirm
        size="default"
        type="primary"
        text="確定配置"
        :disabled="!isEdit"
        :message="`確定要更新配置?操作需謹慎!`"
        @change="onUpdate"
      />
    </template>

    <div class="basic-setting">
      <a-col v-bind="{ xs: 24, sm: 24, md: 24, lg: 24, xxl: 6 }">
        <pivot-card :defaultValue="pivotData" :bordered="false" />
      </a-col>
      <a-col v-bind="{ xs: 24, sm: 24, md: 24, lg: 24, xxl: 18 }">
        <product-item />
      </a-col>
    </div>
    <div class="overlay" v-if="!isEdit" />
  </a-card>
</template>

<script>
import PivotCard from './PivotCard';
import ProductItem from './ProductItem';
import { eventBus } from '@/utils/eventBus';
export default {
  name: 'BasicSetting',
  provide: function() {
    return {
      bus: eventBus
    };
  },
  components: {
    PivotCard,
    ProductItem
  },
  created() {
    eventBus.$on('pivot', this.getPivotData);
    eventBus.$on('productItem', this.getProductItemData);
  },
  beforeDestroy() {
    eventBus.$off('pivot');
  },
  data() {
    return {
      isEdit: false, // 是否開啟編輯
      pivotData: {
        // 基準資訊
        minMoney: 200, // 最低金額
        maxMoney: 4000, // 最高金額
        defaultAmount: 2000 // 預設額度
      }
    };
  },
  methods: {
    onEdit(e) {
      // 開啟關閉編輯
      if (e) {
        this.isEdit = !this.isEdit;
      }
    },
    onUpdate(e) {
      // 更新提交
    },
    getPivotData(e) {
      console.log('我是基準表單的值回撥: ', JSON.stringify(e));
      // 獲取基準資訊的回撥
    },
    getProductItemData(e) {
      console.log('我是產品項的值回撥: ', JSON.stringify(e));
    }
  }
};
</script>

<style lang="scss" scoped>
.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(230, 229, 229, 0.24);
  z-index: 999;
}
</style>


複製程式碼
  • PivotCard.vue子元件
<template>
  <a-card>
    <template #title>
      最低金額、最高金額、預設額度
    </template>
    <a-row type="flex" justify="start" align="middle" style="margin:10px 0;">
      <a-col :sm="24" :md="10">
        <span style="padding:5px 0">最低金額</span>
      </a-col>
      <a-col :sm="24" :md="14">
        <a-input-number
          :min="0"
          v-model="fields.minMoney"
          :formatter="value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
          :parser="value => value.replace(/\¥\s?|(,*)/g, '')"
        />
      </a-col>
    </a-row>
    <a-row type="flex" justify="start" align="middle" style="margin:10px 0;">
      <a-col :sm="24" :md="10"> <span style="padding:5px 0">最高金額</span></a-col>
      <a-col :sm="24" :md="14">
        <a-input-number
          :min="0"
          :formatter="value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
          :parser="value => value.replace(/\¥\s?|(,*)/g, '')"
          v-model="fields.maxMoney"
        />
      </a-col>
    </a-row>
    <a-row type="flex" justify="start" align="middle" style="margin:10px 0;">
      <a-col :sm="24" :md="10"> <span style="padding:5px 0">預設額度</span></a-col>
      <a-col :sm="24" :md="14">
        <a-input-number
          :min="0"
          :max="100"
          v-model="fields.defaultAmount"
          :formatter="value => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
          :parser="value => value.replace(/\¥\s?|(,*)/g, '')"
        />
      </a-col>
    </a-row>
  </a-card>
</template>

<script>
export default {
  inject: ['bus'],
  data() {
    return {
      fields: {}
    };
  },
  props: {
    defaultValue: {
      // 預設值
      type: Object,
      default: function() {
        return {
          minMoney: 200,
          maxMoney: 4000,
          defaultAmount: 2000
        };
      }
    }
  },
  watch: {
    defaultValue: {
      // 把預設值初始化了
      immediate: true,
      deep: true,
      handler(newValue, oldValue) {
        if (newValue) {
          this.fields = newValue;
        }
      }
    },
    fields: {
      // 監聽變動回撥給父
      immediate: true,
      deep: true,
      handler(newValue, oldValue) {
        console.log('newValue, oldValue: ', newValue, oldValue);
        if (newValue) {
          this.bus.$emit('pivot', newValue);
        }
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.ant-input-number {
  min-width: 150px;
}
</style>

複製程式碼

總結

到這裡,我們結合Vue提供的一些特性實現了,可能某些特性有些小夥伴用的少;

這裡有用到新的slot語法,還有比較冷門的provide | inject;

有不對之處請留言,會及時修正,謝謝閱讀

相關文章