[Varlet]彩蛋:实现按钮的auto-loading

390 阅读3分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战

写在前面

前几天,有小伙伴在我们的issue下留言,大致意思是询问能否给button组件添加一个自动加载的事件,也就是说当click的方法返回一个Promise时自动去维护按钮的loading状态,从而省去在外部手动维护loading的工作。

这个功能听着就很实用,自然而然的就进了我们的feature

本文将从我cr以及refactor这部分功能的心得来分享一下。

相应 issue 地址

设计思路

  • 我们需要添加一个auto-loadingprops用来标记是否是否开启自动loading

  • 在组件内部维护一个pending用于标记是否显示loading

  • onClick&&onTouchstart事件进行处理,为其添加维护pending的逻辑

代码实现

我们新增一个attemptAutoLoading函数用来处理自动加载的逻辑。

该函数接收onClick&&onTouchstart事件的返回值。

如果开起了自动加载,那么到这个方法内我们先开启loading,然后使用Promise.resolve将函数返回值包裹成Promise

最终在finally中,将loading关闭。(组件并不关新Promiseresolve还是reject,只要结束就关闭loading)

Promise.resolve(value) 方法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。

const pending: Ref<boolean> = ref(false)

const attemptAutoLoading = (result: any) => {
  if (props.autoLoading) {
    pending.value = true
    Promise.resolve(result).finally(() => {
      pending.value = false
    })
  }
}

const handleClick = (e: Event) => {
  const { loading, disabled, onClick } = props

  if (!onClick || loading || disabled || pending.value) {
    return
  }
  attemptAutoLoading(onClick(e))
}

代码直通车

单元测试

完成功能后,对应的单元测试也是必不可少的,我们来逐行解析看看auto-loading的单测该如何编写。

test('test button auto-loading', async () => { 定义一个 auto-loading 的单元测试
  const onClick = () => { // 声明 onClick 方法 是一个有100ms延迟的异步方法
    return new Promise((resolve) => { 
      setTimeout(resolve, 100)
    })
  }

  const onTouchstart = () => { // 声明 onTouchstart 方法 是一个有100ms延迟的异步方法
    return new Promise((resolve) => {
      setTimeout(resolve, 100)
    })
  }

  const wrapper = mount(VarButton, {  // 加载按钮组件
    props: {
      autoLoading: true,  // 开启自动loading
      onClick,
      onTouchstart,
    },
  })

  await trigger(wrapper, 'click')  // 触发 onClick 方法
  expect(wrapper.find('.var-loading').exists()).toBeTruthy() // 检查是否开启了 loading
  await delay(100) // 等待 100ms
  expect(wrapper.find('.var-loading').exists()).toBeFalsy() // 检查是否关闭了 loading

  await trigger(wrapper, 'touchstart') // 触发 onTouchstart 方法
  expect(wrapper.find('.var-loading').exists()).toBeTruthy() // 检查是否开启了 loading
  await delay(100)
  expect(wrapper.find('.var-loading').exists()).toBeFalsy() // 检查是否关闭了 loading

  expect(wrapper.html()).toMatchSnapshot() // 对比快照
  wrapper.unmount() // 卸载按钮组件
})

最后

感谢 a145789 的建议。

varlet 这是我们的仓库地址欢迎大家star && pr。

issue 也欢迎大家提出宝贵的意见与建议以帮助我们更好的发展。