在Vuex模块中实现继承的方法

16

我正在使用VueJS和Vuex构建我的应用程序,遇到了一个问题:当我有多个模块使用相同的数据字段时,涉及到API配置,例如dat。

getUsers ({ state, commit }) {
    axios.get(urls.API_USER_URL).then( response => {
        let data = response.data;
        parseApi(state, data, 'user');

    }).catch( err => {
        console.log('getUser error: ', err);
    })
},

而其他模块中的另一个函数类似于

getPosts ({ state, commit }) {
    axios.get(urls.API_POST_URL).then( response => {
        let data = response.data;
        parseApi(state, data, 'posts');

    }).catch( err => {
        console.log('getUser error: ', err);
    })
},

我想知道是否可以仅继承我的模块并在其中添加附加的数据字段/函数?

每个模块都会有消息和状态字段,这些字段是我从API响应中获取的。

export default {
    state : {
        message : "",
        status : 0
    },
    parseApi: function(state, data, property) {
        if (data.hasOwnProperty('message')) {
            state.message = data.message;
        }
        if (data.hasOwnProperty('status')) {
            state.status = data.status;
        }
        if (data.hasOwnProperty(property)) {
            state[property] = data[property];
        }
    }
}

大致的意思就是这样。

有没有一种方法可以写一次代码,然后在我使用的每个模块中都拥有它?

编辑:

我甚至无法将这个apiParse函数放在那里,我需要对这些字段进行更改。 但是重复所有时间是毫无意义的... 有什么建议吗?


1
你试过了吗?如果是的话,发生了什么事情? - Ohgodwhy
当然它不起作用。我不知道我应该如何处理它。有什么建议吗? - Canor
5个回答

24

我将可重用的Vuex代码放在小类中。例如:

crud.js

export default class {
    constructor ( endpoint ) {
       this.state = {
          endpoint: endpoint,
          meta:     {},
          status:   null,
          known:    [],
          currentId: null,
       };
       this.getters = {
          id: state => id => state.known.find( o => o.id === id )
       };
       this.actions = {
          async store( context, payload ) {
               *(call to API)*
          },
          async update( context, payload ) {
               *(call to API)*
          },
          *...etc*
      };
      this.mutations = {
         STORED(state, item) {
            state.known.push(item);
         },
         *...etc*
      };
   }
}

然后我可以在所有我的模块中使用它:

user.module.js

import Crud from '/crud';
var crud = new Crud('/api/users');

const state = {
   ...crud.state,
};
const getters = {
   ...crud.getters,
};
const actions = {
   ...crud.actions,
};
const mutations = {
   ...crud.mutations,
};

export default {
   namespaced: true,
   state,
   getters,
   actions,
   mutations
};

endpoint 放在模块的状态中有什么理由吗?我在想,因为它已经在闭包中可用于 "(调用API)" 部分。 - bernie
1
我知道,“感谢”评论是不被鼓励的,但这是我迄今为止看到的最优雅的解决方案之一。 :) - Igor Nikiforov

7

进一步发展埃琳的回答,您可以定义一个具有共同特征的基类,例如:

export default class BaseModule {
    protected state() {
        return {
            isLoading: false,
        };
    };
    protected getters() {
        return {
            isLoading(s) {
                return s.isLoading;
            },
        };
    };
    protected actions() {
        return {};
    };
    protected mutations() {
        return {
            [START_TRANSACTION]: (s) => {
                s.isLoading = true;
            },
            [END_TRANSACTION]: (s) => {
                s.isLoading = false;
            },
        };
    }
    protected modules() {
        return {};
    };

    public getModule = () => {
        return {
            namespaced: true,
            state: this.state(),
            getters: this.getters(),
            actions: this.actions(),
            mutations: this.mutations(),
            modules: this.modules(),
        };
    }
}

现在,您可以通过类继承仅扩展/覆盖需要的部分。例如,如果您需要扩展模块...

import BaseModule from './BaseModule';
import rowDensity from '@/store/modules/reusable/rowDensity';

export default class ItemListModule extends BaseModule {  
  protected modules() {
    return {
      ...super.modules(),
      rowDensity,
    };
  };
}

最后,要将它们作为模块在商店中使用,您可以实例化它们并调用.getModule()

import Vue from 'vue';
import Vuex from 'vuex';
import ItemListModule from './modules/ItemListModule';

Vue.use(Vuex);

const debug = process.env.NODE_ENV !== 'production';

export const MODULE_NAMESPACES = {
  List: 'list',
};

export default new Vuex.Store({
  modules: {
    [MODULE_NAMESPACES.List]: new ItemListModule().getModule(),
  },
  strict: debug,
});

太棒了!我很想看到一个完整的示例项目,例如一个库存店的项目。 - user2495085

4

我根据以下内容解决了一些与状态字段相关的继承问题:

https://vuex.vuejs.org/en/modules.html#namespacing

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations,
    modules : {
        apiResponses
    }
}

