提交 d8a1cf62 编写于 作者: fxy060608's avatar fxy060608

feat(h5): add eventChannel

上级 cd6fe4bb
......@@ -184,6 +184,7 @@ declare namespace UniApp {
topWindow?: boolean
leftWindow?: boolean
rightWindow?: boolean
eventChannel?: any
}
interface PagesJsonPageOptions {
......
......@@ -206,7 +206,7 @@ export function defineTaskApi<T extends TaskApiLike, P = AsyncApiOptions<T>>(
fn: (
args: Omit<P, CALLBACK_TYPES>,
res: {
resolve: (res?: AsyncApiRes<P>) => void
resolve: (res?: AsyncApiRes<P> | void) => void
reject: (err?: string) => void
}
) => ReturnType<T>,
......@@ -237,7 +237,7 @@ export function defineAsyncApi<T extends AsyncApiLike, P = AsyncApiOptions<T>>(
fn: (
args: Omit<P, CALLBACK_TYPES>,
res: {
resolve: (res?: AsyncApiRes<P>) => void
resolve: (res: AsyncApiRes<P> | void) => void
reject: (errMsg?: string, errRes?: any) => void
}
) => void,
......
......@@ -1883,9 +1883,14 @@ var serviceContext = (function (vue) {
selectAllComponents: selectAllComponents
});
function getOpenerEventChannel() {
// TODO App
}
function initAppConfig(appConfig) {
const globalProperties = appConfig.globalProperties;
globalProperties.getOpenerEventChannel = getOpenerEventChannel;
{
const globalProperties = appConfig.globalProperties;
extend(globalProperties, wxInstance);
}
}
......
......@@ -2,10 +2,12 @@ import { AppConfig } from 'vue'
import { extend } from '@vue/shared'
import * as wxInstance from './componentWx'
import { getOpenerEventChannel } from './componentInstance'
export function initAppConfig(appConfig: AppConfig) {
const globalProperties = appConfig.globalProperties
globalProperties.getOpenerEventChannel = getOpenerEventChannel
if (__UNI_FEATURE_WX__) {
const globalProperties = appConfig.globalProperties
extend(globalProperties, wxInstance)
}
}
import { EventChannel } from '@dcloudio/uni-shared'
import { ComponentPublicInstance } from 'vue'
export function getOpenerEventChannel(this: ComponentPublicInstance) {
if (__PLATFORM__ === 'h5') {
if (this.$route) {
const meta = this.$route.meta
if (!meta.eventChannel) {
meta.eventChannel = new EventChannel(this.$page.id)
}
return meta.eventChannel
}
}
// TODO App
}
import { isFunction, extend, isString, hyphenate, isPlainObject, isArray, hasOwn, isObject, capitalize, toRawType, makeMap as makeMap$1, isPromise, invokeArrayFns as invokeArrayFns$1 } from "@vue/shared";
import { once, formatLog, passive, initCustomDataset, invokeArrayFns, normalizeTarget, isBuiltInComponent, ON_RESIZE, ON_APP_ENTER_FOREGROUND, ON_APP_ENTER_BACKGROUND, ON_SHOW, ON_HIDE, ON_PAGE_SCROLL, ON_REACH_BOTTOM, SCHEME_RE, DATA_RE, getCustomDataset, ON_ERROR, callOptions, PRIMARY_COLOR, removeLeadingSlash, getLen, debounce, NAVBAR_HEIGHT, parseQuery, ON_UNLOAD, ON_REACH_BOTTOM_DISTANCE, decodedQuery, WEB_INVOKE_APPSERVICE, ON_WEB_INVOKE_APP_SERVICE, updateElementStyle, ON_BACK_PRESS, parseUrl, addFont, scrollTo, RESPONSIVE_MIN_WIDTH, formatDateTime, ON_PULL_DOWN_REFRESH } from "@dcloudio/uni-shared";
import { once, formatLog, passive, initCustomDataset, invokeArrayFns, normalizeTarget, isBuiltInComponent, ON_RESIZE, ON_APP_ENTER_FOREGROUND, ON_APP_ENTER_BACKGROUND, ON_SHOW, ON_HIDE, ON_PAGE_SCROLL, ON_REACH_BOTTOM, EventChannel, SCHEME_RE, DATA_RE, getCustomDataset, ON_ERROR, callOptions, PRIMARY_COLOR, removeLeadingSlash, getLen, debounce, NAVBAR_HEIGHT, parseQuery, ON_UNLOAD, ON_REACH_BOTTOM_DISTANCE, decodedQuery, WEB_INVOKE_APPSERVICE, ON_WEB_INVOKE_APP_SERVICE, updateElementStyle, ON_BACK_PRESS, parseUrl, addFont, scrollTo, RESPONSIVE_MIN_WIDTH, formatDateTime, ON_PULL_DOWN_REFRESH } from "@dcloudio/uni-shared";
import { openBlock, createBlock, mergeProps, createVNode, toDisplayString, withModifiers, getCurrentInstance, defineComponent, ref, provide, computed, watch, onUnmounted, inject, onBeforeUnmount, reactive, onActivated, onMounted, nextTick, onBeforeMount, withDirectives, vShow, shallowRef, watchEffect, isVNode, Fragment, markRaw, createTextVNode, injectHook, onBeforeActivate, onBeforeDeactivate, renderList, onDeactivated, createApp, Transition, withCtx, KeepAlive, resolveDynamicComponent, renderSlot } from "vue";
import { initVueI18n, LOCALE_EN, LOCALE_ES, LOCALE_FR, LOCALE_ZH_HANS, LOCALE_ZH_HANT } from "@dcloudio/uni-i18n";
import { useRoute, createRouter, createWebHistory, createWebHashHistory, useRouter, isNavigationFailure, RouterView } from "vue-router";
......@@ -1465,9 +1465,21 @@ var wxInstance = /* @__PURE__ */ Object.freeze({
selectComponent,
selectAllComponents
});
function getOpenerEventChannel() {
{
if (this.$route) {
const meta = this.$route.meta;
if (!meta.eventChannel) {
meta.eventChannel = new EventChannel(this.$page.id);
}
return meta.eventChannel;
}
}
}
function initAppConfig(appConfig) {
const globalProperties = appConfig.globalProperties;
globalProperties.getOpenerEventChannel = getOpenerEventChannel;
if (__UNI_FEATURE_WX__) {
const globalProperties = appConfig.globalProperties;
extend(globalProperties, wxInstance);
}
}
......@@ -17373,24 +17385,32 @@ const navigateBack = /* @__PURE__ */ defineAsyncApi(API_NAVIGATE_BACK, ({ delta
getApp().$router.go(-delta);
return resolve();
}, NavigateBackProtocol, NavigateBackOptions);
function navigate(type, url, __id__) {
function navigate({ type, url, events }, __id__) {
const router = getApp().$router;
const { path, query } = parseUrl(url);
return new Promise((resolve, reject) => {
const state2 = createPageState(type, __id__);
router[type === "navigateTo" ? "push" : "replace"]({
path,
query,
force: true,
state: createPageState(type, __id__)
state: state2,
force: true
}).then((failure) => {
if (isNavigationFailure(failure)) {
return reject(failure.message);
}
return resolve(void 0);
if (type === "navigateTo") {
const eventChannel = new EventChannel(state2.__id__, events);
router.currentRoute.value.meta.eventChannel = eventChannel;
return resolve({
eventChannel
});
}
return resolve();
});
});
}
const navigateTo = /* @__PURE__ */ defineAsyncApi(API_NAVIGATE_TO, ({ url }, { resolve, reject }) => navigate(API_NAVIGATE_TO, url).then(resolve).catch(reject), NavigateToProtocol, NavigateToOptions);
const navigateTo = /* @__PURE__ */ defineAsyncApi(API_NAVIGATE_TO, ({ url, events }, { resolve, reject }) => navigate({ type: API_NAVIGATE_TO, url, events }).then(resolve).catch(reject), NavigateToProtocol, NavigateToOptions);
function removeLastPage() {
const page = getCurrentPage();
if (!page) {
......@@ -17400,7 +17420,7 @@ function removeLastPage() {
removePage(normalizeRouteKey($page.path, $page.id));
}
const redirectTo = /* @__PURE__ */ defineAsyncApi(API_REDIRECT_TO, ({ url }, { resolve, reject }) => {
return removeLastPage(), navigate(API_REDIRECT_TO, url).then(resolve).catch(reject);
return removeLastPage(), navigate({ type: API_REDIRECT_TO, url }).then(resolve).catch(reject);
}, RedirectToProtocol, RedirectToOptions);
function removeAllPages() {
const keys = getCurrentPagesMap().keys();
......@@ -17409,7 +17429,7 @@ function removeAllPages() {
}
}
const reLaunch = /* @__PURE__ */ defineAsyncApi(API_RE_LAUNCH, ({ url }, { resolve, reject }) => {
return removeAllPages(), navigate(API_RE_LAUNCH, url).then(resolve).catch(reject);
return removeAllPages(), navigate({ type: API_RE_LAUNCH, url }).then(resolve).catch(reject);
}, ReLaunchProtocol, ReLaunchOptions);
function removeNonTabBarPages() {
const curTabBarPageVm = getCurrentPageVm();
......@@ -17442,7 +17462,7 @@ function getTabBarPageId(url) {
}
}
const switchTab = /* @__PURE__ */ defineAsyncApi(API_SWITCH_TAB, ({ url }, { resolve, reject }) => {
return removeNonTabBarPages(), navigate(API_SWITCH_TAB, url, getTabBarPageId(url)).then(resolve).catch(reject);
return removeNonTabBarPages(), navigate({ type: API_SWITCH_TAB, url }, getTabBarPageId(url)).then(resolve).catch(reject);
}, SwitchTabProtocol, SwitchTabOptions);
const preloadPage = /* @__PURE__ */ defineAsyncApi(API_PRELOAD_PAGE, ({ url }, { resolve, reject }) => {
const path = url.split("?")[0];
......
......@@ -9,8 +9,10 @@ import { navigate } from './utils'
export const navigateTo = defineAsyncApi<API_TYPE_NAVIGATE_TO>(
API_NAVIGATE_TO,
({ url }, { resolve, reject }) =>
navigate(API_NAVIGATE_TO, url).then(resolve).catch(reject),
({ url, events }, { resolve, reject }) =>
navigate({ type: API_NAVIGATE_TO, url, events })
.then(resolve)
.catch(reject),
NavigateToProtocol,
NavigateToOptions
)
......@@ -19,7 +19,8 @@ export const reLaunch = defineAsyncApi<API_TYPE_RE_LAUNCH>(
API_RE_LAUNCH,
({ url }, { resolve, reject }) => {
return (
removeAllPages(), navigate(API_RE_LAUNCH, url).then(resolve).catch(reject)
removeAllPages(),
navigate({ type: API_RE_LAUNCH, url }).then(resolve).catch(reject)
)
},
ReLaunchProtocol,
......
......@@ -24,7 +24,7 @@ export const redirectTo = defineAsyncApi<API_TYPE_REDIRECT_TO>(
return (
// TODO exists 属性未实现
removeLastPage(),
navigate(API_REDIRECT_TO, url).then(resolve).catch(reject)
navigate({ type: API_REDIRECT_TO, url }).then(resolve).catch(reject)
)
},
RedirectToProtocol,
......
......@@ -48,7 +48,7 @@ export const switchTab = defineAsyncApi<API_TYPE_SWITCH_TAB>(
({ url }, { resolve, reject }) => {
return (
removeNonTabBarPages(),
navigate(API_SWITCH_TAB, url, getTabBarPageId(url))
navigate({ type: API_SWITCH_TAB, url }, getTabBarPageId(url))
.then(resolve)
.catch(reject)
)
......
import { parseUrl } from '@dcloudio/uni-shared'
import { EventChannel, parseUrl } from '@dcloudio/uni-shared'
import { isNavigationFailure, Router } from 'vue-router'
import { createPageState } from '../../../framework/setup/page'
......@@ -8,24 +8,36 @@ export type NavigateType =
| 'reLaunch'
| 'switchTab'
interface NavigateOptions {
type: NavigateType
url: string
events?: Record<string, any>
}
export function navigate(
type: NavigateType,
url: string,
{ type, url, events }: NavigateOptions,
__id__?: number
): Promise<undefined> {
): Promise<void | { eventChannel: EventChannel }> {
const router = getApp().$router as Router
const { path, query } = parseUrl(url)
return new Promise((resolve, reject) => {
const state = createPageState(type, __id__)
router[type === 'navigateTo' ? 'push' : 'replace']({
path,
query,
state,
force: true,
state: createPageState(type, __id__),
}).then((failure) => {
if (isNavigationFailure(failure)) {
return reject(failure.message)
}
return resolve(undefined)
if (type === 'navigateTo') {
const eventChannel = new EventChannel(state.__id__, events)
router.currentRoute.value.meta.eventChannel = eventChannel
return resolve({
eventChannel,
})
}
return resolve()
})
})
}
......@@ -863,6 +863,69 @@ const ON_APP_ENTER_FOREGROUND = 'onAppEnterForeground';
const ON_APP_ENTER_BACKGROUND = 'onAppEnterBackground';
const ON_WEB_INVOKE_APP_SERVICE = 'onWebInvokeAppService';
class EventChannel {
constructor(id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach((name) => {
this.on(name, events[name]);
});
}
}
emit(eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args);
}
fns.forEach((opt) => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter((opt) => opt.type !== 'once');
}
on(eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once(eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off(eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return;
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
}
else {
delete this.listener[eventName];
}
}
_clearCache(eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName, ...cacheArgs.shift()]);
}
}
}
_addListener(eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type,
});
}
}
function getEnvLocale() {
const { env } = process;
const lang = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
......@@ -888,6 +951,7 @@ exports.COMPONENT_NAME_PREFIX = COMPONENT_NAME_PREFIX;
exports.COMPONENT_PREFIX = COMPONENT_PREFIX;
exports.COMPONENT_SELECTOR_PREFIX = COMPONENT_SELECTOR_PREFIX;
exports.DATA_RE = DATA_RE;
exports.EventChannel = EventChannel;
exports.EventModifierFlags = EventModifierFlags;
exports.NAVBAR_HEIGHT = NAVBAR_HEIGHT;
exports.NODE_TYPE_COMMENT = NODE_TYPE_COMMENT;
......
......@@ -105,6 +105,24 @@ export declare const defaultRpx2Unit: {
declare type DictArray = [number, number][];
export declare class EventChannel {
id: number;
private listener;
private emitCache;
constructor(id: number, events?: NavigateToOptionEvents);
emit(eventName: string, ...args: any[]): number | undefined;
on(eventName: string, fn: EventChannelListener['fn']): void;
once(eventName: string, fn: EventChannelListener['fn']): void;
off(eventName: string, fn: EventChannelListener['fn']): void;
_clearCache(eventName: string): void;
_addListener(eventName: string, type: EventChannelListener['type'], fn: EventChannelListener['fn']): void;
}
declare interface EventChannelListener {
type: 'on' | 'once';
fn: (...args: any[]) => void;
}
export declare const EventModifierFlags: {
stop: number;
prevent: number;
......@@ -168,6 +186,8 @@ export declare interface IUniPageNode {
export declare const NAVBAR_HEIGHT = 44;
declare type NavigateToOptionEvents = Record<string, (...args: any[]) => void>;
export declare const NODE_TYPE_COMMENT = 8;
export declare const NODE_TYPE_ELEMENT = 1;
......
......@@ -859,10 +859,73 @@ const ON_APP_ENTER_FOREGROUND = 'onAppEnterForeground';
const ON_APP_ENTER_BACKGROUND = 'onAppEnterBackground';
const ON_WEB_INVOKE_APP_SERVICE = 'onWebInvokeAppService';
class EventChannel {
constructor(id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach((name) => {
this.on(name, events[name]);
});
}
}
emit(eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args);
}
fns.forEach((opt) => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter((opt) => opt.type !== 'once');
}
on(eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once(eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off(eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return;
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
}
else {
delete this.listener[eventName];
}
}
_clearCache(eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName, ...cacheArgs.shift()]);
}
}
}
_addListener(eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type,
});
}
}
function getEnvLocale() {
const { env } = process;
const lang = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE;
return (lang && lang.replace(/[.:].*/, '')) || 'en';
}
export { ACTION_TYPE_ADD_EVENT, ACTION_TYPE_CREATE, ACTION_TYPE_EVENT, ACTION_TYPE_INSERT, ACTION_TYPE_PAGE_CREATE, ACTION_TYPE_PAGE_CREATED, ACTION_TYPE_REMOVE, ACTION_TYPE_REMOVE_ATTRIBUTE, ACTION_TYPE_REMOVE_EVENT, ACTION_TYPE_SET_ATTRIBUTE, ACTION_TYPE_SET_TEXT, ATTR_CLASS, ATTR_STYLE, BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, EventModifierFlags, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_ADD_TO_FAVORITES, ON_APP_ENTER_BACKGROUND, ON_APP_ENTER_FOREGROUND, ON_BACK_PRESS, ON_ERROR, ON_HIDE, ON_LAUNCH, ON_LOAD, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, ON_PAGE_NOT_FOUND, ON_PAGE_SCROLL, ON_PULL_DOWN_REFRESH, ON_REACH_BOTTOM, ON_REACH_BOTTOM_DISTANCE, ON_READY, ON_RESIZE, ON_SHARE_APP_MESSAGE, ON_SHARE_TIMELINE, ON_SHOW, ON_TAB_ITEM_TAP, ON_THEME_CHANGE, ON_UNHANDLE_REJECTION, ON_UNLOAD, ON_WEB_INVOKE_APP_SERVICE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, debounce, decode, decodedQuery, defaultRpx2Unit, formatDateTime, formatLog, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeEventType, normalizeTarget, once, parseEventName, parseQuery, parseUrl, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
export { ACTION_TYPE_ADD_EVENT, ACTION_TYPE_CREATE, ACTION_TYPE_EVENT, ACTION_TYPE_INSERT, ACTION_TYPE_PAGE_CREATE, ACTION_TYPE_PAGE_CREATED, ACTION_TYPE_REMOVE, ACTION_TYPE_REMOVE_ATTRIBUTE, ACTION_TYPE_REMOVE_EVENT, ACTION_TYPE_SET_ATTRIBUTE, ACTION_TYPE_SET_TEXT, ATTR_CLASS, ATTR_STYLE, BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, EventChannel, EventModifierFlags, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_ADD_TO_FAVORITES, ON_APP_ENTER_BACKGROUND, ON_APP_ENTER_FOREGROUND, ON_BACK_PRESS, ON_ERROR, ON_HIDE, ON_LAUNCH, ON_LOAD, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, ON_PAGE_NOT_FOUND, ON_PAGE_SCROLL, ON_PULL_DOWN_REFRESH, ON_REACH_BOTTOM, ON_REACH_BOTTOM_DISTANCE, ON_READY, ON_RESIZE, ON_SHARE_APP_MESSAGE, ON_SHARE_TIMELINE, ON_SHOW, ON_TAB_ITEM_TAP, ON_THEME_CHANGE, ON_UNHANDLE_REJECTION, ON_UNLOAD, ON_WEB_INVOKE_APP_SERVICE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, debounce, decode, decodedQuery, defaultRpx2Unit, formatDateTime, formatLog, getCustomDataset, getEnvLocale, getLen, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeEventType, normalizeTarget, once, parseEventName, parseQuery, parseUrl, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle };
type NavigateToOptionEvents = Record<string, (...args: any[]) => void>
interface EventChannelListener {
type: 'on' | 'once'
fn: (...args: any[]) => void
}
export class EventChannel {
id: number
private listener: Record<string, EventChannelListener[]>
private emitCache: Record<string, any[][]>
constructor(id: number, events?: NavigateToOptionEvents) {
this.id = id
this.listener = {}
this.emitCache = {}
if (events) {
Object.keys(events).forEach((name) => {
this.on(name, events[name])
})
}
}
emit(eventName: string, ...args: any[]) {
const fns = this.listener[eventName]
if (!fns) {
return (
this.emitCache[eventName] || (this.emitCache[eventName] = [])
).push(args)
}
fns.forEach((opt) => {
opt.fn.apply(opt.fn, args)
})
this.listener[eventName] = fns.filter((opt) => opt.type !== 'once')
}
on(eventName: string, fn: EventChannelListener['fn']) {
this._addListener(eventName, 'on', fn)
this._clearCache(eventName)
}
once(eventName: string, fn: EventChannelListener['fn']) {
this._addListener(eventName, 'once', fn)
this._clearCache(eventName)
}
off(eventName: string, fn: EventChannelListener['fn']) {
const fns = this.listener[eventName]
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length; ) {
if (fns[i].fn === fn) {
fns.splice(i, 1)
i--
}
i++
}
} else {
delete this.listener[eventName]
}
}
_clearCache(eventName: string) {
const cacheArgs = this.emitCache[eventName]
if (cacheArgs) {
for (; cacheArgs.length > 0; ) {
this.emit.apply(this, [eventName, ...cacheArgs.shift()!])
}
}
}
_addListener(
eventName: string,
type: EventChannelListener['type'],
fn: EventChannelListener['fn']
) {
;(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type,
})
}
}
......@@ -8,5 +8,6 @@ export * from './utils'
export * from './query'
export * from './debounce'
export * from './constants'
export * from './EventChannel'
export * from './node/locale'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册