diff --git a/packages/shims-uni-app.d.ts b/packages/shims-uni-app.d.ts index bd1d5871d5dc4b1efc42eaddf6eb5c077f1c5621..5d34c4650fb5f1603dced0dfc1d2156faf28d6ea 100644 --- a/packages/shims-uni-app.d.ts +++ b/packages/shims-uni-app.d.ts @@ -184,6 +184,7 @@ declare namespace UniApp { topWindow?: boolean leftWindow?: boolean rightWindow?: boolean + eventChannel?: any } interface PagesJsonPageOptions { diff --git a/packages/uni-api/src/helpers/api/index.ts b/packages/uni-api/src/helpers/api/index.ts index 4283d5953bb878a744645970ddff865f0f2cd319..53aed461832978d6306756476adb07bad3daafed 100644 --- a/packages/uni-api/src/helpers/api/index.ts +++ b/packages/uni-api/src/helpers/api/index.ts @@ -206,7 +206,7 @@ export function defineTaskApi>( fn: ( args: Omit, res: { - resolve: (res?: AsyncApiRes

) => void + resolve: (res?: AsyncApiRes

| void) => void reject: (err?: string) => void } ) => ReturnType, @@ -237,7 +237,7 @@ export function defineAsyncApi>( fn: ( args: Omit, res: { - resolve: (res?: AsyncApiRes

) => void + resolve: (res: AsyncApiRes

| void) => void reject: (errMsg?: string, errRes?: any) => void } ) => void, diff --git a/packages/uni-app-plus/dist/uni-app-service.es.js b/packages/uni-app-plus/dist/uni-app-service.es.js index 42049c0ab25900a64593ec9354e55616b39734eb..898c1e2e79f129dda1457306685d7fc33aaa641b 100644 --- a/packages/uni-app-plus/dist/uni-app-service.es.js +++ b/packages/uni-app-plus/dist/uni-app-service.es.js @@ -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); } } diff --git a/packages/uni-core/src/service/plugin/appConfig.ts b/packages/uni-core/src/service/plugin/appConfig.ts index e7d24fa67d6367b934fa69c88b55584768962fee..eee01e16ae41bcb3f89064b3b7fa8041f2276c40 100644 --- a/packages/uni-core/src/service/plugin/appConfig.ts +++ b/packages/uni-core/src/service/plugin/appConfig.ts @@ -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) } } diff --git a/packages/uni-core/src/service/plugin/componentInstance.ts b/packages/uni-core/src/service/plugin/componentInstance.ts new file mode 100644 index 0000000000000000000000000000000000000000..95a88d1633eea138fdb64dcd865090fca32319ea --- /dev/null +++ b/packages/uni-core/src/service/plugin/componentInstance.ts @@ -0,0 +1,15 @@ +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 +} diff --git a/packages/uni-h5/dist/uni-h5.es.js b/packages/uni-h5/dist/uni-h5.es.js index 594fadeee2015e0ee6375047eaaa6d24651f64c2..3e47791e51e9e0486d021c66b49b4878fdca9205 100644 --- a/packages/uni-h5/dist/uni-h5.es.js +++ b/packages/uni-h5/dist/uni-h5.es.js @@ -1,5 +1,5 @@ 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]; diff --git a/packages/uni-h5/src/service/api/route/navigateTo.ts b/packages/uni-h5/src/service/api/route/navigateTo.ts index 728e7d4e456a0b5ac7b1f76c65dd3ed1a6608a06..85be987306328254308b922069bd270ab02a99c2 100644 --- a/packages/uni-h5/src/service/api/route/navigateTo.ts +++ b/packages/uni-h5/src/service/api/route/navigateTo.ts @@ -9,8 +9,10 @@ import { navigate } from './utils' export const navigateTo = defineAsyncApi( 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 ) diff --git a/packages/uni-h5/src/service/api/route/reLaunch.ts b/packages/uni-h5/src/service/api/route/reLaunch.ts index f3fd38c1c0686a7400fa7048041e75a676cda461..120f739d2611b9a0d2dc35770491792ef5de7651 100644 --- a/packages/uni-h5/src/service/api/route/reLaunch.ts +++ b/packages/uni-h5/src/service/api/route/reLaunch.ts @@ -19,7 +19,8 @@ export const reLaunch = defineAsyncApi( 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, diff --git a/packages/uni-h5/src/service/api/route/redirectTo.ts b/packages/uni-h5/src/service/api/route/redirectTo.ts index 8102f2550a5d67ea9eb1349a817877857887a7c2..3b4330806b223b5323679705d77bf4314791577b 100644 --- a/packages/uni-h5/src/service/api/route/redirectTo.ts +++ b/packages/uni-h5/src/service/api/route/redirectTo.ts @@ -24,7 +24,7 @@ export const redirectTo = defineAsyncApi( return ( // TODO exists 属性未实现 removeLastPage(), - navigate(API_REDIRECT_TO, url).then(resolve).catch(reject) + navigate({ type: API_REDIRECT_TO, url }).then(resolve).catch(reject) ) }, RedirectToProtocol, diff --git a/packages/uni-h5/src/service/api/route/switchTab.ts b/packages/uni-h5/src/service/api/route/switchTab.ts index 7ced71eccfcd613c2fd4ba94b0c1dfde77651ffd..241a4e28dc98110e61f6ccade8fadfb236c2616c 100644 --- a/packages/uni-h5/src/service/api/route/switchTab.ts +++ b/packages/uni-h5/src/service/api/route/switchTab.ts @@ -48,7 +48,7 @@ export const switchTab = defineAsyncApi( ({ url }, { resolve, reject }) => { return ( removeNonTabBarPages(), - navigate(API_SWITCH_TAB, url, getTabBarPageId(url)) + navigate({ type: API_SWITCH_TAB, url }, getTabBarPageId(url)) .then(resolve) .catch(reject) ) diff --git a/packages/uni-h5/src/service/api/route/utils.ts b/packages/uni-h5/src/service/api/route/utils.ts index 9f78268da1a26ac55c6b9e9dd60e8d4319bec4b5..0562a98d279ca07e7486c2ee3422ec22ced4259f 100644 --- a/packages/uni-h5/src/service/api/route/utils.ts +++ b/packages/uni-h5/src/service/api/route/utils.ts @@ -1,4 +1,4 @@ -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 +} export function navigate( - type: NavigateType, - url: string, + { type, url, events }: NavigateOptions, __id__?: number -): Promise { +): Promise { 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() }) }) } diff --git a/packages/uni-shared/dist/uni-shared.cjs.js b/packages/uni-shared/dist/uni-shared.cjs.js index f4b22c9e3cf23158318ae395adf1fb35ac567cf8..47a28c2871a5a4c906978ee48a0fe3fab240a69e 100644 --- a/packages/uni-shared/dist/uni-shared.cjs.js +++ b/packages/uni-shared/dist/uni-shared.cjs.js @@ -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; diff --git a/packages/uni-shared/dist/uni-shared.d.ts b/packages/uni-shared/dist/uni-shared.d.ts index 0a67c5a2e3d2e27e7969babef510a8f89a515393..3dc2530fe12e5ce0668d191482da440fb0688b02 100644 --- a/packages/uni-shared/dist/uni-shared.d.ts +++ b/packages/uni-shared/dist/uni-shared.d.ts @@ -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 void>; + export declare const NODE_TYPE_COMMENT = 8; export declare const NODE_TYPE_ELEMENT = 1; diff --git a/packages/uni-shared/dist/uni-shared.es.js b/packages/uni-shared/dist/uni-shared.es.js index f3184c5519aae6ed32df79029097d31260e9cedd..8acfabe3de46a9ff0afe740570dd5fbf5a71fe51 100644 --- a/packages/uni-shared/dist/uni-shared.es.js +++ b/packages/uni-shared/dist/uni-shared.es.js @@ -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 }; diff --git a/packages/uni-shared/src/EventChannel.ts b/packages/uni-shared/src/EventChannel.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed5f6a15ef3eb36b8099514108d26e471d73432d --- /dev/null +++ b/packages/uni-shared/src/EventChannel.ts @@ -0,0 +1,83 @@ +type NavigateToOptionEvents = Record void> + +interface EventChannelListener { + type: 'on' | 'once' + fn: (...args: any[]) => void +} + +export class EventChannel { + id: number + private listener: Record + private emitCache: Record + 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, + }) + } +} diff --git a/packages/uni-shared/src/index.ts b/packages/uni-shared/src/index.ts index 7d9713b99531902c2ddccc5891518affaf252e31..5e8ed15f23562301218d33f8154f84b780213dd7 100644 --- a/packages/uni-shared/src/index.ts +++ b/packages/uni-shared/src/index.ts @@ -8,5 +8,6 @@ export * from './utils' export * from './query' export * from './debounce' export * from './constants' +export * from './EventChannel' export * from './node/locale'