浅析vuex

84 阅读1分钟

为什么需要引入vuex

当我们构建简单的应用的时候,没有很多很复杂的页面通信的需求,通过data以及bus可以处理绝大多数的信息通信,这样就可以完成对中小型项目的状态管理,但如果我们需要管理一个大的项目(例如商城),我们需要考虑如何更好的进行状态管理,所以vuex就成为了自然而然的选择。

vuex几个要素

state
const CommonState: any = {
    leftNodeTrees: [],
    userInfo: {
        img: '',
        name: '',
        misId: '',
    },
};
getters
const G = {
    leftNodeTrees(state) {
        return state.leftNodeTrees;
    },
    userInfo(state) {
        return state.userInfo;
    },
};
actions
    const A = {
    // /** 普通调用的例子 */
    // async testApi() {
    //   await api.getSchool()
    // },
    setLeftNodeTreeData({ commit }, params) {
        commit('SET_TREE_DATA', params);
    },
    setUserInfo({ commit }: any, params) {
        commit('SET_USER_INFO', params);
    },
};
mutations
export const M = {
    SET_TREE_DATA(localState, params) {
        localState.leftNodeTrees = params;
    },
    SET_USER_INFO(localState, params) {
        localState.userInfo = params;
    },
};
高级用法 modules
    const user = {
    // 模块化并设置命名空间 
    namespaced:true,
    state:{
        userInfo:{
            id:110,
            username:'laoxie'
        }
    },
    getters:{
        isLogin(state){
            return !!state.userInfo.id
        }
    },
    mutations:{
        login(state,payload){
            state.userInfo = payload
        },
        logout(state){
            state.userInfo = {}
        }
    },
    actions:{
        login(ctx,payload){
            ajax('/login',payload).then(data=>{
                ctx.commit('login',data)
            })
        }
    }
}
const store = new Vuex({
    modules:{
        user
    }
})

mapActions mapGetters mapState mapMutations

    {
    computed:{
        // 把同名属性userInfo映射到computed中,以下两行代码等效
        // userInfo(){return this.$store.state.userInfo}
        ...mapState(['userInfo']), 
        
        // 如需更改名字,则可使用对象形式(字符串或函数形式)
        ...mapState({user:'userInfo',currentUser:state=>state.userInfo})
        
        // mapGetters采用对象参数时不支持函数写法
        ...mapGetters(['isLogin']),
        ...mapGetters({logined:'isLogin'}),
    },
    methods:{
        // 以下两种写法等效于:logout(){this.$store.commit('logout')}
        ...mapMutations(['logout']), 
        ...mapActions({
            logout(commit,payload){
                // commit固定为第一个参数
                // payload为调用logout时传入的参数,不传则为undefined
                commit('logout')
            }
        }),
        
        // 以下两种写法等效于:login(user){this.$store.dispatch('login',user)}
        ...mapActions(['login']),
        ...mapActions({
            login(dispatch,payload){
                // dispatch固定为第一个参数
                // payload为调用login时传入的参数,不传则为undefined
                dispatch('login',payload)
            }
        })
    }
}

实现一个简单的vuex

    // pvuex.js
let Vue;
function install(Vue_){
    Vue = Vue_;
    // 混入一个beforeCreate生命周期函数,在Vue原型里注册$store属性
    Vue.mixin({
        beforeCreate() {
            if(this.$options.store){
                Vue.prototype.$store = this.$options.store;
            }
        },
    })
}
class Store {
    constructor(options){
        // 作为在commit执行中的标识符
        this._committing = false
        // 保存用户的mutations、actions、getters的配置
        this._mutations = options.mutations;
        this._actions = options.actions;
        this._getters = options.getters;
        // 代表是严格模式下,不能直接给state里的状态值赋值,只可通过commit的方式提交修改
        this.strict = options.strict;
        // 定义配置在Vue里的computed,用Vue的computed特性实现getters机制
        let computed = {};
        // 暴露给外面使用的getters
        this.getters = {};
        // 把this保存一份
        const store = this;
        // 遍历每个_getters里的方法,封装一个高阶函数,目的是往遍历的方法里传递一个state
        Object.keys(this._getters).forEach(key => {
            const fn = this._getters[key];
            computed[key] = function(){
                return fn(store.state);
            }
            // 给store.getters的每个属性做一层访问代理,并封印getters里属性的setter
            Object.defineProperty(store.getters, key, {
                get(){
                    return store._vm[key];
                },
                set(val){
                    console.error('getters not set');
                    return;
                }
            });
        });
        // 借鸡生蛋,借助new Vue完成数据options.state的响应式
        this._vm = new Vue({
            data: {
                $$state: options.state
            },
            computed
        });

        // 如果是严格模式,则定义个用户watch,在每个state里的值改变的时候调用assert方法
        if(this.strict){
            this._vm.$watch(function () { return this._data.$$state }, (v) => {
                assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
              }, { deep: true, sync: true });
        }

        // 绑定commit和dispatch函数中的this指向
        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
    }
    // 通过存取器获取响应后的state
    get state(){
        return this._vm._data.$$state;
    }
    // 直接覆盖state,将抛出错误
    set state(val){
        throw new Error();
    }
    
    commit(type, payload){
        const entry = this._mutations[type];
        if(!entry){
            console.error('unkown mutation type');
        }
        this._wrappedCommit(() => entry(this.state,payload))
    }

    dispatch(type, payload){
        const entry = this._actions[type];
        if(!entry){
            console.error('unkown action type');
        }
        entry(this,payload);
    }
    // 封装commit执行
    _wrappedCommit (fn) {
        const committing = this._committing;
        this._committing = true;
        fn();
        this._committing = committing;
    }
}

// 不合法抛出错误
function assert (condition, msg) {
  if (!condition) throw new Error(`[vuex] ${msg}`);
}

export default { Store , install}