如果你厌倦了Vue的状态管理,不妨点开文章看看

555 阅读2分钟

你厌倦了Vuex的状态管理吗

  • 你厌倦了诸如StateGetterMutationActionModule 和各种map映射方法…… 等等复杂的概念吗?

  • 你是否想以更优雅、更简易、更轻量、更具面向对象思想的管理你的数据和方法?

  • 你是否想为你的项目提供一个Service模块,以便更好的管理页面无关逻辑?

那么以前的 Vue 状态管理库都不能满足你的需求!!!你应该考虑使用rx-srv来助力你的开发体验!!!

你真的想要那么复杂的状态管理库吗?

现在放下所有的旧思想,认真的想一个问题:你真的想要那么复杂的状态管理库吗?

请看下面的代码

import RxService from 'rx-srv'
import {Service} from "typedi";

@Service()
class UserService extends RxService {

  public userName: string = 'Tom'
  public userAge: number = 18

  public userAddress = {
    province: '安徽',
    city: '池州'
  }

  constructor() {
    super();
    super.observe()

    window['UserService'] = this
  }

  public rename(name: string) { this.userName = name }
  public growUp() { this.userAge ++ }
  public updateProvince(province: string) { this.userAddress.province = province }
  public updateCity(city: string) { this.userAddress.city = city }

}

export const userService = Container.get(UserService) // 获取单例的 UserService

如果我说上面的 userService 的属性(如:userName)都是响应式的,可以用于存储跨页面的状态值,然后每个Service可以写你需要的方法(如rename())去处理页面无关逻辑。

上面的这个类是不是能满足你所有的状态管理和功能逻辑的需求呢?

如何在前端页面中使用呢?

<template>
  <div id="app">
    User Info:
    <div>Name: {{userService.userName}}</div>
    <div>Age:  {{userService.userAge}}</div>
    <div>Addr: {{userService.userAddress.province}} - {{userService.userAddress.city}}</div>

    <div>
      <button @click="growUp">Grow Up</button>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { userService } from "@/service";

@Component
export default class App extends Vue {
  public userService = userService

  public growUp() {
    this.userService.growUp()
  }
}

</script>
<style scoped>
#app {
   font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  height: 100%;
  overflow: hidden;
  width: 100%;
}
</style>

在前端页面中使用

我们不需要诸如 this.$store 这样隐式的方法,显得非常突兀(并且安装的时候需要隐式的挂载到每个Vue实例中)

我们也不需要诸如mapState这样第一眼看过去非常懵逼的,需要学习成本的奇怪概念。

我们只用导入userService,然后就可以使用它的属性及方法。

怎么做到使得UserService的属性值是响应式的呢?

读到这里你是否会有疑问,你是怎么做到使得UserService的属性值是响应式的?会不会有内存或者性能的问题?

首先,使得属性值是响应式的实现非常简单,如下rx-srv源码

import Vue from "vue";

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: () => {},
  set: (key: string) => {}
}

export function proxy(target: object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter() {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter(val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

class RxService {
  private __data: object = {}

  constructor() { }

  public observe() {
    Reflect.ownKeys(this).forEach((key: string) => {
      if (key.startsWith('_') || key.startsWith('$')) return

      this['__data'][key] = this[key]

      Reflect.deleteProperty(this, key)

      proxy(this, `__data`, key)
    })

    Vue.observable(this['__data'])
  }
}

export default RxService

源码地址见GitHub:github.com/simo-an/rx-…

在上面代码中observe()方法中我们将Service的属性先剪切到__data属性上,再代理回Service,最后利用Vue提供的observable()方法将属性值做成响应式的,即实现了我们需要的功能。

Vuex中我们至少创建了一个Vue实例,但是在rx-srv中我们甚至不需要这个Vue实例的消耗,你说这是不是在性能和内存上都做到了极简?

快去使用一下吧

你可以现在你的Demo试用一下吧 ( www.npmjs.com/package/rx-…

yarn add rx-srv -D
yarn add reflect-metadata -D
yarn add typedi -D

其中安装 reflect-metadatatypedi 是为了让代码更加优雅的创建单例Service

你需要在main.ts中首先导入reflect-metadata,如下

import 'reflect-metadata'

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

好了,以上就是全部内容了,欢迎使用之后给出意见,帮助我们一起将这个库做的更好!同时也欢迎 start、fork。

全部代码见:github.com/simo-an/rx-…