diff --git a/package.json b/package.json index 1482e3c8b40c13f04417b0d8a034b3d01e9a9834..e541fa17e7232146e2eb42ef647eacc5363ea6de 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "node": ">=10.0.0" }, "devDependencies": { - "@dcloudio/types": "^2.0.24", + "@dcloudio/types": "^2.0.25", "@microsoft/api-extractor": "^7.13.2", "@rollup/plugin-alias": "^3.1.1", "@rollup/plugin-commonjs": "^17.0.0", diff --git a/packages/global.d.ts b/packages/global.d.ts index efabe4045b762468db31b3fde8e55f061b761d39..4dcb043e2a1d41cbecee43de6c5411115bb6ce02 100644 --- a/packages/global.d.ts +++ b/packages/global.d.ts @@ -35,7 +35,7 @@ declare var __UNI_FEATURE_NAVIGATIONBAR_TRANSPARENT__: boolean declare var __uniRoutes: UniApp.UniRoutes declare var __uniConfig: UniApp.UniConfig declare var UniViewJSBridge: any -declare var UniServiceJSBridge: any +declare var UniServiceJSBridge: UniApp.UniServiceJSBridge declare const getCurrentPages: ( isAll?: boolean diff --git a/packages/shims-uni-app.d.ts b/packages/shims-uni-app.d.ts index 14e301fe6ddfdd108813760e9b1a9e2b3589840d..c42dbfd54fa11a91b0a9c32823df6cc64b772ffa 100644 --- a/packages/shims-uni-app.d.ts +++ b/packages/shims-uni-app.d.ts @@ -163,4 +163,45 @@ declare namespace UniApp { leftWindow?: PagesJsonWindowOptions rightWindow?: PagesJsonWindowOptions } + + type OnApiLike = (callback: (result: unknown) => void) => void + interface UniServiceJSBridge { + /** + * 监听 service 层的自定义事件。事件由 emit 触发,回调函数会接收所有传入事件触发函数的额外参数。 + * @param event + * @param callback + */ + on(event: string | string[], callback: Function): void + /** + * 监听 service 层的自定义事件。仅触发一次,在第一次触发之后移除监听器。 + * @param event + * @param callback + */ + once(event: string, callback: Function): void + /** + * 移除 service 层的自定义事件监听器。 + * 如果没有提供参数,则移除所有的事件监听器; + * 如果只提供了事件,则移除该事件所有的监听器; + * 如果同时提供了事件与回调,则只移除这个回调的监听器。 + * @param event + * @param callback + */ + off(event?: string | string[], callback?: Function): void + /** + * 触发 Service 层的事件。附加参数都会传给监听器回调。 + * @param event + * @param args + */ + emit(event: string, ...args: any[]): void + /** + * 触发 Service 层事件类型API(on开头)回调。 + * @param name + * @param res + */ + invokeOnCallback( + name: string, + res: Parameters[0]>[0] + ): void + publishHandler(event: string, args: unknown, pageId: number): void + } } diff --git a/packages/uni-api/src/helpers/api/callback.ts b/packages/uni-api/src/helpers/api/callback.ts index 7923b1e70631c72e9398315e3e6ea304f7c67c0d..144eb6fe75f8697915804c2963315b296eabf25d 100644 --- a/packages/uni-api/src/helpers/api/callback.ts +++ b/packages/uni-api/src/helpers/api/callback.ts @@ -13,10 +13,6 @@ const invokeCallbacks: { } } = {} -function createInvokeCallbackName(name: string, callbackId: number) { - return 'api.' + name + '.' + callbackId -} - function addInvokeCallback( id: number, name: string, @@ -45,29 +41,41 @@ export function invokeCallback(id: number, res: unknown, extras?: unknown) { return res } -function getKeepAliveApiCallback(name: string, callback: Function) { - const onName = 'api.' + name.replace('off', 'on') +export function findInvokeCallbackByName(name: string) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true + } + } + return false +} + +export function removeKeepAliveApiCallback(name: string, callback: Function) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key] - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key] - return Number(key) } } - return -1 +} + +export function offKeepAliveApiCallback(name: string) { + UniServiceJSBridge.off('api.' + name) +} + +export function onKeepAliveApiCallback(name: string) { + UniServiceJSBridge.on('api.' + name, (res: unknown) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key] + if (opts.name === name) { + opts.callback(res) + } + } + }) } export function createKeepAliveApiCallback(name: string, callback: Function) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback) - } - const id = invokeCallbackId++ - return addInvokeCallback( - id, - createInvokeCallbackName(name, id), - callback, - true - ) + return addInvokeCallback(invokeCallbackId++, name, callback, true) } export const API_SUCCESS = 'success' @@ -117,21 +125,17 @@ export function createAsyncApiCallback( const hasFail = isFunction(fail) const hasComplete = isFunction(complete) const callbackId = invokeCallbackId++ - addInvokeCallback( - callbackId, - createInvokeCallbackName(name, callbackId), - (res: ApiRes) => { - res = res || {} - res.errMsg = normalizeErrMsg(res.errMsg, name) - isFunction(beforeAll) && beforeAll(res) - if (res.errMsg === name + ':ok') { - isFunction(beforeSuccess) && beforeSuccess(res) - hasSuccess && success!(res) - } else { - hasFail && fail!(res) - } - hasComplete && complete!(res) + addInvokeCallback(callbackId, name, (res: ApiRes) => { + res = res || {} + res.errMsg = normalizeErrMsg(res.errMsg, name) + isFunction(beforeAll) && beforeAll(res) + if (res.errMsg === name + ':ok') { + isFunction(beforeSuccess) && beforeSuccess(res) + hasSuccess && success!(res) + } else { + hasFail && fail!(res) } - ) + hasComplete && complete!(res) + }) return callbackId } diff --git a/packages/uni-api/src/helpers/api/index.ts b/packages/uni-api/src/helpers/api/index.ts index 1082812425b333755b46d085810d562f32aee48d..208551c7a8d573bafa6d8a6df7e624204f8eb4dd 100644 --- a/packages/uni-api/src/helpers/api/index.ts +++ b/packages/uni-api/src/helpers/api/index.ts @@ -4,17 +4,23 @@ import { API_TYPE_ON_PROTOCOLS, validateProtocols } from '../protocol' import { invokeCallback, createAsyncApiCallback, + onKeepAliveApiCallback, + offKeepAliveApiCallback, + findInvokeCallbackByName, createKeepAliveApiCallback, + removeKeepAliveApiCallback, } from './callback' import { promisify } from './promise' export const API_TYPE_ON = 0 -export const API_TYPE_TASK = 1 -export const API_TYPE_SYNC = 2 -export const API_TYPE_ASYNC = 3 +export const API_TYPE_OFF = 1 +export const API_TYPE_TASK = 2 +export const API_TYPE_SYNC = 3 +export const API_TYPE_ASYNC = 4 type API_TYPES = | typeof API_TYPE_ON + | typeof API_TYPE_OFF | typeof API_TYPE_TASK | typeof API_TYPE_SYNC | typeof API_TYPE_ASYNC @@ -35,8 +41,28 @@ function formatApiArgs(args: any[], options?: ApiOptions) { } function wrapperOnApi(name: string, fn: Function) { - return (callback: Function) => - fn.apply(null, createKeepAliveApiCallback(name, callback)) + return (callback: Function) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name) + createKeepAliveApiCallback(name, callback) + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name) + fn() + } + } +} + +function wrapperOffApi(name: string, fn: Function) { + return (callback: Function) => { + name = name.replace('off', 'on') + removeKeepAliveApiCallback(name, callback) + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name) + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name) + fn() + } + } } function wrapperTaskApi(name: string, fn: Function, options?: ApiOptions) { @@ -96,6 +122,20 @@ export function defineOnApi( ) as T } +export function defineOffApi( + name: string, + fn: T, + options?: ApiOptions +) { + return defineApi( + API_TYPE_OFF, + name, + fn, + __DEV__ ? API_TYPE_ON_PROTOCOLS : undefined, + options + ) as T +} + export function defineTaskApi( name: string, fn: T, @@ -190,6 +230,8 @@ function defineApi( switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options) + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options) case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options) case API_TYPE_SYNC: diff --git a/packages/uni-api/src/index.ts b/packages/uni-api/src/index.ts index 9a4a23db652485c9c95b326b353c4da0cfe83b63..8ec6ee834d1473657ddbdb25968293b54fc577b3 100644 --- a/packages/uni-api/src/index.ts +++ b/packages/uni-api/src/index.ts @@ -25,6 +25,7 @@ export * from './protocols/route/route' // helpers export { defineOnApi, + defineOffApi, defineTaskApi, defineSyncApi, defineAsyncApi, diff --git a/packages/uni-core/src/helpers/TinyEmitter.js b/packages/uni-core/src/helpers/TinyEmitter.js new file mode 100644 index 0000000000000000000000000000000000000000..c59aa445710e17e89499b5822f5d74c9c7af1b6f --- /dev/null +++ b/packages/uni-core/src/helpers/TinyEmitter.js @@ -0,0 +1,64 @@ +function E() { + // Keep this empty so it's easier to inherit from + // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3) +} + +E.prototype = { + on: function (name, callback, ctx) { + var e = this.e || (this.e = {}) + + ;(e[name] || (e[name] = [])).push({ + fn: callback, + ctx: ctx, + }) + + return this + }, + + once: function (name, callback, ctx) { + var self = this + function listener() { + self.off(name, listener) + callback.apply(ctx, arguments) + } + + listener._ = callback + return this.on(name, listener, ctx) + }, + + emit: function (name) { + var data = [].slice.call(arguments, 1) + var evtArr = ((this.e || (this.e = {}))[name] || []).slice() + var i = 0 + var len = evtArr.length + + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data) + } + + return this + }, + + off: function (name, callback) { + var e = this.e || (this.e = {}) + var evts = e[name] + var liveEvents = [] + + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]) + } + } + + // Remove event from queue to prevent memory leak + // Suggested by https://github.com/lazd + // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910 + + liveEvents.length ? (e[name] = liveEvents) : delete e[name] + + return this + }, +} + +export default E diff --git a/packages/uni-core/src/helpers/bridge.ts b/packages/uni-core/src/helpers/bridge.ts index 692831996c0bb4bf8ea4107713f63a4cf0f427c0..a29df6151a730c3570b95c1c9d837e4dfcc43d86 100644 --- a/packages/uni-core/src/helpers/bridge.ts +++ b/packages/uni-core/src/helpers/bridge.ts @@ -1,29 +1,20 @@ -type Handler = (event?: T) => void +import { extend } from '@vue/shared' +// @ts-ignore TODO 等待 vue3 的兼容模式自带emitter +import E from './TinyEmitter' -export function initBridge(namespace: 'service' | 'view') { +export function initBridge( + namespace: 'service' | 'view' +): UniApp.UniServiceJSBridge { // TODO vue3 compatibility builds - const { on, off, emit } = { - on(event: string, callback: Handler) { - console.log(event, callback) + const emitter = new E() + return extend(emitter, { + subscribe(event: string, callback: Function): void { + return emitter.on(`${namespace}.${event}`, callback) }, - off(event: string, callback: Handler) { - console.log(event, callback) + unsubscribe(event: string, callback: Function): void { + return emitter.off(`${namespace}.${event}`, callback) }, - emit(event: string, ...args: any[]) { - console.log(event, args) - }, - } - return { - on, - off, - emit, - subscribe(event: string, callback: Handler) { - return on(`${namespace}.${event}`, callback) - }, - unsubscribe(event: string, callback: Handler) { - return off(`${namespace}.${event}`, callback) - }, - subscribeHandler(event: string, args: any, pageId: number) { + subscribeHandler(event: string, args: unknown, pageId: number): void { if (__DEV__) { console.log( `[${namespace}][subscribeHandler][${Date.now()}]:${event}, ${JSON.stringify( @@ -31,7 +22,7 @@ export function initBridge(namespace: 'service' | 'view') { )}, ${pageId}` ) } - return emit(`${namespace}.${event}`, args, pageId) + return emitter.emit(`${namespace}.${event}`, args, pageId) }, - } + }) } diff --git a/packages/uni-core/src/service/bridge/index.ts b/packages/uni-core/src/service/bridge/index.ts index e1f0cf4b5a8bdbe7a6bf71f368939993295a4f70..82e9b17e11d77d1a292c7fe9605dac5197acad9d 100644 --- a/packages/uni-core/src/service/bridge/index.ts +++ b/packages/uni-core/src/service/bridge/index.ts @@ -1,3 +1,9 @@ +import { extend } from '@vue/shared' + import { initBridge } from '../../helpers/bridge' -export const ServiceJSBridge = initBridge('service') +export const ServiceJSBridge = extend(initBridge('service'), { + invokeOnCallback(name: string, res: unknown) { + return UniServiceJSBridge.emit('api.' + name, res) + }, +}) diff --git a/packages/uni-h5/dist/uni-h5.esm.js b/packages/uni-h5/dist/uni-h5.esm.js index cde6f4c8bcb6db4a39a7e98c25180254af9609c5..13816b1ece25d16f0acf2cff011e3c0701626543 100644 --- a/packages/uni-h5/dist/uni-h5.esm.js +++ b/packages/uni-h5/dist/uni-h5.esm.js @@ -55,35 +55,66 @@ function initApp$1(app) { globalProperties.$applyOptions = applyOptions; } } -function initBridge(namespace) { - const {on, off, emit} = { - on(event2, callback) { - console.log(event2, callback); - }, - off(event2, callback) { - console.log(event2, callback); - }, - emit(event2, ...args) { - console.log(event2, args); +function E() { +} +E.prototype = { + on: function(name, callback, ctx) { + var e2 = this.e || (this.e = {}); + (e2[name] || (e2[name] = [])).push({ + fn: callback, + ctx + }); + return this; + }, + once: function(name, callback, ctx) { + var self = this; + function listener() { + self.off(name, listener); + callback.apply(ctx, arguments); } - }; - return { - on, - off, - emit, + listener._ = callback; + return this.on(name, listener, ctx); + }, + emit: function(name) { + var data = [].slice.call(arguments, 1); + var evtArr = ((this.e || (this.e = {}))[name] || []).slice(); + var i = 0; + var len = evtArr.length; + for (i; i < len; i++) { + evtArr[i].fn.apply(evtArr[i].ctx, data); + } + return this; + }, + off: function(name, callback) { + var e2 = this.e || (this.e = {}); + var evts = e2[name]; + var liveEvents = []; + if (evts && callback) { + for (var i = 0, len = evts.length; i < len; i++) { + if (evts[i].fn !== callback && evts[i].fn._ !== callback) + liveEvents.push(evts[i]); + } + } + liveEvents.length ? e2[name] = liveEvents : delete e2[name]; + return this; + } +}; +function initBridge(namespace) { + const emitter2 = new E(); + return extend(emitter2, { subscribe(event2, callback) { - return on(`${namespace}.${event2}`, callback); + return emitter2.on(`${namespace}.${event2}`, callback); }, unsubscribe(event2, callback) { - return off(`${namespace}.${event2}`, callback); + return emitter2.off(`${namespace}.${event2}`, callback); }, subscribeHandler(event2, args, pageId) { if (process.env.NODE_ENV !== "production") { console.log(`[${namespace}][subscribeHandler][${Date.now()}]:${event2}, ${JSON.stringify(args)}, ${pageId}`); } - return emit(`${namespace}.${event2}`, args, pageId); + return emitter2.emit(`${namespace}.${event2}`, args, pageId); } - }; + }); } const ViewJSBridge = initBridge("view"); const LONGPRESS_TIMEOUT = 350; @@ -639,7 +670,11 @@ function initView(app) { } initAppConfig$1(app._context.config); } -const ServiceJSBridge = initBridge("service"); +const ServiceJSBridge = extend(initBridge("service"), { + invokeOnCallback(name, res) { + return UniServiceJSBridge.emit("api." + name, res); + } +}); function querySelector(vm, selector) { const el = vm.$el.querySelector(selector); return el && el.__vue__; @@ -7483,6 +7518,13 @@ function decode(base64) { } return arraybuffer; } +const API_TYPE_ON_PROTOCOLS = [ + { + name: "callback", + type: Function, + required: true + } +]; function validateProtocolFail(name, msg) { const errMsg = `${name}:fail ${msg}`; { @@ -7617,9 +7659,6 @@ function tryCatch(fn) { } let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return "api." + name + "." + callbackId; -} function addInvokeCallback(id2, name, callback, keepAlive = false) { invokeCallbacks[id2] = { name, @@ -7640,23 +7679,37 @@ function invokeCallback(id2, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = "api." + name.replace("off", "on"); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off("api." + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on("api." + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf("off") === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id2 = invokeCallbackId++; - return addInvokeCallback(id2, createInvokeCallbackName(name, id2), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } const API_SUCCESS = "success"; const API_FAIL = "fail"; @@ -7687,7 +7740,7 @@ function createAsyncApiCallback(name, args = {}, {beforeAll, beforeSuccess} = {} const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -7727,9 +7780,10 @@ function promisify(fn) { }; } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || !isPlainObject(options.formatArgs) && isPlainObject(params)) { @@ -7742,7 +7796,25 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace("off", "on"); + removeKeepAliveApiCallback(name, callback); + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -7771,6 +7843,12 @@ function wrapperApi(fn, name, protocol, options) { return fn.apply(null, formatApiArgs(args, options)); }; } +function defineOnApi(name, fn, options) { + return defineApi(API_TYPE_ON, name, fn, process.env.NODE_ENV !== "production" ? API_TYPE_ON_PROTOCOLS : void 0, options); +} +function defineOffApi(name, fn, options) { + return defineApi(API_TYPE_OFF, name, fn, process.env.NODE_ENV !== "production" ? API_TYPE_ON_PROTOCOLS : void 0, options); +} function defineSyncApi(name, fn, protocol, options) { return defineApi(API_TYPE_SYNC, name, fn, process.env.NODE_ENV !== "production" ? protocol : void 0, options); } @@ -7781,6 +7859,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: @@ -8332,6 +8412,51 @@ const getSystemInfoSync = defineSyncApi("getSystemInfoSync", () => { const getSystemInfo = defineAsyncApi("getSystemInfo", () => { return Promise.resolve(getSystemInfoSync()); }); +const API_ON_NETWORK_STATUS_CHANGE = "onNetworkStatusChange"; +function networkListener() { + getNetworkType().then(({networkType}) => { + UniServiceJSBridge.invokeOnCallback(API_ON_NETWORK_STATUS_CHANGE, { + isConnected: networkType !== "none", + networkType + }); + }); +} +function getConnection() { + return navigator.connection || navigator.webkitConnection || navigator.mozConnection; +} +const onNetworkStatusChange = defineOnApi(API_ON_NETWORK_STATUS_CHANGE, () => { + const connection = getConnection(); + if (connection) { + connection.addEventListener("change", networkListener); + } else { + window.addEventListener("offline", networkListener); + window.addEventListener("online", networkListener); + } +}); +const offNetworkStatusChange = defineOffApi("offNetworkStatusChange", () => { + const connection = getConnection(); + if (connection) { + connection.removeEventListener("change", networkListener); + } else { + window.removeEventListener("offline", networkListener); + window.removeEventListener("online", networkListener); + } +}); +const getNetworkType = defineAsyncApi("getNetworkType", () => { + const connection = getConnection(); + let networkType = "unknown"; + if (connection) { + networkType = connection.type; + if (networkType === "cellular" && connection.effectiveType) { + networkType = connection.effectiveType.replace("slow-", ""); + } else if (!["none", "wifi"].includes(networkType)) { + networkType = "unknown"; + } + } else if (navigator.onLine === false) { + networkType = "none"; + } + return Promise.resolve({networkType}); +}); const openDocument = defineAsyncApi(API_OPEN_DOCUMENT, ({filePath}) => { window.open(filePath); return Promise.resolve(); @@ -8401,6 +8526,9 @@ var api = /* @__PURE__ */ Object.freeze({ makePhoneCall, getSystemInfo, getSystemInfoSync, + onNetworkStatusChange, + offNetworkStatusChange, + getNetworkType, openDocument, getImageInfo, navigateBack, @@ -9516,4 +9644,4 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { ]); } _sfc_main.render = _sfc_render; -export {_sfc_main$1 as AsyncErrorComponent, _sfc_main as AsyncLoadingComponent, _sfc_main$o as Audio, _sfc_main$n as Canvas, _sfc_main$m as Checkbox, _sfc_main$l as CheckboxGroup, _sfc_main$k as Editor, _sfc_main$j as Form, index$2 as Icon, _sfc_main$h as Image, _sfc_main$g as Input, _sfc_main$f as Label, _sfc_main$e as MovableView, _sfc_main$d as Navigator, index as PageComponent, _sfc_main$c as Progress, _sfc_main$b as Radio, _sfc_main$a as RadioGroup, _sfc_main$i as ResizeSensor, _sfc_main$9 as RichText, _sfc_main$8 as ScrollView, _sfc_main$7 as Slider, _sfc_main$6 as SwiperItem, _sfc_main$5 as Switch, index$1 as Text, _sfc_main$4 as Textarea, UniServiceJSBridge$1 as UniServiceJSBridge, UniViewJSBridge$1 as UniViewJSBridge, _sfc_main$3 as View, addInterceptor, arrayBufferToBase64, base64ToArrayBuffer, canIUse, createIntersectionObserver, createSelectorQuery, getApp$1 as getApp, getCurrentPages$1 as getCurrentPages, getImageInfo, getSystemInfo, getSystemInfoSync, makePhoneCall, navigateBack, navigateTo, openDocument, index$3 as plugin, promiseInterceptor, reLaunch, redirectTo, removeInterceptor, switchTab, uni$1 as uni, upx2px}; +export {_sfc_main$1 as AsyncErrorComponent, _sfc_main as AsyncLoadingComponent, _sfc_main$o as Audio, _sfc_main$n as Canvas, _sfc_main$m as Checkbox, _sfc_main$l as CheckboxGroup, _sfc_main$k as Editor, _sfc_main$j as Form, index$2 as Icon, _sfc_main$h as Image, _sfc_main$g as Input, _sfc_main$f as Label, _sfc_main$e as MovableView, _sfc_main$d as Navigator, index as PageComponent, _sfc_main$c as Progress, _sfc_main$b as Radio, _sfc_main$a as RadioGroup, _sfc_main$i as ResizeSensor, _sfc_main$9 as RichText, _sfc_main$8 as ScrollView, _sfc_main$7 as Slider, _sfc_main$6 as SwiperItem, _sfc_main$5 as Switch, index$1 as Text, _sfc_main$4 as Textarea, UniServiceJSBridge$1 as UniServiceJSBridge, UniViewJSBridge$1 as UniViewJSBridge, _sfc_main$3 as View, addInterceptor, arrayBufferToBase64, base64ToArrayBuffer, canIUse, createIntersectionObserver, createSelectorQuery, getApp$1 as getApp, getCurrentPages$1 as getCurrentPages, getImageInfo, getNetworkType, getSystemInfo, getSystemInfoSync, makePhoneCall, navigateBack, navigateTo, offNetworkStatusChange, onNetworkStatusChange, openDocument, index$3 as plugin, promiseInterceptor, reLaunch, redirectTo, removeInterceptor, switchTab, uni$1 as uni, upx2px}; diff --git a/packages/uni-h5/src/service/api/device/network.ts b/packages/uni-h5/src/service/api/device/network.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c96fd7449a48a81e1580722948af629588ddc0c --- /dev/null +++ b/packages/uni-h5/src/service/api/device/network.ts @@ -0,0 +1,69 @@ +import { defineOnApi, defineOffApi, defineAsyncApi } from '@dcloudio/uni-api' + +type OnNetworkStatusChange = typeof uni.onNetworkStatusChange + +const API_ON_NETWORK_STATUS_CHANGE = 'onNetworkStatusChange' + +function networkListener() { + getNetworkType().then(({ networkType }) => { + UniServiceJSBridge.invokeOnCallback( + API_ON_NETWORK_STATUS_CHANGE, + { + isConnected: networkType !== 'none', + networkType, + } + ) + }) +} + +function getConnection() { + return ( + (navigator as any).connection || + (navigator as any).webkitConnection || + (navigator as any).mozConnection + ) +} + +export const onNetworkStatusChange = defineOnApi( + API_ON_NETWORK_STATUS_CHANGE, + () => { + const connection = getConnection() + if (connection) { + connection.addEventListener('change', networkListener) + } else { + window.addEventListener('offline', networkListener) + window.addEventListener('online', networkListener) + } + } +) + +export const offNetworkStatusChange = defineOffApi< + typeof uni.offNetworkStatusChange +>('offNetworkStatusChange', () => { + const connection = getConnection() + if (connection) { + connection.removeEventListener('change', networkListener) + } else { + window.removeEventListener('offline', networkListener) + window.removeEventListener('online', networkListener) + } +}) + +export const getNetworkType = defineAsyncApi( + 'getNetworkType', + () => { + const connection = getConnection() + let networkType = 'unknown' + if (connection) { + networkType = connection.type + if (networkType === 'cellular' && connection.effectiveType) { + networkType = connection.effectiveType.replace('slow-', '') + } else if (!['none', 'wifi'].includes(networkType)) { + networkType = 'unknown' + } + } else if (navigator.onLine === false) { + networkType = 'none' + } + return Promise.resolve({ networkType }) + } +) diff --git a/packages/uni-h5/src/service/api/index.ts b/packages/uni-h5/src/service/api/index.ts index ecd4e619f9a0e589f5af5e50e78038f3b1263583..8adba93890c4019f36d5b81741cb1e1458ce711d 100644 --- a/packages/uni-h5/src/service/api/index.ts +++ b/packages/uni-h5/src/service/api/index.ts @@ -3,6 +3,7 @@ export * from './base/canIUse' export * from './device/makePhoneCall' export * from './device/getSystemInfo' export * from './device/getSystemInfoSync' +export * from './device/network' export * from './file/openDocument' diff --git a/packages/uni-mp-alipay/dist/uni.api.esm.js b/packages/uni-mp-alipay/dist/uni.api.esm.js index c304b2def8e687c6647cadc77073c3f9e6fcbeff..00f37339f95548d1ff33e47a3e701ab7d5fdf1cd 100644 --- a/packages/uni-mp-alipay/dist/uni.api.esm.js +++ b/packages/uni-mp-alipay/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/packages/uni-mp-baidu/dist/uni.api.esm.js b/packages/uni-mp-baidu/dist/uni.api.esm.js index b272b0b85f82ef5113771222bad51e8c0568d5de..7c602bd9042c67860a23feed5546782bf8202ef3 100644 --- a/packages/uni-mp-baidu/dist/uni.api.esm.js +++ b/packages/uni-mp-baidu/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/packages/uni-mp-qq/dist/uni.api.esm.js b/packages/uni-mp-qq/dist/uni.api.esm.js index 43cdfef41c213168e6681384ecce6e39c5583cfc..f9c1d30283b1da1a9e7d4a3e0cdeaf44b1da5e9e 100644 --- a/packages/uni-mp-qq/dist/uni.api.esm.js +++ b/packages/uni-mp-qq/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/packages/uni-mp-toutiao/dist/uni.api.esm.js b/packages/uni-mp-toutiao/dist/uni.api.esm.js index fa56a88dda035ce558588379e9817bb50fc192af..e7d4a1e551bf9e80a3faba5bc41c448371a68333 100644 --- a/packages/uni-mp-toutiao/dist/uni.api.esm.js +++ b/packages/uni-mp-toutiao/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/packages/uni-mp-weixin/dist/uni.api.esm.js b/packages/uni-mp-weixin/dist/uni.api.esm.js index 19b03b6859a29892cd883fc5cf2c9cce0c953bb3..712f9d66ba0ce6fdbf1ecec7adc04e1b554e6582 100644 --- a/packages/uni-mp-weixin/dist/uni.api.esm.js +++ b/packages/uni-mp-weixin/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/packages/uni-quickapp-webview/dist/uni.api.esm.js b/packages/uni-quickapp-webview/dist/uni.api.esm.js index 4aeffca06a2ee44e2ce92dc2d3b29487f6fba7da..b85cc0f48af03b4f00e83dbdaeaeb072e98a78f4 100644 --- a/packages/uni-quickapp-webview/dist/uni.api.esm.js +++ b/packages/uni-quickapp-webview/dist/uni.api.esm.js @@ -154,9 +154,6 @@ function tryCatch(fn) { let invokeCallbackId = 1; const invokeCallbacks = {}; -function createInvokeCallbackName(name, callbackId) { - return 'api.' + name + '.' + callbackId; -} function addInvokeCallback(id, name, callback, keepAlive = false) { invokeCallbacks[id] = { name, @@ -178,23 +175,37 @@ function invokeCallback(id, res, extras) { } return res; } -function getKeepAliveApiCallback(name, callback) { - const onName = 'api.' + name.replace('off', 'on'); +function findInvokeCallbackByName(name) { + for (const key in invokeCallbacks) { + if (invokeCallbacks[key].name === name) { + return true; + } + } + return false; +} +function removeKeepAliveApiCallback(name, callback) { for (const key in invokeCallbacks) { const item = invokeCallbacks[key]; - if (item.callback === callback && item.name.indexOf(onName) === 0) { + if (item.callback === callback && item.name === name) { delete invokeCallbacks[key]; - return Number(key); } } - return -1; +} +function offKeepAliveApiCallback(name) { + UniServiceJSBridge.off('api.' + name); +} +function onKeepAliveApiCallback(name) { + UniServiceJSBridge.on('api.' + name, (res) => { + for (const key in invokeCallbacks) { + const opts = invokeCallbacks[key]; + if (opts.name === name) { + opts.callback(res); + } + } + }); } function createKeepAliveApiCallback(name, callback) { - if (name.indexOf('off') === 0) { - return getKeepAliveApiCallback(name, callback); - } - const id = invokeCallbackId++; - return addInvokeCallback(id, createInvokeCallbackName(name, id), callback, true); + return addInvokeCallback(invokeCallbackId++, name, callback, true); } function getApiCallbacks(args) { const apiCallbacks = {}; @@ -222,7 +233,7 @@ function createAsyncApiCallback(name, args = {}, { beforeAll, beforeSuccess } = const hasFail = isFunction(fail); const hasComplete = isFunction(complete); const callbackId = invokeCallbackId++; - addInvokeCallback(callbackId, createInvokeCallbackName(name, callbackId), (res) => { + addInvokeCallback(callbackId, name, (res) => { res = res || {}; res.errMsg = normalizeErrMsg(res.errMsg, name); isFunction(beforeAll) && beforeAll(res); @@ -250,9 +261,10 @@ function handlePromise(promise) { } const API_TYPE_ON = 0; -const API_TYPE_TASK = 1; -const API_TYPE_SYNC = 2; -const API_TYPE_ASYNC = 3; +const API_TYPE_OFF = 1; +const API_TYPE_TASK = 2; +const API_TYPE_SYNC = 3; +const API_TYPE_ASYNC = 4; function formatApiArgs(args, options) { const params = args[0]; if (!options || @@ -266,7 +278,27 @@ function formatApiArgs(args, options) { return args; } function wrapperOnApi(name, fn) { - return (callback) => fn.apply(null, createKeepAliveApiCallback(name, callback)); + return (callback) => { + // 是否是首次调用on,如果是首次,需要初始化onMethod监听 + const isFirstInvokeOnApi = !findInvokeCallbackByName(name); + createKeepAliveApiCallback(name, callback); + if (isFirstInvokeOnApi) { + onKeepAliveApiCallback(name); + fn(); + } + }; +} +function wrapperOffApi(name, fn) { + return (callback) => { + name = name.replace('off', 'on'); + removeKeepAliveApiCallback(name, callback); + // 是否还存在监听,若已不存在,则移除onMethod监听 + const hasInvokeOnApi = findInvokeCallbackByName(name); + if (!hasInvokeOnApi) { + offKeepAliveApiCallback(name); + fn(); + } + }; } function wrapperTaskApi(name, fn, options) { return (args) => fn.apply(null, [args, createAsyncApiCallback(name, args, options)]); @@ -304,6 +336,8 @@ function defineApi(type, name, fn, protocol, options) { switch (type) { case API_TYPE_ON: return wrapperApi(wrapperOnApi(name, fn), name, protocol, options); + case API_TYPE_OFF: + return wrapperApi(wrapperOffApi(name, fn), name, protocol, options); case API_TYPE_TASK: return wrapperApi(wrapperTaskApi(name, fn), name, protocol, options); case API_TYPE_SYNC: diff --git a/yarn.lock b/yarn.lock index 490854dac86fe43d9793cda519bd8a33b350b0a4..494e7efc1810421971b3b2aeddc648952cc12b8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -337,10 +337,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@dcloudio/types@^2.0.24": - version "2.0.24" - resolved "https://registry.yarnpkg.com/@dcloudio/types/-/types-2.0.24.tgz#ed7ba22dd7604e67a7ccd3949f95055f042983e1" - integrity sha512-R5bmKdsHv3cewuJs9eQL2nwdNzDMWic3DyJ75Atno2oti9MEk1DZqcuxu1T2VyjQnEOn85ACy/FXGsLGhInStA== +"@dcloudio/types@^2.0.25": + version "2.0.25" + resolved "https://registry.yarnpkg.com/@dcloudio/types/-/types-2.0.25.tgz#c8e3de361b722e2bd7e40e477c0b9626e3777b31" + integrity sha512-r+AlgylZVlX2OLepxuyKzB4mRtnfdcIrnzHWbJOgZ57WDpJEcrXpP1mO0JhRGz3ewehru7P6sAcbKqRYcTkeXw== "@eslint/eslintrc@^0.4.0": version "0.4.0"