vuex-class,vue-class-component和vue-property-decorator

vuex-class

npm install --save vuex-class
import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'
 
const someModule = namespace('path/to/module')
 
@Component
export class MyComp extends Vue {
  @State('foo') stateFoo
  @State(state => state.bar) stateBar
  @Getter('foo') getterFoo
  @Action('foo') actionFoo
  @Mutation('foo') mutationFoo
  @someModule.Getter('foo') moduleGetterFoo
 
  // If the argument is omitted, use the property name
  // for each state/getter/action/mutation type
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux
 
  created () {
    this.stateFoo // -> store.state.foo
    this.stateBar // -> store.state.bar
    this.getterFoo // -> store.getters.foo
    this.actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
    this.mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
    this.moduleGetterFoo // -> store.getters['path/to/module/foo']
  }
}

Vuex-Class是一个基于decorators(装饰器)的Vuex状态管理库,通过利用Vue2.6的TypeScript装饰器的特性,可以让我们在Vue组件中更加方便地使用VuexVuex-Class是对Vuex库的一种补充,将Vuex状态管理库中的API转换成 TypeScriptdecorators的形式。

Vuex-Class主要特性:

  1. 装饰器方式 - @State, @Getter, @Action, @Mutation
  2. 声明性 - Vuex模块化代码更易于理解和维护
  3. TypeScript兼容 - 可以与TypeScript完美结合
  4. 根据模块创建Vuex Store

使用Vuex-Class可以让我们在Vue组件代码中简便地使用Vuex状态管理库提供的一些API,例如:

import { Component, Vue } from 'vue-property-decorator';
import { Getter, Commit } from 'vuex-class';

@Component
export default class MyComponent extends Vue {
  @Getter counter!: number;
  @Commit('increment') increment!: () => void;

  onClick() {
    this.increment();
  }
}

通过上述代码,我们在MyComponent组件中通过@Getter装饰器来获取Vuex状态管理库中counter状态的值,并在onClick方法中通过@Commit装饰器来提交Vuex中的increment方法。

除此之外,Vuex-Class还提供一些其他的装饰器和注解,如@Mutation,@Action,@Module,@Namespace等等,它们都可以让我们更加方便地使用Vuex

总结而言,Vuex-Class是一个方便使用的Vuex状态管理库,它能够让我们在Vue组件中更加容易地使用VuexAPI,强类型检查和提高代码可读性。
好的,请看下面的代码:

