/** * @author dbkillerf6 * @since 2020-04-10 */ import { cloneDeep } from 'lodash'; import type { OnGlobalStateChangeCallback, MicroAppStateActions } from './interfaces'; let globalState: Record = {}; const deps: Record = {}; // 触发全局监听 function emitGlobal(state: Record, prevState: Record) { Object.keys(deps).forEach((id: string) => { if (deps[id] instanceof Function) { deps[id](cloneDeep(state), cloneDeep(prevState)); } }); } export function initGlobalState(state: Record = {}) { if (state === globalState) { console.warn('[qiankun] state has not changed!'); } else { const prevGlobalState = cloneDeep(globalState); globalState = cloneDeep(state); emitGlobal(globalState, prevGlobalState); } return getMicroAppStateActions(`global-${+new Date()}`, true); } export function getMicroAppStateActions(id: string, isMaster?: boolean): MicroAppStateActions { return { /** * onGlobalStateChange 全局依赖监听 * * 收集 setState 时所需要触发的依赖 * * 限制条件:每个子应用只有一个激活状态的全局监听,新监听覆盖旧监听,若只是监听部分属性,请使用 onGlobalStateChange * * 这么设计是为了减少全局监听滥用导致的内存爆炸 * * 依赖数据结构为: * { * {id}: callback * } * * @param callback * @param fireImmediately */ onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) { if (!(callback instanceof Function)) { console.error('[qiankun] callback must be function!'); return; } if (deps[id]) { console.warn(`[qiankun] '${id}' global listener already exists before this, new listener will overwrite it.`); } deps[id] = callback; const cloneState = cloneDeep(globalState); if (fireImmediately) { callback(cloneState, cloneState); } }, /** * setGlobalState 更新 store 数据 * * 1. 对输入 state 的第一层属性做校验,只有初始化时声明过的第一层(bucket)属性才会被更改 * 2. 修改 store 并触发全局监听 * * @param state */ setGlobalState(state: Record = {}) { if (state === globalState) { console.warn('[qiankun] state has not changed!'); return false; } const changeKeys: string[] = []; const prevGlobalState = cloneDeep(globalState); globalState = cloneDeep( Object.keys(state).reduce((_globalState, changeKey) => { if (isMaster || _globalState.hasOwnProperty(changeKey)) { changeKeys.push(changeKey); return Object.assign(_globalState, { [changeKey]: state[changeKey] }); } console.warn(`[qiankun] '${changeKey}' not declared when init state!`); return _globalState; }, globalState), ); if (changeKeys.length === 0) { console.warn('[qiankun] state has not changed!'); return false; } emitGlobal(globalState, prevGlobalState); return true; }, // 注销该应用下的依赖 offGlobalStateChange() { delete deps[id]; return true; }, }; }