vuex 生成器,能通过 Service 自动映射成组件引用 vuex 的 State、Action 和 Mutation 并带有自动重置功能(此功能是一个 vuex 的插件)
一个简单的例子,用 vuex 编写一个标椎用户的模块,用户模块中是标准的 CRUD 操作,我们大致会这样去写
- 定义 UserService,UserService 大致会是这样
import { stringify } from 'qs';
import request from '@/utils/request';
// 列表
export async function fetchtList(params) {
return request.get('fetchList');
}
// 详情
export async function fetchtInfo(id) {
return request.get('fetchtInfo');
}
// 添加
export async function fetchtSave(payload) {
return request.post('fetchSave');
}
// 删除
export async function fetchtDelete(id) {
return request.delete('fetchtDelete');
}
// 修改
export async function fetchtUpdate(payload) {
return request.put('fetchtUpdate');
}
- 定义 UserModel,UserModel 大致会是这样
import {
fetchtList,
fetchtInfo,
fetchtSave,
fetchtDelete,
fetchtUpdate,
} from '@/services/UserService';
export default {
namespace: true,
state: {
list: {
list: [],
total: 0,
},
info: {},
},
actions: {
// 列表
fetchList({ commit, state }, payload) {
fetchtList(payload).then((response) => {
if (response.code === 0) {
commit('receive', {
...state,
list: response.data,
});
}
});
},
// 详情
fetchInfo({ commit, state }, { id }) {
fetchInfo(id).then((response) => {
if (response.code === 0) {
commit('receive', {
...state,
info: response.data,
});
}
});
},
// 添加
feachSave({ commit, state }, { success, ...other }) {
feachSave(other).then((response) => {
if (response.code === 0) {
if (success) {
success();
}
}
});
},
// 修改
feachUpdate({ commit, state }, { success, ...other }) {
feachUpdate(other).then((response) => {
if (response.code === 0) {
if (success) {
success();
}
}
});
},
// 删除
feachDelete({ commit, state }, { success, id }) {
feachDelete(id).then((response) => {
if (response.code === 0) {
if (success) {
success();
}
}
});
},
},
mutations: {
receive(state, payload) {
const keys = Object.keys(state);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
state[key] = payload[key];
}
},
},
};
- 定义 UserPage, UserPage 大致会是这样
<template>...</template>
<script>
export default {
computed: {
...mapState({
list: state => state.user.list.list,
info: state => state.user.info,
}),
},
methods: {
...mapActions({
fetchList: 'user/fetchList',
fetchInfo: 'user/fetchInfo',
feachSave: 'user/feachSave',
feachUpdate: 'user/feachUpdate',
feachDelete: 'user/feachDelete',
}),
...mapMutations({
receive:'user/receive'
}),
}
};
</script>
我们会发现一个问题,像这样比较常规的 CRUD 操作,从 Service -> Model -> Component 的 computed 和 methods 方法的名字都是一一对应的, 而 Model 中的 actions 操作基本都是调用 Service 中相应的接口,并且注入到数据流当中,而且 Service 也是按照相应模块编写的比如 UserService 就是处理 User 相关的操作, 这样就和 Model 中的 namespace 相对应,再则 Model 中的 Mutations 里面应该只有一个 receive 的 Mutation, 不应该有多个处理,actions 中所有的 commit(mutation)都应该调用 commit('receive')来进行处理 所以我们就可以根据 Service 自动生成 computed 和 methods 和 Model 中的 actions 和 mutations, 我们只处理标椎模块,如果自动生成的这三部分不能满足需求,可以进行重写覆盖
npm install @ctsj/vuexgenerator@next
yarn add @ctsj/vuexgenerator@next
- 定义 UserService
import { stringify } from 'qs';
import request from '@/utils/request';
// 列表
export async function fetchtList(params) {
return request.get('fetchList');
}
// 详情
export async function fetchtInfo(id) {
return request.get('fetchtInfo');
}
// 添加
export async function fetchtSave(payload) {
return request.post('fetchSave');
}
// 删除
export async function fetchtDelete(id) {
return request.delete('fetchtDelete');
}
// 修改
export async function fetchtUpdate(payload) {
return request.put('fetchtUpdate');
}
// 默认导出与接口先关的参数
export default {
// 接口成功失败的状态键
codeKey: 'code',
// 接口成功的状态值
codeSuccessKey: 200,
// 接口数据的键
dataKey: 'data',
// 接口消息键
messageKey: 'message',
};
- 定义 UserModel
export default {};
- 定义 UserPage
<template>
<a-table :columns="columns" :data-source="userFetchList.list" :loading="loading['user/fetchList']" :pagination="false">
<a slot="name" slot-scope="text">{{ text }}</a>
<span slot="customTitle"><a-icon type="smile-o"> Name</a-icon></span>
<span slot="tags" slot-scope="tags">
<a-tag v-for="tag in tags" :key="tag" :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'">
{{ tag.toUpperCase() }}
</a-tag>
</span>
<span slot="action" slot-scope="text, record">
<a>Invite 一 {{ record.name }}</a>
<a-divider type="vertical">
<a>Delete</a>
<a-divider type="vertical">
<a class="ant-dropdown-link"> More actions <a-icon type="down"> </a-icon></a>
</a-divider></a-divider></span>
</a-table>
</template>
<script>
import { mapState, mapMutations, mapActions, cleanMixin } from '@ctsj/vuexgenerator';
export default {
data() {
return {
columns: [
{
dataIndex: 'name',
key: 'name',
slots: { title: 'customTitle' },
scopedSlots: { customRender: 'name' },
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
scopedSlots: { customRender: 'tags' },
},
{
title: 'Action',
key: 'action',
scopedSlots: { customRender: 'action' },
},
]
}
},
mounted() {
this.userFetchListAction();
},
mixins: [cleanMixin(['user'])],
computed: {
...mapState(['user']),
},
methods: {
...mapActions(['user']),
...mapMutations(['user']),
},
};
</script>
- 注册 Service(在一个单独的文件中 VuexGeneratorPlugin.js)
import VuexGenerator from '@ctsj/vuexgenerator';
import UserModel from '../modules/user';
import PersonModel from '../modules/person';
function serviceRegister() {
const requireComponent = require.context('../../service', false, /.*\.(js)$/);
const services = {};
requireComponent.keys().forEach((fileName) => {
const serviceKey = fileName.substring(2, fileName.length - 3);
services[serviceKey] = requireComponent(fileName);
});
return services;
}
// 创建VuexGeneratorPlugin插件
export default VuexGenerator(serviceRegister(), {
user: UserModel,
person: PersonModel,
});
- 在 main.js 中进行引用插件
import { createApp } from 'vue'
import Antd from 'ant-design-vue'
import store from './store'
import router from './router'
import App from './App.vue'
import './registerServiceWorker'
import 'ant-design-vue/dist/antd.css'
createApp(App).use(store).use(router).use(Antd).mount('#app')
-
工厂方法 - 创建 vuex 的插件(传入 ServiceConfig 和 Modules 的定义)
-
mapState - state 的辅助函数
-
mapActions - action 的辅助函数
-
mapMutations - Mutations 的辅助函数
-
cleanMixin - 用户自动重置 vuex 数据的 mixin
demo 目录下附带了一个 demo