import { State, Getter, Action, Mutation, namespace } from "vuex-class";
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const someModule = {
  namespaced: true,
  state: {
    count: 0
  },
  getters: {
    doubleCount(state) {
      return state.count * 2
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    incrementAfterDelay({ commit }) {
      return new Promise(resolve => {
        setTimeout(() => {
          commit('increment');
          resolve();
        }, 1000);
      });
    }
  }
}

const store = new Vuex.Store({
  modules: {
    someModule: someModule
  },
  state: {
    name: 'Vue.js'
  },
  getters: {
    reverseName(state) {
      return state.name.split('').reverse().join('');
    }
  },
  mutations: {
    setName(state, newName) {
      state.name = newName;
    }
  },
  actions: {
    setNameWithDelay({ commit }, newName) {
      return new Promise(resolve => {
        setTimeout(() => {
          commit('setName', newName);
          resolve();
        }, 1000);
      });
    }
  }
});

// 打印someModule的导出情况
console.log(someModule);

const AppComponent = Vue.extend({
  template: '
' + '

{{ name }}

'
+ '

{{ reverseName }}

'
+ '

{{ count }}

'
+ '

{{ doubleCount }}

'
+ '' + '' + '' + '
'
, computed: { // 使用 State 辅助类将 name 属性与 Vuex 中的 state.name 进行绑定 @State('name') name, // 使用 Getter 辅助类将 reverseName 属性与 Vuex 中的 getters.reverseName 进行绑定 @Getter('reverseName') reverseName, // 使用 namespace 函数指定 someModule,然后使用 State 辅助类将 count 属性与 someModule 中的 state.count 进行绑定 @State(state => state.someModule.count) count, // 使用 namespace 函数指定 someModule,然后使用 Getter 辅助类将 doubleCount 计算属性与 someModule 中的 getters.doubleCount 进行绑定 @Getter(state => `someModule/${state.doubleCount}`) doubleCount }, methods: { // 使用 Mutation 装饰器指定 setNameWithDelay 方法调用 Vuex 中的 mutation:setName @Mutation('setName') setName, // 使用 Mutation 装饰器指定 increment 方法调用 Vuex 中的 mutation:increment @Mutation(state => `someModule/${state.increment}`) increment, // 使用 Action 装饰器指定 incrementAfterDelay 方法调用 Vuex 中的 action:incrementAfterDelay @Action('someModule/incrementAfterDelay') incrementAfterDelay } }); const app = new AppComponent({ el: '#app', store });

在上面的代码中,我们首先定义了一个名为 someModule 的 Vuex 模块,其中包含了一个单独的状态 count,以及计算属性 doubleCount、变化方法 increment 和异步操作 incrementAfterDelay。接着,我们注册了这个模块到了 Vuex 的仓库中。

然后,我们在组件中使用了 @State@Getter@Action@Mutation 装饰器,分别将四个调用 Vuex 中对应对象的方法 namereverseNamecountdoubleCountsetNameincrementincrementAfterDelay 绑定到了组件中的相应属性和方法上。在这些装饰器中,我们有时使用了 namespace 函数,指定了操作的模块。

最后,我们创建了一个 AppComponent 组件,并在其中使用了上述这些绑定的属性和方法。这样,在应用中我们就可以直接使用这些属性和方法,而无需手动访问 Vuex 对象了。

vue-class-component

<template>
  <OtherComponent />
</template>
import Vue from 'vue'
import Component from 'vue-class-component'
import OtherComponent from './OtherComponent.vue'
// 如果没有OtherComponent,可以写 `@Component`
@Component({
  components: {
    OtherComponent
  }
})
export default class HelloWorld extends Vue {
  message1 = 'Hello World!' // data的响应式数据message1
  message2 = undefined // 这样声明的,不是响应式的
  // Declared as component method
  hello() {
    console.log('Hello World!')
  }
  // Declared as computed property getter
  get name() {
    return this.firstName + ' ' + this.lastName
  }
  // Declared as computed property setter
  set name(value) {
    const splitted = value.split(' ')
    this.firstName = splitted[0]
    this.lastName = splitted[1] || ''
  }
  data() {
    return {
   		message: undefined
    }
  }
  // Declare mounted lifecycle hook
  mounted() {
    console.log('mounted')
  }
  // Declare render function
  render() {
    return <div>Hello World!</div>
  }
}

文件class-component-hooks.js

import Component from 'vue-class-component'

// 注册钩子函数
Component.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate'
])

文件main.js

// Make sure to register before importing any components
import './class-component-hooks' // 引入声明的钩子

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

new Vue({
  el: '#app',
  render: h => h(App)
})

注册钩子后,类组件将它们实现为类原型方法:

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Vue {
  // 本来是不允许这样的,但是上面已经注册过了,就可以用了
  beforeRouteEnter(to, from, next) {
    console.log('beforeRouteEnter')
    next()
  }

  beforeRouteUpdate(to, from, next) {
    console.log('beforeRouteUpdate')
    next()
  }

  beforeRouteLeave(to, from, next) {
    console.log('beforeRouteLeave')
    next()
  }
}

自定义装饰器

文件decorators.js

import { createDecorator } from 'vue-class-component'
export const Log = createDecorator((options, key) => {
  const originalMethod = options.methods[key]
  options.methods[key] = function wrapperMethod(...args) {
    console.log(`Invoked: ${key}(`, ...args, ')')
    originalMethod.apply(this, args)
  }
})
import Vue from 'vue'
import Component from 'vue-class-component'
import { Log } from './decorators'
@Component
class MyComp extends Vue {
  @Log
  hello(value) {}
}

Extend 和 Mixins

您可以将现有类组件扩展为本机类继承。假设您有以下超类组件:

文件super.js

import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class Super extends Vue {
  superValue = 'Hello'
}

每个超类都必须是一个类组件。它需要继承构造函数作为祖先,并由装饰器进行装饰

import Super from './super'
import Component from 'vue-class-component'

@Component
export default class HelloWorld extends Super {
  created() {
    console.log(this.superValue) // -> Hello
  }
}

文件mixins.js

import Vue from 'vue'
import Component from 'vue-class-component'

// You can declare mixins as the same style as components.
@Component
export class Hello extends Vue {
  hello = 'Hello'
}

@Component
export class World extends Vue {
  world = 'World'
}

在类样式组件中使用它们:

import Component, { mixins } from 'vue-class-component'
import { Hello, World } from './mixins'

@Component
export class HelloWorld extends mixins(Hello, World) {
  created () {
    console.log(this.hello + ' ' + this.world + '!') 
    // -> Hello World!
  }
}

与超类一样,所有 mixin 都必须声明为类组件。

Props Definition

Vue Class Component 没有提供专门的 props 定义 API。但是,您可以使用规范 API 来做到这一点:Vue.extend

<template>
  <div>{{ message }}</div>
</template>
<script lang="ts">
	import Vue from 'vue'
	import Component from 'vue-class-component'
	
	const GreetingProps = Vue.extend({
	  props: {
	    name: String
	  }
	})
	
	@Component
	export default class Greeting extends GreetingProps {
	  get message(): string {
	    return 'Hello, ' + this.name
	  }
	}
</script>

根据定义的 prop 类型的推断,可以通过扩展类组件来使用它们。Vue.extend

如果你有一个超类组件或 mixin 要扩展,请使用 helper 将定义的 props 与它们组合在一起:mixins

<template>
  <div>{{ message }}</div>
</template>
<script lang="ts">
	import Vue from 'vue'
	import Component, { mixins } from 'vue-class-component'
	import Super from './super'
	
	const GreetingProps = Vue.extend({
	  props: {
	    name: String
	  }
	})
	
	@Component
	export default class Greeting extends mixins(GreetingProps, Super) {
	  get message(): string {
	    return 'Hello, ' + this.name
	  }
	}
</script>

vue-property-decorator

vue-property-decorator是一个vue组件的装饰器库,它提供了一些装饰器来简化在Vue组件中使用的一些常见选项,如状态、计算属性、事件、方法等。使用它,你可以编写更加简洁、易读、易维护的Vue代码。

下面是vue-property-decorator中一些常用的装饰器:

@Component

@Component是在一个类组件中使用的装饰器,可以用来声明一个Vue组件并指定其模板、样式和其他与Vue组件相关的选项。

import { Component, Vue } from 'vue-property-decorator';

@Component({
  name: 'my-component',
  template: '
Hello, {{name}}!
'
, style: ` div { color: red; } ` }) export default class MyComponent extends Vue { name = 'World'; }

@Prop

@Prop是一个装饰器,用于声明一个组件的属性,并指定其类型、默认值和其他选项。如果要在父组件中向子组件传递数据,则需要使用该装饰器来声明子组件的props。

import { Component, Vue, Prop } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  @Prop(String) readonly name!: string;
  @Prop({ default: 18 }) readonly age!: number;
}

@Watch

@Watch是一个装饰器,用于监听某个属性的变化,并在其变化时执行回调。如果要在某个属性变化时执行一些操作,如调用API获取新数据、更新相关状态等,则可使用该装饰器。

import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  @Prop(String) readonly name!: string;
  @Prop({ default: 18 }) readonly age!: number;

  @Watch('name')
  onNameChanged(newVal: string, oldVal: string) {
    console.log(`Name changed from ${oldVal} to ${newVal}`);
  }
}

