类似于redux状态机管理工具

231 阅读3分钟

//2019-08-30

js状态机管理,类似于redux,观察者模式的基础上添加了订阅监听机制,并且可以添加/删除中间件

//检测类型
function testType(target,type){
    let targetType = Object.prototype.toString.call(target).toLowerCase();
    return targetType.match(/\[object (\S*)\]/)[1] === type;
}
//数组工具
let arrayUtils = {
    filter: (arr,fn) => {
        let info = null;
        let status = true;
        arr.every((data,index) => {
            try{
                status = fn(data,index) ? false : true;
                if(!status){
                    info = {
                        data,index
                    }
                }
            }catch(e){
                console.log(e);
                status = false;
            }
            return status;
        })
        return info;
    } 
};
//创建store
function createStore(reducer){
    //检测reducer类型
    if(!testType(reducer,'function')){
        throw 'reducer为function类型,用以处理数据逻辑!';
    }
    /*
        * 私有属性:state,middleware
        * 保留方法:dispatch
        * 订阅列表:subscribe
    */
    let descriptor = Object.create(null);
    descriptor.state = undefined;
    descriptor.middleware = [];
    descriptor.dispatch = null;
    descriptor.subscribe = {};
    //构建store
    let store = new Proxy({
        state:null,
        middleware:null,
        //设置state
        setState(v){
            descriptor.state = v;
        },
        //提取state
        getState(){
            return this.state;
        },
        //派发
        dispatch(action){
            if(!testType(action,'object')){
                throw 'action为object类型!';
            }
            //设置state
            this.setState(reducer(action,this.getState()));
            //发布订阅
            this.publish(action);
        },
        //添加中间件
        addMiddleware(middleware){
            let isArray = testType(middleware,'array');
            if(isArray){
                descriptor.middleware.push.apply(descriptor.middleware,middleware);
            }else{
                descriptor.middleware.push(middleware);
            }
            //应用中间件
            this.applyMiddleware();
            //返回快捷卸载
            return () => {
                if(isArray){
                    middleware.forEach(mw => {
                        this.removeMiddleware(mw);
                    })
                }else{
                    this.removeMiddleware(middleware);
                }
            }
        },
        //删除中间件
        removeMiddleware(middleware){
            try{
                let info = arrayUtils.filter(descriptor.middleware,data => data == middleware);
                descriptor.middleware.splice(info.index,1);
                //应用中间件
                this.applyMiddleware();
            }catch(e){
                console.warn(e);
            }
            
        },
        //应用中间件
        applyMiddleware(){
            //保存原始dispatch
            if(testType(descriptor.dispatch,'null')){
                descriptor.dispatch = this.dispatch.bind(this);
            }
            //只做简单劫持store的dispatch,具体中间件逻辑在中间件中执行
            let next = descriptor.dispatch;
            //判断是否还存在中间件
            if(!descriptor.middleware.length){
                this.dispatch = next;
                return;
            }
            //混入中间件
            for(let i = descriptor.middleware.length - 1; i >= 0; i--){
                let middleware = descriptor.middleware[i];
                try{
                    this.dispatch = middleware(this,next);
                    next = this.dispatch;
                }catch(e){
                    console.log(e)
                }
            }
        },
        //发布
        publish(action){
            let state = this.getState();
            try{
                Object.keys(descriptor.subscribe).forEach(key => {
                    let status = key == '@@_global' ||  key == action.type || action.all;
                    //判断是否发布
                    if(status){
                        descriptor.subscribe[key].forEach(fn => {
                            fn(state);
                        })
                    }
                })
            }catch(e){
                console.warn(e);
            }

        },
        //订阅
        subscribe(action,fn){
            let argus = [].slice.call(arguments);
            if(!argus.length){
                console.warn('订阅参数出错!');
                return;
            }
            //遍历插入
            let key = '@@_global';
            argus.every((item,i) => {
                if(testType(item,'string')){
                    key = item;
                }else if(testType(item,'function')){
                    descriptor.subscribe[key] = descriptor.subscribe[key] ? descriptor.subscribe[key] : [];
                    descriptor.subscribe[key].push(item);
                    return false;
                }
                return true;
            });
            //返回取消订阅快捷方式
            return () => {
                this.unsubscribe(action,fn);
            }
        },
        //取消订阅
        unsubscribe(action,fn){
            let argus = [].slice.call(arguments);
            if(!argus.length){
                console.warn('取消订阅参数出错!');
                return;
            }
            //遍历查询
            let key = '@@_global';
            argus.every((item,i) => {
                if(testType(item,'string')){
                    key = item;
                }else if(testType(item,'function')){
                    //判断是否存在当前订阅队列中
                    try{
                        let info = arrayUtils.filter(descriptor.subscribe[key],f => f == item);
                        descriptor.subscribe[key].splice(info.index,1);
                    }catch(e){
                        console.warn(e);
                    }
                    return false;
                }
                return true;
            });
        }
    },{
        set(target,attr,value){
            switch(attr){
                case 'state':
                    console.warn('state为私有属性,禁止直接修改!请通过dispatch发起更新流程或者调用setState方法更新');
                    break;
                case 'middleware':
                    console.warn('middleware为私有属性,禁止直接修改!请通过addMiddleware/removeMiddleware方法更新');
                    break;
                default :
                    target[attr] = value;
                    break;
            }
            //返回store
            return target
        },
        get(target,attr){
            let result;
            switch(attr){
                case 'state':
                    //state:只作为数据存储,不涉及function,regexp等类型,故而采用json方式深度克隆
                    result = descriptor[attr];
                    result = testType(result,'undefined') ? result : JSON.parse(JSON.stringify(result));
                    break;
                case 'middleware':
                    result = descriptor[attr].slice();
                    break
                default :
                    result = target[attr];
                    break;
            }
            //返回attr值
            return result;
        }
    });
    //默认执行
    store.dispatch({
        type:'@@_init_state',
        all:true
    });
    //返回
    return store;
}
//合并reducer
function combineReducer(reducers,initstate = null){
    if(!testType(reducers,'object')){
        throw 'reducer集合为object类型!';
    }
    //action映射reducer
    let actionMapReducer = combineReducer.createActionMapReducer(reducers);
    return (action,state = initstate) => {
        state =  Object.assign({},state);
        //action处理全局
        if(action.all){
            //action通知全部执行reducer
            for(let key in reducers){
                state[key] = reducers[key](action,state[key]);
            }
        }else{
            //action通过event映射执行reducer
            let reducerGroup = actionMapReducer[action.type] ? actionMapReducer[action.type] : [];
            //合并全局reducer
            reducerGroup.concat(actionMapReducer['@@_global']).forEach(item => {
                state[item.key] = item.reducer(action,state[item.key]);
            })
        }
        return state;
    };
}
//构建事件对应reducer映射列表
combineReducer.createActionMapReducer = reducers => {
    let mapList = Object.create(null);
    //全局reducer
    mapList['@@_global'] = [];
    try{
        Object.keys(reducers).forEach(key => {
            let reducer = reducers[key];
            //判断reducer是否存在事件列表
            try{
                reducer.actions.forEach(action => {
                    mapList[action] = !mapList[action] ? [] : mapList[action];
                    mapList[action].push({
                        key,
                        reducer
                    })
                });
            }catch(e){
                mapList['@@_global'].push({
                    key,
                    reducer
                })
            }
        })
    }catch(e){
        throw '构建事件映射列表失败!';
    }
    //返回
    return mapList
};
//创建reducer
function createReducer(reducer,actions){
    if(!actions){ return reducer}
    if(testType(actions,'string')){
        reducer.actions = [actions];
    }else if(testType(actions,'array')){
        reducer.actions = actions;
    }
    //返回
    return reducer;
}
//创建同步中间件
function createSyncMiddleware(middleware){
    return (store, next) => action => {
        try{
            //兼容dispatch方法的function参数
            action(store);
        }catch(e){
            try{
                console.group('action-type:' + action.type);
                console.log('action',action)
                middleware(store,next,action);
                console.groupEnd();
            }catch(e){
                console.log(e)
            }
        }
    };
}


