UNPKG

@hyext/utils

Version:

A tools lib for huya miniapp business development

409 lines (333 loc) 12.1 kB
# `@hyext/utils` > 小程序业务开发工具库 ## Installation ```shell $ npm i @hyext/utils -S ``` ## Usage ```js import { memory, createLogger, createSDKPolyfill } from '@hyext/utils' ``` 下述的每个函数都可以参照上方import导入。 ## Summary - [createLogger](#createlogger) - 创建一个logger对象 - [createSDKPolyfill](#createsdkpolyfill) - 基于小程序SDK二次封装的SDK对象 - [createPromisfyFnWithCatch](#createpromisfyfnwithcatch) - Catch Promisy Func - [once](#once) - once - [throttle](#throttle) - 防抖 - [createPolling](#createpolling) - 轮询 - [memory](#memory) - 记忆函数 - [promiseTimeout](#promisetimeout) - 超时promise - [promiseRetry](#promiseretry) - 重试promise - isUndef (v: any): boolean - 判断值是否为undefined或null - isDef (v: any): boolean - 判断值不是undefined且不是null - isTrue (v: any): boolean - v === true - isFalse (v: any): boolean - v === false - isObject (v: any): boolean - 判断值是否是一个对象 - isFunction (v: any): boolean - 判断值是否是一个函数 - isArray (v: any): boolean - 判断值是否是一个数组 - isPlainObject(v: any): boolean - 判断值是否是一个pure object - isRegExp (v: any): boolean - 判断值是否是一个RegExp - capitalize(v: string): string - 转首字母大写 - camelize(v: string): string - 转驼峰 - hyphenate(v: string): string - 转连字:xxA -> xx-a - toNumber (val: string): number | string - 尝试将val转成number,失败则返回原字符串 - toString (val: any): string - 将任意值转成一个字符串 - second2Minute (seconds: number): string - 秒转成mm:ss - delay (duration: number): Promise<void> - 延迟 - getTime(date:Date): string - date实例转成hh:mm:ss - patchNumber(v: number): string - 数字补0 - second2Minute(seconds: number): string - 秒转·mm:ss· - limit(v: string|number): string - 收缩范围,1 - +00 -> 1 - 99+ - isWebViewEnv() - 是否在webview环境。 - scalePxOnWebView(px: number, base: number = 750) - 在 webview 环境中使用 scalePx 接口。 ## 模块类 ### createLogger - createLogger(options: CreateLoggerOptions) - 创建一个logger对象 - logger.log(msg: string, data: any) - 默认输出绿色字体的log - logger.info(msg: string, data: any) - 默认输出蓝色字体的log - logger.warn(msg: string, data: any) - 默认输出黄色字体的log - logger.error(msg: string, data: any) - 默认输出红色字体的log #### 入参解析 ```js type CreateLoggerOptions = { onBefore?: (logInfo: LogInfo) => void // log之前触发,传入一个LogInfo onAfter?: (logInfo: LogInfo) => void // log之后触发,传入一个LogInfo prefix?: (() => string) | string // 打印日志前缀 logColor?: LogColor // 每个接口的字体颜色配置 enableLog?: boolean | ((logInfo: LogInfo) => boolean) // 是否打印原生log,传入一个LogInfo } type LogInfo = { prefix: string // CreateLoggerOptions.prefix中获取 msg: string // logger接口传入的msg data: any // logger接口传入的data desc: string // desc = prefix + msg } type LogColor = { info?: string // 颜色,可以是颜色英文或hex: 例如:red, #cccccc warn?: string error?: string log?: string } ``` #### Demo ```js // create const logger = createLogger({ onAfter(logInfo) { global.hyext.logger.log(logInfo.desc + ' ' + JSON.stringify(logInfo.data)) }, prefix () { return `miniappName * date:${Date.now()} uid: ${uid} sessionId:${sessionId} -` }, enableLog(logInfo) { // 例如我们在APP只通过onAfter钩子打印sdk的log, // 禁止打印原生log,提高性能 if (platform !== 'web') return false return true } }) // call logger.log('oh my god~') ``` ### createSDKPolyfill - createSDKPolyfill(options: SDKPolyfillOptions) - 基于小程序SDK二次封装的SDK对象,补丁了错误的catch,可控制其接口的call,resolve, reject #### 参数解析 ```js type SDKPolyfillOptions = { paths: string[] // 接口访问路径队列,例如: hyExt.advance.sendWup -> paths => ['advance.sendWup'] SDK: SDKModel // 传入global.hyExt onError?: (errMsg: string, apiName: string, path: string) => void // SDK调用失败时 触发 onCall?: (calledArgs: Array<any>, apiName: string, path: string) => void // SDK调用时 触发 onSuccess?: (res: any, apiName: string, path: string) => void // SDK调用成功时 触发 } type SDKModel = { [key: string]: any } ``` ### Demo ```js // 与logger配合使用的例子,甩锅神器 const ployfillSDK = createSDKPolyfill({ SDK: global.hyExt, paths: [ 'advance.sendWup' ], onCall(callArgs, apiName, path) { logger.log(`SDK.${path}开始调用`, callArgs) }, onError(errMsg, apiName, path) { logger.log(`SDK.${path}调用失败`, errMsg) }, onSuccess(res, apiName, path) { logger.log(`SDK.${path}调用成功`, res) }, onPolyfill(polyfillSDK, apiName, apiFN) { // 补丁前触发, 可以拦截sdk进行二次处理。 polyfillSDK[apiName] = jest.fn(apiFN) } }) // ployfillSDK的每个接口只会resolve,因为内部已被catch错误,发生错误res是false ployfillSDK.sendWup(options).then((res) => { if (!res) return; // 发生错误就跳过了 // do something }) ``` ## 高阶函数类 ### getSetModeWithQueueFn 背景: 小程序SDK原来的setMode方法, resolve后小程序马上会被显示. 但是在新版APP上, setMode resolve 并不表示马上会显示, 而是会在恰当的时候通过 hyExt.popup.onNoticeShow 通知小程序. 这个变更会给部分小程序带来影响, 比如你要10秒后关闭浮窗, 并不能在调用setMode之后开始倒计时, 而是在 onNoticeShow 回调中开始倒计时. 本函数封装了SDK中的以下方法: - setMode - onNoticeShow/offNoticeShow - onNoticeHide/offNoticeHide getSetModeWithQueueFn 每次调用都会取消监听 noticeShow/noticeHide 事件, 再重新进行监听. 调用后返回 setModeWithQueue 函数 setModeWithQueue 函数是对setMode的封装, 用于支持 notice 排队. 对比setMode, 添加两个额外的参数: - onShow: 如果当前mode是NOTICE, 那么当收到终端的展示事件时, 会调用这个函数. 如果当前mode不是NOTICE, 则在 setMode resolve 后调用这个函数 - onNoticeHide: 当notice超时被终端隐藏, 或者调用了setMode 切换到非NOTICE模式时 , 会调用这个函数. setModeWithQueue 兼容性: - 可以兼容老版本APP, 在老版本APP上和原始的 setMode 方式行为一致 - 可以兼容不同的mode, 'NOTICE'/'NORMAL'/'RIGHT_BOTTOM_BTN' #### Demo ```ts const setModeWithQueue = getSetModeWithQueueFn(msg => console.log(msg)) const App = () => { // 收到推送后才显示notice const dataPush = useMainSelector(s => s.dataPush) const [didShow, setShow] = useState(false) useEffect(() => { setModeWithQueue({ mode: dataPush ? 'NOTICE' : 'NORMAL' }, onShow: () => {console.log('show')}) }, [dataPush]) if (!dataPush) return null return ( <View> <Text>我迟早会显示</Text> </View> ) } ``` #### 参数 ```ts export function getSetModeWithQueueFn(logger: (msg: string) => void): SetModeWithQueueFn; type SetModeWithQueueFn = (params: { mode: string, onShow?: Callback, onNoticeHide?: Callback, showTime?: number, // 期望展示的时间 liveroomPopupKey?: string, waitAfterResetNormal?: number, // 重置为NORMAL后, 等待多久才调用setMode设置其他值, 默认800ms skipResetNormal?: boolean, // 是否跳过重置为NORMAL, 默认false } & { [key: string]: any }) // 其他需要透传给hyExt.popup.setMode的参数 => Promise<any> ``` ### createPromisfyFnWithCatch - createPromisfyFnWithCatch(options: PromisfyFnWithCatchOptions) - hack一个promisfy函数的Pending、Resolve、Reject过程,返回一个新promisfy函数。 #### 参数解析 ```js type PromisfyFn<T, U> = (...args: Array<T>) => Promise<U> type PromisfyFnWithCatchOptions = { executePromiseFn: PromisfyFn<any, any>, onPending?: (callArgs: Array<any>) => void onResolve?: (response: any) => void onReject?: (error: Error) => void } ``` #### Demo ```js // __tests__/func.test.ts const mockFn = createMockFn(() => Promise.resolve({b: 'bar'})) const mockCall = { a: 'foo' } const fn = createPromisfyFnWithCatch({ executePromiseFn: mockFn, onPending(args: any) { expect(args[0]).toBe(mockCall) }, onResolve(res: any) { expect(res).toMatchObject({b: 'bar'}) } }) fn(mockCall).then((res) => { expect(res).toBeTruthy() expect(mockFn.mock.calls.length).toBe(1) done() }) ``` ### once - once(fn) - 返回一个cache函数,缓存fn首次调用的结果,fn只会执行一次。 ```js // __tests__/func.test.ts const mockFn = createMockFn(() => true) const mockFnOnce = once(mockFn) const result1 = mockFnOnce() const result2 = mockFnOnce() expect(mockFn.mock.calls.length).toBe(1) expect(result1 === result2).toBe(true) ``` ### createPolling - createPolling(options: PollingOptions) - 返回一个轮询函数 #### 参数解析 ```js type PollingOptions = { intervalTime: number // 轮询周期。 fn: (...args: Array<any>) => boolean // 执行函数,返回一个是否继续轮询的标记,true代表继续,false代表结束。 immediately?: boolean // 默认是true, 轮询函数调用就马上执行fn;false,要等到intervalTime时间到达时执行fn。 onEnd?: NormalFn // 轮询结束时调用。 } ``` #### Demo ```js // __tests__/func.test.ts let callCount = 0 const mockFn = createMockFn((res: any) => { expect(res).toBe(1) callCount += 1 return callCount > 1 ? false : true }) const pollingFn = createPolling({ intervalTime: 500, fn: mockFn, onEnd() { expect(callCount).toBe(2) done() } }) pollingFn(1) ``` ### throttle - throttle(delay: number, fn: NormalFn) - 返回一个防抖函数, delay代表延时触发的时间,fn代表执行函数 #### Demo ```js // __tests__/func.test.ts const mockFn = createMockFn((res:any) => { expect(res).toBe(1) expect(mockFn.mock.calls.length).toBe(1) done() }) const throttleFn = throttle(1000, mockFn) throttleFn(1) throttleFn(1) ``` ### memory - memory(fn) - 返回一个缓存每次调用的结果并输出结果的函数,fn代表纯函数,入参成员必须是 number | string, 输出可以是any。 #### Demo ```js // __tests__/func.test.ts const mockFn = createMockFn((str: string, num: number) => { return str + num }) const cacheFn = memory<[string, number], string>(mockFn) const result1 = cacheFn('alex', 1) const result2 = cacheFn('alex', 1) expect(result1 === result2).toBe(true) expect(mockFn.mock.calls.length).toBe(1) }) ``` ## Promise风格函数 ### promiseTimeout - promiseTimeout(ms: number, promise: Promise<any>) - 返回一个promise,超时会reject一个超时字符串。 #### Demo ```js it('promiseTimeout pass', (done) => { const passPromise = createDelayPromise(200); promiseTimeout(500, passPromise).then((res) => { expect(res).toBe(true) done() }).catch(done) }) it('promiseTimeout timeout', (done) => { const failPromise = createDelayPromise(200); const timeout = 100 promiseTimeout(timeout, failPromise).catch((err) => { expect(err).toMatch(`Timed out in ${timeout}ms.`) done() }) }) ``` ### promiseRetry - promiseTimeout(maxExeCount: number, interval: number, cb: RetryHandleCallback) - 返回一个promise,超过调用数会reject一个错误。 #### 参数解析 ```js type RetryResult = { isDone: boolean payload: any } type RetryHandleCallback = (currExeCount: number) => Promise<RetryResult> ``` ### Demo ```js it('retry resolve', (done) => { const fn = jest.fn(async (count) => { return { isDone: count === 3 ? true : false, payload: { foo: 'bar' } } }) const promise = promiseRetry(3, 100, fn) promise.then((payload) => { expect(fn).toBeCalledTimes(3) expect(payload).toMatchObject({ foo: 'bar' }) done() }).catch(done) }) ```