在vue项目中有时某些组件之间需要共同维护一个全局状态,使用vuex又没有必要的时候,可使用此响应式全局状态管理。
功能
- 全局状态管理
- 类型检查
- 默认值设置
- 修改状态监控
- 响应式
使用
// 定义状态
const storeConfig = {
// 当前套餐id
count: {
type: Number,
default: 0
},
str: {
type: [String, undefined],
default: ''
}
};
const store = new StoreFactory(storeConfig);
Vue.prototype.$state = Vue.observable(store.state); // 响应式挂载state
Vue.prototype.$store = store; // 响应式挂载state
// vue组件
export default {
mounted() {
console.log(this.$state.count); // 0
this.$store.setState('count', 1); // setState -> 1
this.$store.setState('count', 'string'); // type check error
},
watch: {
$state: {
deep: true,
handler() {
console.log('$state change:', this.$state);
}
},
'$state.count': {
deep: true,
handler() {
console.log('$state.count change:', this.$state.count);
}
}
}
}
源码
/*
* 作者: Alan
* 日期:2020.11.19
* 功能:store模式:简单易用的全局状态管理工具
*/
/**
* 构造函数实现store模式,简单易用的全局状态管理工具
* @class {Function} Store
* @param {Object} initialState 初始化状态对象
*/
function StoreFactory(option) {
const Vue = option.Vue;
const initialState = option.states;
// 要返回的状态映射map
this.state = {};
for (let prop in initialState) {
if (initialState.hasOwnProperty(prop)) {
const state = initialState[prop];
const type = state.type;
const typeStr = typeOf(type);
if (!['function', 'array', 'undefined', 'null'].includes(typeStr)) {
throw new Error('Type of state must be one of: Array, Constructor, undefined, null!');
}
this.state[prop] = initialState[prop].default;
}
}
this.state = Vue.observable(this.state);
/**
* 设置单个状态,会判断属性是否在satate中初始化,并且检查类型是否一致
* @param {String} prop 状态名
* @param {String} val 状态的值
*/
this.setSingleState = function(prop, val) {
const typeCheckSuccess = typeCheck(prop, val);
if (typeCheckSuccess === true) {
console.log('setState:', prop, '->', val);
this.state[prop] = val;
} else {
throw new Error(typeCheckSuccess);
}
};
/**
* 设置状态,参数可以用对象一次修改多个状态,也可以由键值对修改单个状态
* @param {String|Object} state 状态名|状态名和状态值键值对组成的对象
* @param {Any} val 状态的值|第一个参数是对象时,不需要第二个参数
*/
this.setState = function(state, val) {
if (typeOf(state) === 'object') {
for (let prop in state) {
if (state.hasOwnProperty(prop)) {
this.setSingleState(prop, state[prop]);
}
}
} else {
this.setSingleState(state, val);
}
};
/**
* 获取状态,只可以通过此方法获取状态的值
* @param {String} prop 状态名
* @return {String} val 返回状态的值
*/
this.getState = function(prop) {
if (prop === undefined) {
return this.state;
}
if (!initialState.hasOwnProperty(prop)) {
console.error(`The prop ${prop} of state is undefined!'`);
}
// console.log('getState:', prop);
return this.state[prop];
};
/**
* 状态类型校验
* @param {String} prop 状态名
* @param {Any} value 状态值
* @return {Boolen|String} 校验是否成功
*/
function typeCheck (prop, value) {
let errorMsg = '';
if (initialState.hasOwnProperty(prop)) {
const state = initialState[prop];
const type = state.type;
const typeStr = typeOf(type);
if (typeStr === 'function') {
if (!isExpectType(value, type)) {
errorMsg = `The type of the state ${prop} must be ${type.name}, but get ${typeOf(value)}!'`;
}
} else if (typeStr === 'array') {
let typeCheckSuccess = false;
for (let i = 0; i < type.length; i ++) {
const constructorFun = type[i];
if (isExpectType(value, constructorFun)) {
typeCheckSuccess = true;
break;
}
}
if (!typeCheckSuccess) {
const typeStr = type.map(fun => fun.name).join('|');
errorMsg = `The type of the state ${prop} must be one of ${typeStr}, but get ${typeOf(value)}!'`;
}
} else if (type === undefined || type === null) {
if (value !== type) {
errorMsg = `The type of the state ${prop} must be ${type}, but get ${typeOf(value)}!'`;
}
} else {
errorMsg = 'State type error!';
}
} else {
errorMsg = `The prop ${prop} of state is not defined!'`;
}
return errorMsg ? errorMsg : true;
}
/**
* 是否是预期设置的类型
* @param {Any} value 状态值
* @param {Function} type 类型:构造函数或undefined、null
* @return {Boolean} 是否是预期设置的类型
*/
function isExpectType (value, type) {
if (type === undefined || type === null) {
return value === value;
}
return typeOf(value) === type.name.toLowerCase();
}
/**
* 判断变量类型
* @param {Any} param 参数
* @return {String} 变量类型
*/
function typeOf(param) {
return Object.prototype.toString.call(param).slice(8, -1).toLowerCase();
}
}