@hyext/utils
TypeScript icon, indicating that this package has built-in type declarations

3.0.6 • Public • Published

@hyext/utils

小程序业务开发工具库

Installation

$ npm i @hyext/utils -S

Usage

import { memory, createLogger, createSDKPolyfill  }  from '@hyext/utils'

下述的每个函数都可以参照上方import导入。

Summary

  • createLogger - 创建一个logger对象
  • createSDKPolyfill - 基于小程序SDK二次封装的SDK对象
  • createPromisfyFnWithCatch - Catch Promisy Func
  • once - once
  • throttle - 防抖
  • createPolling - 轮询
  • memory - 记忆函数
  • promiseTimeout - 超时promise
  • 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 - 延迟
  • 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

入参解析

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

// 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

参数解析

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

// 与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

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>
  )
}

参数

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函数。

参数解析

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

// __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只会执行一次。
// __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) - 返回一个轮询函数

参数解析

type PollingOptions = {
  intervalTime: number // 轮询周期。
  fn: (...args:  Array<any>) => boolean // 执行函数,返回一个是否继续轮询的标记,true代表继续,false代表结束。
  immediately?: boolean // 默认是true, 轮询函数调用就马上执行fn;false,要等到intervalTime时间到达时执行fn。
  onEnd?: NormalFn // 轮询结束时调用。
}

Demo

// __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

// __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

// __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) - 返回一个promise,超时会reject一个超时字符串。

Demo

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一个错误。

参数解析

type RetryResult = { 
  isDone: boolean
  payload: any
}

type RetryHandleCallback = (currExeCount: number) => Promise<RetryResult>

Demo

 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)
  })

Package Sidebar

Install

npm i @hyext/utils

Weekly Downloads

106

Version

3.0.6

License

ISC

Unpacked Size

117 kB

Total Files

16

Last publish

Collaborators

  • hy-ext
  • wundereye
  • maizhiying
  • xiangwang123
  • zhangjiaheng
  • limingyi_100