派发器思想应用

开发Vue中遇到的最大的问题就是框架的规范性太强,导致所有的业务方法都必须定义在methods对象里,代码可以达到几百行,甚至几千行。当然,也可以通过抽离组件的方式来查拆分业务,但是还是会存在大量的业务逻辑堆积。这时,使用JS的派发器思想就可以进行业务逻辑的抽离,便于后期维护。

废话不多说,直接上干货。

1. 案例效果

d1.png

案例是比较简单的,是一个数值计算案例。

点击 + 的时候让数值加1;
点击 - 号的时候数值减1;

包括3个组件。Button组件、Result组件、Counter组件。

Button组件触发事件处理函数,用于事件的处理;
Result组件用来显示计算后的结果;
Counter组件用来处理数值计算的业务;

本次的目的就是抽离Counter组件的业务逻辑。

2. 目录结构

d2.png

由于案例比较简单,大概说一下实现流程。

首先创建一个简单的Vue项目。项目不涉及Vue-Router和Vuex,一切从简。

vue create vue_dispatch // 创建项目

创建基础项目后,删掉没用的样式和组件,在components文件中编写自己的组件,实现对应的逻辑后,在App.vue中引入。

代码如下:

App.vue

<template>
  <div id="app">
    <counter />    
  </div>
</template>

<script>
import Counter from './components/Counter';

export default {
  name: 'App',
  components: {
    Counter
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

index.vue

<template>
  <div class="counter-container">
    <!-- 结果显示区域 -->
    <counter-result :result="result" />
    <!-- Button 按钮组 -->
    <div>
      <counter-button
        innerText="+"
        type="PLUS"
        @handBtnClick="handBtnClick"
      />
      <counter-button
        innerText="-"
        type="MINUS"
        @handBtnClick="handBtnClick"
      />
    </div>
  </div>
</template>

<script>
import CounterResult from './Result';
import CounterButton from './Button';

export default {
  name: 'Counter',
  components: {
    CounterResult,
    CounterButton
  },
  data () {
    return {
      result: 0
    }
  },
  methods: {
    handBtnClick (type) {
      switch (type) {
        case 'PLUS':
          this.result++;
          break;
        case 'MINUS':
          this.result--;
          break;
        default:
          break;
      }
    }
  }
}
</script>

Button.vue

<template>
  <button
    class="counter-btn"
    :type="type"
    @click="count"
  >
    {{ innerText }}    
  </button>
</template>

<script>
export default {
  name: 'CounterButton',
  props: {
    innerText: String,
    type: String
  },
  methods: {
    count () {
      this.$emit('handBtnClick', this.type);
    }
  }
}
</script>

<style scoped>
.counter-btn {
  width: 60px;
  height: 40px;
  margin: 5px;
}
</style>

Result.vue

<template>
  <h1>{{ result }}</h1>
</template>

<script>
export default {
  name: 'CounterResult',
  props: {
    result: Number
  }
}
</script>

3. 优化思路

实现思路

type -> 事件 -> 逻辑 -> type -> 派发器 -> 数据更改

你会发现,派发器的思想其实和redux是一致的,只不过在这里不对store进行实现,只实现reducer、type和dispatch部分。还有,如果项目中没有特别较多需要共享的状态,不需要引入redux或者vuex,它们只是为我们提供便利的中央数据存储。

4. 项目优化

a. 创建actions文件夹,新建counter.js文件

actions/counter.js

定义action类型,旨在通过不同的action触发不同的操作。

const PLUS = "PLUS",
      MINUS = "MINUS";

export {
  PLUS,
  MINUS
};

b. 创建reducers文件夹,新建counter.js文件

reducers/counter.js

主要定义操作数据的纯函数。

function counterReducer (data) {
  function plus () {
    return data.result + 1;
  }

  function minus () {
    return data.result - 1;
  }

  return {
    plus,
    minus
  };
}

export default counterReducer;

c. 创建dispatchers文件夹,新建counter.js文件

使用定义的reducer和action,用于业务逻辑处理。

import reducer from '@/reducers/counter';
import { PLUS, MINUS } from '@/actions/counter';

export default (ctx) => {
  const { plus, minus } = reducer(ctx.$data);

  return function (type, ...args) {
    switch (type) {
      case PLUS:
        ctx.result = plus(...args);
        break;
      case MINUS:
        ctx.result = minus(...args);
        break;
      default:
        break;
    }
  }
}

d. 改造现有业务逻辑

components/Counter/index.vue

methods对象现有方法

methods: {
  handBtnClick (type) {
    switch (type) {
      case 'PLUS':
        this.result++;
        break;
      case 'MINUS':
        this.result--;
        break;
      default:
        break;
    }
  }
}

methods对象改造后

methods: {
  handBtnClick (...args) {
    dispatch(this)(...args);
  }
}	

测试当前程序,可以正常运行。相比于之前来说,现在的逻辑要比之前清晰很多,当业务复杂时,这种对比效果就更加明显,使用这种方式可以有效的分割业务代码,易于项目后期的拓展与维护。

e. 代码优化

处理一下方法名称和传递的参数名,使之更加规范。

Counter/index.vue

<template>
  <div class="counter-container">
    ...
    <div>
      <counter-button
        innerText="+"
        action="PLUS"
        @dispatch="dispatch"
      />
      <counter-button
        innerText="-"
        action="MINUS"
        @dispatch="dispatch"
      />
    </div>
  </div>
</template>

<script>
...

export default {
  ...
  methods: {
    dispatch (...args) {
      dispatch(this)(...args);
    }
  }
}
</script>

Counter/Button.vue

<template>
  <button
    class="counter-btn"
    :action="action"
    @click="count"
  >
    {{ innerText }}    
  </button>
</template>

<script>
export default {
  name: 'CounterButton',
  props: {
    innerText: String,
    action: String
  },
  methods: {
    count () {
      this.$emit('dispatch', this.action);
    }
  }
}
</script>

5. 总结

通过一个数值计算案例,讲解如何使用派发器的思想来抽离业务逻辑。当业务复杂时,可以通过这种思路来优化代码,进行业务逻辑的抽离。这仅是一种优化的思路,作为一个合格的攻城狮,不仅要实现功能,更要考虑功能后期的拓展与维护。

派发器思想应用