npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@hyext/utils

v3.0.6

Published

A tools lib for huya miniapp business development

Downloads

143

Readme

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