/*============导出========*/

export {
    createStore,
    combineReducer,
    createReducer,
    createSyncMiddleware
}

使用:

import {
    createStore,
    combineReducer,
    createReducer,
    createSyncMiddleware
} from './main';
/******************测试*******************/
let reducer1 = createReducer((action,state={name:'set'}) => {
    state = JSON.parse(JSON.stringify(state));
    switch(action.type){
        case 'set-name':
                state.name = action.payload.name
            break;
            
    }
    return state;
},'set-name');

let reducer2 = createReducer((action,state={name:'init'}) => {
    state = JSON.parse(JSON.stringify(state));
    switch(action.type){
        case 'init-name':
                state.name = action.payload.name
            break;
    }
    return state;
},'init-name');

let reducer = combineReducer({
    reducer1,
    reducer2
},{
    reducer1:{
        name:1
    },
    reducer2:{
        name:2
    }
});

//中间件-同步
let syncMiddleware = createSyncMiddleware((store,next,action) => {
    next(action);
    console.log('state:',store.getState());
});

//实例化
let store = createStore(reducer);
//添加中间件
let uninstall = store.addMiddleware([
    syncMiddleware
]);

//订阅
let unsubscribe = store.subscribe('init-name',(state) => {
    console.log(state,'接收订阅')
});
//派发
store.dispatch({
    type:'set-name',
    payload:{
        name:'set-蹦恰恰'
    }
});
store.dispatch({
    type:'init-name',
    payload:{
        name:'init-蹦恰恰'
    }
});