我们来更好的使用Vuex吧 - Vuex交互规范

1,308 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

前言

1、编码规范可以尽可能的减少一个软件的维护成本;

2、改善软件的可读性,可以让开发人员尽快而彻底地理解新的代码;

3、最大限度的提高团队开发的合作效率;

4、让开发人员养成好的编码习惯,锻炼出更加严谨的思维;

规范要点

根据业务拆分成modules

应用层级的状态应该集中到单个 store 对象中、根据业务拆分成modules,便于维护和阅读

# 正例
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块      
#反例

└── store
    ├── index.js          # 包含项目中所有模块状态(例如:购物车模块+产品模块+用户模块),结构臃肿,高耦合,不易维护。
    ├── actions.js        # 根级别的 action
    └── mutations.js      # 根级别的 mutation

提交 mutation 是更改状态的唯一方法。

提交 mutation 是更改状态的唯一方法,并且这个过程是同步的,异步逻辑都应该封装到 action 里面

#正例
// user.js
// actions
const actions = {
  getUserInfo ({ commit, state }, token) {
    // 异步操作
    login(token).then((userInfo)=>{
        commit('UPDATE_USER_INFO',userInfo)
    })
  }
}
 
 
// mutations
const mutations = {
  UPDATE_USER_INFO (state, userInfo) {
    state.userInfo = userInfo
  }
}
#反例
// login.vue
mothed: {
  getUserInfo (token) {
    // 在业务类中直接对store.state.userInfo进行直接负值
    login(token).then((userInfo)=>{
        this.$store.state.userInfo = userInfo
    })
  }
}

mutation 中的函数命名采用大写+"_"。

action中函数使用驼峰命名法,mutation 中的函数命名采用大写+"_" ,提高代码阅读效率。

# 正例
// user.js
// actions
const actions = {
    // 函数命名为驼峰
  getUserInfo ({ commit, state }, token) {
    login(token).then((userInfo)=>{
        // 调用的函数名为大写,潜规则表示修改state,也是唯一修改改state的方法
        commit('UPDATE_USER_INFO',userInfo)
    })
  }
}
 
 
// mutations
const mutations = {
    // 函数命名为全大写
  UPDATE_USER_INFO (state, userInfo) {
    state.userInfo = userInfo
  }
}
 
 
// loginOut.vue
import { mapMutations } from 'vuex';
mothed: {
    ...mapMutations(['UPDATE_USER_INFO']),
    // 业务逻辑清除缓存
    clearData() {
        // 调用的函数名为全大写,表示此方法为store 中的mutation方法,便于理解
        this.UPDATE_USER_INFO(null);
    }
}
#反例
// user.js
// actions
const actions = {
    // 函数命名为驼峰
  getUserInfo ({ commit, state }, token) {
    login(token).then((userInfo)=>{
        // 调用的函数名为驼峰,有可能是action,有可能是mutation
        commit('updateUserInfo',userInfo)
    })
  }
}
 
 
// mutations
const mutations = {
    // 函数命名为驼峰
  updateUserInfo (state, userInfo) {
    state.userInfo = userInfo
  }
}
 
 
// loginOut.vue
import { mapMutations } from 'vuex';
mothed: {
    ...mapMutations(['updateUserInfo']),
    // 业务逻辑清除缓存
    clearData() {
        // 调用的函数名为驼峰,则该方法有可能是本文件定义的函数,有可能是actions,有可能是mutation,不利于高效阅读
        this.updateUserInfo(null);
    }
}

action中返回promise。

action中返回promise,便于在业务代码中衔接逻辑

#正例
// user.js
actions: {
        getUserInfo({ commit, state }) {
            // 返回promise
            return getUserInfoReq({
                token
            })
            .then(data => {
                commit(' UPDATE_USER_INFO',data)               
            })
            .catch(error => {
                commit('CLEAR_USER_INFO',null)
            })
        }
    },
 
// login.vue
import { mapActions } from 'vuex';
mothed: {
    ...mapActions(['getUserInfo']),
    // 登录
    login() {
        // 调用action
        this.getUserInfo.then(()=>{
            // 此时机能确保用户本地用户缓存已经更新,如需获取门户权限则可直接衔接
            // code
        }).catch(()=>{
            // 此时机能准确表示与后台交互失败,可衔接清除本地数据逻辑
            // code
        });
    }
}
#反例
// user.js
actions: {
    getUserInfo({ commit, state }) {
        // 不返回promise
        getUserInfoReq({
            token
        }).then(data => {
            commit(' UPDATE_USER_INFO',data)
        }).catch(error => {
            commit('CLEAR_USER_INFO',null)
        })
    }
},
 
// login.vue
import { mapActions } from 'vuex';
mothed: {
    ...mapActions(['getUserInfo']),
    // 登录
    login() {
        // 调用action
        this.getUserInfo();
        // 此时机为action异步操作,直接衔接业务逻辑时,可能网络请求还未响应,存在逻辑漏洞
    }
}

如果项目只有一个容器,所有内容都依赖于容器内的vuex,就需要用到vux的解耦,否则会影响首屏加载速度。

解藕原因,项目只有一个容器,所有内容都依赖于容器内的vuex,会影响首屏加载速度

实现一个vue插件

1.在带有isVue属性的组件,具备动态注册vuex的能力

2.在组件beforeCreate周期组册store

3.需要注册vuex的组件,在组件导出的时候,需要把store挂载到window._wgSpa下

// plugin.js
export default function install(vue) {
    vue.mixin({
        beforeCreate() {
            if (this.$options.isVuex) {
                const { name } = this.$options
                if (!window._wgSpa) {
                    console.warn('window._wgSpa::: is undefined')
                }
                this.$store.registerModule(name, window._wgSpa?.pages[name]?.store)
            }
        },
    })
}
 
// main.js
import plugin from './plugin'
Vue.use(plugin)

组件内部导出

// index.vue
export default {
  name: "accessControl",
  isVuex: true
}
 
// index.js
import components from './index.vue'
 
const state = {
    test: '',
}
 
const mutations = {
    INIT_META_DATA: (state, data) => {
        state.test = data
    },
}
 
const actions = {
    async initData({ commit }) {
    }
}
 
if (!window._wgSpa) {
    window._wgSpa = {}
}
 
window._wgSpa.pages = Object.assign({
    accessControl: {
        pages: components,
        name: components.name,
        store: {
            state,
            mutations,
            actions
        }
    }
}, window._wgSpa?.pages)
 
export default components