@Emit

@Emit是一个装饰器,用于在一个组件中定义一个方法,并在该方法触发时发出一个自定义事件。如果您需要在子组件中触发父组件中的事件,则可以使用该装饰器。

import { Component, Vue, Emit } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  counter = 0;

  @Emit()
  increment() {
    this.counter += 1;
  }
}

@Inject

@Inject用于从父组件中注入一个属性的值。如果您需要向子组件中提供所需的实例,可以使用该装饰器。

import { Component, Vue, Inject } from 'vue-property-decorator';

@Component
export default class MyComponent extends Vue {
  @Inject('theme') readonly theme!: string;
}

以上是vue-property-decorator的一些常见装饰器及其用法。此外,该库还提供了许多其他有用的功能,如只读属性、方法修饰符等。

需要注意的是,使用vue-property-decorator之前需要先确保您的项目中已经安装并正确配置了TypeScriptBabel

详细介绍import { State, Getter, Action, Mutation, namespace } from “vuex-class”

State
State是一个装饰器函数,用于在 Vue 组件中获取 Vuex store 中的状态(state)。通过在类的属性上使用@State装饰器,可以在组件中访问和使用这些状态。

import { Component, Vue } from 'vue-property-decorator';
import { State } from 'vuex-class';

@Component
export default class MyComponent extends Vue {
  @State counter: number;
}

Getter
Getter是一个装饰器函数,用于在 Vue 组件中获取 Vuex store 中的计算属性(getter)。通过在类的属性上使用@Getter装饰器,可以在组件中访问和使用这些计算属性。

import { Component, Vue } from 'vue-property-decorator';
import { Getter } from 'vuex-class';

@Component
export default class MyComponent extends Vue {
  @Getter fullName: string;
}

Action
Action是一个装饰器函数,用于在 Vue 组件中调用 Vuex store 中的方法(action)。通过在类的方法上使用@Action装饰器,可以在组件中调用这些方法。

import { Component, Vue } from 'vue-property-decorator';
import { Action } from 'vuex-class';

@Component
export default class MyComponent extends Vue {
  @Action fetchData(): Promise<void> {
    // 调用 Vuex store 中的 fetchData action
    return this.$store.dispatch('fetchData');
  }
}

