阅读 872

Vue 折腾记 - (18) 用Vue的Inject Provide结合Event Bus来实现局部的状态维护

前言

原型有个东西,看着是几个功能组件的组合体;

想拆分成对应的组件(全部写在一起是贼恐怖的事情),又不想用Vuex这类来实现。

那最终的方案就是Vueeventbus


效果图

只展示部分功能,实际原型要复杂的多;

原型大体是这样的


实现原理

其实就是各个组件独立维护自己的状态,组件的默认值从外部传入;

而内部通过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;

有不对之处请留言,会及时修正,谢谢阅读

关注下面的标签,发现更多相似文章
评论