我先在带命名空间的模块用户中导出了apiResponses模块,接着我用同样的方式处理了帖子模块。

这些命名空间继承了那些消息/状态和它们的变化,我只是在我的用户和帖子模块中调用了它们。现在它们正常工作。

我的消息变异来自apiResponses:

[types.SET_MESSAGE] (state, message) {
    state.message = message;
},

可在我的用户模块的动作中使用

if (data.hasOwnProperty('message')) {
    commit(types.SET_MESSAGE, data.message);
}

然后在我的组件中,我只需调用。
    computed: {
        ...mapGetters({
            user : 'user/user',
            userMessage : 'user/message',
            post: 'post/monitoring',
            postMessage : 'post/message',

        }),
    },

编辑过的

我的问题的最后部分如下。

我在apiResponse模块中得到了操作。

let actions = {
    getResponseParsed({commit}, payload) {
        console.log(payload)
        if (payload.data.hasOwnProperty('message')) {
            commit(types.SET_MESSAGE, payload.data.message);
        }
        if (payload.data.hasOwnProperty('status')) {
            commit(types.SET_STATUS, payload.data.status);
        }
        if (payload.data.hasOwnProperty(payload.property)) {
            commit(payload.mutation, payload.data[payload.property]);
        }
    }
}

然后在我的用户和其他模块中,我这样调用它:
getUser ({ state, commit, dispatch }) {
    axios.get(urls.API_GET_USER_URL).then( response => {
        let data = response.data;

        dispatch('getResponseParsed', {
            data : data,
            mutation : types.SET_USER,
            property : 'user'
        });

    });
},

最后一件事,我们需要使这个新模块能够重复使用。根据文档,我们需要将其创建为组件。

export default {
    state() {
        return {
            message : '',
            status : 0,
        }
    },
    getters,
    mutations,
    actions
}

使用状态作为函数 :)

希望有其他人遇到相同问题 :D


2

以下是我的工作内容:

首先,我创建了一个名为mainApi.js的文件,其任务仅是与API建立连接。

mainApi.js

import axios from "@/plugins/axios";

export default {
    get(url ,id){
        return axios.get(`/${url}/${id}`);
    },
    getAll(url, filter) {
        return axios.get(`/${url}`, {params: {...filter}});
    },
    create(url ,teBeCreated){
        return axios.post(`/${url}`, teBeCreated);
    },
    update(url ,toBeUpdated){
        return axios.put(`/${url}/${toBeUpdated.oid}`, toBeUpdated);
    },
    delete(url ,id){
        return axios.delete(`/${url}/${id}`);
    },
}

第二步:我编写了一个基类来定义存储数据所需的函数。然后其他存储模块可以继承这个类。

gate.js

import mainApi from '@/api/main'
import store from '@/store'

export default class {

  constructor() {

    this.state = {
        view: null,
        list: [],
    };

    this.getters = {
        view: (state) => state.view,
        list: (state) => state.list,
    }

    this.mutations = {
        SET_VIEW(state, payload) {
            state.view = payload;
        },
        SET_LIST(state, payload) {
            state.list = payload;
        },
        UN_SET_VIEW(state) {
            state.view = null;
        },
        UN_SET_LIST(state) {
            state.list = [];
        },
    }

    this.actions = {
        get({ commit }, { url, id }) {
            return new Promise((resolve, reject) => {
                mainApi.get(url, id)
                    .then(response => {
                        commit('SET_VIEW', response.data.data);
                        resolve(response)
                    })
                    .catch(error => {
                        console.log("error in get method in gate store: ", error);
                        commit('UN_SET_VIEW');
                        reject(error)
                    })
            });
        },
        getAll({ commit }, { url, filter }) {
            return new Promise((resolve, reject) => {
                mainApi.getAll(url, filter)
                    .then(response => {
                        commit('SET_LIST', response.data.data);
                        resolve(response)
                    })
                    .catch(error => {
                        console.log("error in getAll method in gate store: ", error);
                        commit('UN_SET_LIST');
                        reject(error)
                    })
            });
        },
        create({ commit }, { url, params }) {
            return new Promise((resolve, reject) => {
                mainApi.create(url, params)
                    .then(response => {
                        resolve(response)
                    })
                    .catch(error => {
                        console.log("error in create method in gate store: ", error);
                        reject(error)
                    });
            });
        },
        update({ commit }, { url, params }) {
            return new Promise((resolve, reject) => {
                mainApi.update(url, params)
                    .then(response => {
                        resolve(response)
                    })
                    .catch(error => {
                        console.log("error in update method in gate store: ", error);
                        reject(error)
                    })
            })
        },
        delete({ commit }, { url, id }) {
            return new Promise((resolve, reject) => {
                mainApi.delete(url, id)
                    .then(response => {
                        resolve(response);
                    })
                    .catch(error => {
                        console.log("error in delete method in gate store: ", error);
                        reject(error)
                    })
            });
        },
    }
}

第三步:现在,我们可以定义尽可能多的独立存储模块。如下所示,在每个模块中,我们只需要获取从视图检索到的数据并将其传递给mainApi(gate.js基类的函数和方法都是我们模块的一部分),并操作接收到的数据。

someStore.js

import Gate from '@/store/modules/gate'

let gate = new Gate();
const url = 'customUrl'

const gateStates = { ...gate.state }
const gateGetters = { ...gate.getters }
const gateMutations = { ...gate.mutations }

const state = {
    ...gateStates,
};
const getters = {
    ...gateGetters,
};
const mutations = {
    ...gateMutations,
};

const actions = {
    get: ({ commit }, id) => gate.actions.get({ commit }, { url, id }),
    getAll: ({ commit }) => gate.actions.getAll({ commit }, {url, filter: {}}),
    create: ({ commit }, params) => gate.actions.create({ commit }, { url, params }),
    update: ({ commit }, params) => gate.actions.update({ commit }, { url, params }),
    delete: ({ commit }, id) => gate.actions.delete({ commit }, { url, id })
};

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
};