Mutation
Mutation是一个装饰器函数,用于在 Vue 组件中调用 Vuex store 中的方法(mutation)。通过在类的方法上使用@Mutation装饰器,可以在组件中调用这些方法。

import { Component, Vue } from 'vue-property-decorator';
import { Mutation } from 'vuex-class';

@Component
export default class MyComponent extends Vue {
  @Mutation increment(): void {
    // 调用 Vuex store 中的 increment mutation
    this.$store.commit('increment');
  }
}

namespace
namespace是一个函数,用于在 Vuex store 中定义和管理命名空间(namespace)。命名空间可以帮助我们更好地组织和管理 Vuex store 中的模块状态(state)、计算属性(getters)、方法(actions)和提交(mutations)。

在使用命名空间之前,我们需要在 Vuex store 的模块中指定命名空间:

// Vuex store 模块中
const exampleModule = {
  namespaced: true,  // 启用命名空间
  state: { ... },
  getters: { ... },
  actions: { ... },
  mutations: { ... }
};

使用namespace函数,我们可以在 Vue 组件中直接访问和使用命名空间中的状态、计算属性、方法和提交,而不再需要手动硬编码命名空间。

import { Component, Vue } from 'vue-property-decorator';
import { namespace } from 'vuex-class';

const exampleNamespace = namespace('exampleModule');

@Component
export default class MyComponent extends Vue {
  @exampleNamespace.State counter: number;
  @exampleNamespace.Getter fullName: string;
  @exampleNamespace.Action fetchData(): Promise<void> {
    return this.exampleNamespace.dispatch('fetchData');
  }
  @exampleNamespace.Mutation increment(): void {
    this.exampleNamespace.commit('increment');
  }
}

使用namespace函数创建了一个命名空间的装饰器对象exampleNamespace。通过在组件的类属性上使用这些装饰器,我们可以在组件中访问和使用命名空间中的状态counter、计算属性fullName、方法fetchData和提交increment

这种方式可以帮助我们更好地组织和封装 Vuex store 的模块,减少了在组件中硬编码命名空间的问题,并提高了代码的可读性和可维护性。需要注意的是,在使用命名空间之前,需要正确配置和安装 Vuex 和 "vuex-class" 库。

{
  "name": "pawm-pms",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "serve": "hui dev",
    "build": "hui build --filename-hashing",
    "eslint": "npm run eslint"
  },
  "dependencies": {
    "@pa/pawm-portal-sdk": "1.1.30",
    "@fc/regex": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PAWM-PMS-FRONTEND-UI.git#regex",
    "@fc/request": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PAWM-PMS-FRONTEND-UI.git#request",
    "@fc/ui": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PAWM-PMS-FRONTEND-UI.git#ui",
    "@hui/cli": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PACKAGE/PAWM-PMS-FRONTEND-HUI-CLI.git",
    "@hui/bundler": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PACKAGE/PAWM-PMS-FRONTEND-HUI-BUNDLER.git#release",
    "@wchbrad/vue-easy-tree": "1.0.12",
    "axios": "0.21.4",
    "bignumber.js": "9.1.1",
    "dayjs": "1.11.7",
    "devextreme": "23.1.5",
    "dragselect": "2.5.5",
    "echarts": "5.4.0",
    "element-ui": "2.15.12",
    "html2canvas": "1.0.0",
    "hui-core": "git+ssh://[email protected]:LCKJ/PAWM/PAWM-FRONT/PACKAGE/PAWM-PMS-FRONTEND-HUI-CORE.git",
    "moment": "2.29.4",
    "monaco-editor": "0.20.0",
    "sortablejs": "1.15.0",
    "vue": "2.6.10",
    "vue-class-component": "7.2.6",
    "vue-json-views": "1.3.0",
    "vue-property-decorator": "9.1.2",
    "vuex-class": "0.3.2"
  },
  "devDependencies": {
    "@babel/core": "7.21.5",
    "@babel/plugin-syntax-jsx": "7.18.6",
    "@babel/plugin-transform-typescript": "7.20.2",
    "@babel/preset-env": "7.20.2",
    "@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
    "@vue/cli-plugin-babel": "5.0.8",
    "ag-grid-community": "30.0.3",
    "ag-grid-vue": "30.0.3",
    "babel-plugin-import": "1.13.5",
    "core-js": "3.26.1",
    "tslib": "2.4.1",
    "typescript": "4.9.3",
    "typescript-plugin-css-modules": "3.4.0",
    "xlsx": "0.18.5",
    "xml2js": "0.6.0"
  }
}

你可能感兴趣的:(前端)