UNPKG

@morjs/runtime-base

Version:
244 lines 8.85 kB
import { __spreadArray } from "tslib"; import { logger } from './logger'; import { asArray } from './utils/asArray'; var HookInvokeState; (function (HookInvokeState) { HookInvokeState["pausing"] = "pausing"; HookInvokeState["resuming"] = "resuming"; })(HookInvokeState || (HookInvokeState = {})); /** * 同步 Hook */ var SyncHook = /** @class */ (function () { /** * @constructor * @param name - Hook 名称 */ function SyncHook(name, sharedState) { this.name = name || ''; this.taps = []; this.sharedState = sharedState; } /** * 返回 hook 是否已被使用 */ SyncHook.prototype.isUsed = function () { return this.taps.length > 0; }; /** * 创建 hook alias * @param name Hook 名称 */ SyncHook.prototype.alias = function (name) { var aliasHook = new SyncHook(name, this.sharedState); // 这里直接使用 taps 数组, 方便 alias Hook 共用 aliasHook.taps = this.taps; return aliasHook; }; /** * 添加 hook 插件 * @param nameOrOptions 名称或选项 * @param fn 函数 */ SyncHook.prototype.tap = function (nameOrOptions, fn) { var _a; var name; var stage; if (typeof nameOrOptions === 'string') { name = nameOrOptions; stage = 0; } else { name = nameOrOptions.name; stage = (_a = nameOrOptions.stage) !== null && _a !== void 0 ? _a : 0; } if (name == null) { logger.error("$hooks.".concat(this.name, ".tap \u7F3A\u5C11 name")); } this.taps.push({ type: 'sync', name: name, stage: stage, fn: fn }); }; /** * 执行 hook * @param context 上下文 * @param args 参数列表 */ SyncHook.prototype.call = function (context) { var _a; var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } // 按照 stage 排序 var taps = this.taps.sort(function (a, b) { return a.stage - b.stage; }); for (var _b = 0, taps_1 = taps; _b < taps_1.length; _b++) { var tap = taps_1[_b]; // 暂停中的 hook 将所有调用保存到堆栈中,等待后续恢复 if (this.isPausing()) { this.sharedState.stack.push([this.name, tap, context, args]); } else { try { (_a = tap.fn).call.apply(_a, __spreadArray([context], args, false)); } catch (err) { logger.error(this.name, tap.name, err); } } } }; SyncHook.prototype.isPausing = function () { var _a; var state = this.sharedState; // 当触发了 $hooks.pause 暂停,若未传入需要指定暂停的 hooks 则暂停所有生命周期触发 // 若传入了某些指定的 hooks 数组,则只暂停这些传入 hooks if ((state === null || state === void 0 ? void 0 : state.state) !== HookInvokeState.pausing) return false; if (((_a = state.hooksNameList) === null || _a === void 0 ? void 0 : _a.length) === 0) return true; if (state.hooksNameList.indexOf(this.name) !== -1) return true; return false; }; return SyncHook; }()); export { SyncHook }; // 搜集所有创建的 hooks 实例 // 主要用于 调试或检查 var HOOKS_INSTANCES = {}; /** * 创建 hooks 对象 * @param reason Hooks 创建原因 * @returns hooks 对象 */ export function createHooks(reason) { var sharedState = { state: HookInvokeState.resuming, stack: [], hooksNameList: [] }; var appOnConstruct = new SyncHook('appOnConstruct', sharedState); var pageOnConstructHook = new SyncHook('pageOnConstruct', sharedState); var componentOnInitHook = new SyncHook('componentOnInit', sharedState); var componentDidMountHook = new SyncHook('componentDidMount', sharedState); var componentDidUnmountHook = new SyncHook('componentDidUnmount', sharedState); var componentOnError = new SyncHook('componentOnError', sharedState); var hooks = { /* App 相关 hooks */ appOnConstruct: appOnConstruct, // appOnInit 已废弃, 这里出于兼容性暂不移除 appOnInit: appOnConstruct.alias('appOnInit'), appOnLaunch: new SyncHook('appOnLaunch', sharedState), appOnError: new SyncHook('appOnError', sharedState), appOnShow: new SyncHook('appOnShow', sharedState), appOnHide: new SyncHook('appOnHide', sharedState), appOnPageNotFound: new SyncHook('appOnPageNotFound', sharedState), appOnUnhandledRejection: new SyncHook('appOnUnhandledRejection', sharedState), /* Page 相关 hooks */ pageOnConstruct: pageOnConstructHook, // pageOnInit 已废弃, 这里出于兼容性暂不移除 pageOnInit: pageOnConstructHook.alias('pageOnInit'), pageOnLoad: new SyncHook('pageOnLoad', sharedState), pageOnReady: new SyncHook('pageOnReady', sharedState), pageOnShow: new SyncHook('pageOnShow', sharedState), pageOnHide: new SyncHook('pageOnHide', sharedState), pageOnUnload: new SyncHook('pageOnUnload', sharedState), /* Component 相关 hooks */ componentOnConstruct: new SyncHook('componentOnConstruct', sharedState), componentOnInit: componentOnInitHook, componentOnCreated: componentOnInitHook.alias('componentOnCreated'), componentDidMount: componentDidMountHook, componentOnAttached: componentDidMountHook.alias('componentOnAttached'), componentDidUnmount: componentDidUnmountHook, componentOnDetached: componentDidUnmountHook.alias('componentOnDetached'), componentOnError: componentOnError, pause: function (hooksNameList) { sharedState.state = HookInvokeState.pausing; sharedState.hooksNameList = hooksNameList || []; }, resume: function () { var _a; sharedState.state = HookInvokeState.resuming; var stackItem = sharedState.stack.shift(); while (stackItem) { var name_1 = stackItem[0], tap = stackItem[1], context = stackItem[2], args = stackItem[3]; try { tap === null || tap === void 0 ? void 0 : (_a = tap.fn).call.apply(_a, __spreadArray([context], args, false)); } catch (error) { logger.error(name_1, tap.name, error); } stackItem = sharedState.stack.shift(); } } }; // 记录创建的所有 hooks HOOKS_INSTANCES[reason] = HOOKS_INSTANCES[reason] || []; HOOKS_INSTANCES[reason].push({ createdAt: +new Date(), hooks: hooks }); return hooks; } /** * 获取所有 hooks */ export function getAllHooks() { return HOOKS_INSTANCES; } /** * 获取全局共享属性,用于作为原子化的兜底实现 * 1. 首先查找上下文中的属性 * 2. 如果不存在,则查找 getApp 中的 * 3. 如果不存在,则查找 小程序环境的 globalObject, 如 my 中是否存在 */ export var hooks = createHooks('default'); /** * 应用插件 * @param hooks Hooks * @param plugins 插件列表 */ export function applyPlugins(hooks, plugins) { var pluginsNames = []; plugins.forEach(function (plugin) { try { plugin.apply(hooks); pluginsNames.push(plugin.pluginName); } catch (err) { logger.error("[plugin ".concat(plugin.pluginName, "]: \u521D\u59CB\u5316\u62A5\u9519"), err); } }); return pluginsNames; } /** * 应用 Solutions * @param hooks Hooks * @param solutions 插件集列表 */ export function applySolutions(hooks, solution) { var solutions = asArray(solution); var plugins = []; try { solutions.forEach(function (solution) { var _a; if (typeof solution === 'function') { plugins = plugins.concat(((_a = solution()) === null || _a === void 0 ? void 0 : _a.plugins) || []); } else { logger.error("\u521D\u59CB\u5316\u8FD0\u884C\u65F6\u63D2\u4EF6\u5931\u8D25, \u539F\u56E0: ".concat(solution, " \u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 solution")); } }); } catch (err) { logger.error("\u521D\u59CB\u5316\u8FD0\u884C\u65F6\u63D2\u4EF6\u5931\u8D25, \u539F\u56E0: ".concat(err)); } return applyPlugins(hooks, plugins); } //# sourceMappingURL=hooks.js.map