//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-蹦恰恰'
}
});