最后,我们需要导入模块并将其定义为 "vuex store 模块",如下所示: store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import someModule from './modules/someModule'

Vue.use(Vuex)

export default new Vuex.Store({

    state: {},

    mutations: {},

    actions: {},

    modules: {
        someModule
    },

    plugins: {}

})

在这个示例中,我使用了另一个Promise,因为我需要直接在视图中获取服务器的响应。如果你只想在 store 中使用响应,则不需要这些 Promise,并且应该按以下方式将其删除:
gate.js中改为:
get({ commit }, { url, id }) {
    return new Promise((resolve, reject) => {
        mainApi.get(url, id)
            .then(response => {
                commit('SET_VIEW', response.data.data);
                resolve(response)
            })
            .catch(error => {
                commit('UN_SET_VIEW');
                console.log("error in getOne method in gate store: ", error);
                reject(error)
            })
    });
},

转换为这样

get({ commit }, { url, id }) {
    mainApi.get(url, id)
        .then(response => {
            commit('SET_VIEW', response.data.data);
        })
        .catch(error => {
            commit('UN_SET_VIEW');
            console.log("error in getOne method in gate store: ", error);
        })
},

这样,每个模块都有listview参数,可以在你的视图中轻松调用:

someView.vue

created() {
    store.dispatch('someModule/get', this.$route.params.id)
}

computed: {
    view() {
        return store.getters('someModule/view')
    }
}

0
作为个人挑战,我想创建一个纯ES6类来表达这个需求(意味着不允许使用注释)。因此,我创建了一个AbstractModule类来定义高级操作:
export default class AbstractModule {
    constructor(namespaced = true) {
        this.namespaced = namespaced;
    }

    _state () {
        return {}
    }

    _mutations () {
        return {}
    }

    _actions () {
        return {}
    }

    _getters () {
        return {}
    }

    static _exportMethodList (instance, methods) {
        let result = {};
        // Process methods when specified as array
        if (Array.isArray(methods)) {
            for (let method of methods) {
                if (typeof method === 'string') {
                    result[method] = instance[method].bind(instance);
                }

                if (typeof method === 'function') {
                    result[method.name] = method.bind(instance);
                }

                // else ignore
            }
        }

        // Process methods when specified as plain object
        if (typeof methods === "object") {
            for (const [name, method] of Object.entries(methods)) {
                if (typeof method === 'string') {
                    result[name] = instance[method].bind(instance);
                }

                if (typeof method === 'function') {
                    result[name] = method.bind(instance);
                }
            }
        }

        // Process methods when specified as single string
        if (typeof methods === 'string') {
            result[name] = instance[methods].bind(instance);
        }

        // Process methods when specified as single callback
        if (typeof methods === 'function') {
            result[name] = methods.bind(instance);
        }

        return result;
    }

    static module() {
        let instance = new this();
        console.log(instance);

        return {
            namespaced: instance.namespaced,
            state: instance._state(),
            mutations: AbstractModule._exportMethodList(instance, instance._mutations()),
            actions: AbstractModule._exportMethodList(instance, instance._actions()),
            getters: AbstractModule._exportMethodList(instance, instance._getters())
        }
    }
}

基于这个,我通过重新定义我想要自定义的parent方法来创建了自己的类模块

export default class QuestionModule extends AbstractModule{
    constructor(question) {
        super();
        this.question = question;
    }

    selectLine (state, line) {
        this.question.selectLine(line);
    }

    unselectLine (state, line) {
        this.question.unselectLine(line);
    }

    submit ({ state, commit, rootState }) {
        /** API call */
    }

    _state () {
        return this.question;
    }

    _mutations () {
        return [this.selectLine, this.unselectLine, this.validate];
    }

    _actions () {
        return this.submit;
    }
}

最后一步是通过调用module静态方法将我的类模块声明到Vuex存储中:
const store = new Vuex.Store({
  modules: {
      question: QuestionModule.module()
  },
  strict: process.env.NODE_ENV !== 'production'
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接