Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: ♻️ 重构使用 requestAnimationFrame 的逻辑修复微信小程序报错方法重复定义的问题 #749

Merged
merged 1 commit into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/uni_modules/wot-design-uni/components/common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ export const requestAnimationFrame = (cb = () => {}) => {
* @param ms 延迟时间
* @returns
*/
export const pause = (ms: number) => {
export const pause = (ms: number = 1000 / 30) => {
return new AbortablePromise((resolve) => {
const timer = setTimeout(() => {
clearTimeout(timer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default {
<script lang="ts" setup>
import wdPickerView from '../../wd-picker-view/wd-picker-view.vue'
import { computed, ref, watch, onMounted } from 'vue'
import { debounce, isArray, isEqual, isNumber, requestAnimationFrame } from '../../common/util'
import { debounce, isArray, isEqual, isNumber, pause } from '../../common/util'
import { compareMonth, formatMonthTitle, getMonthEndDay, getMonths, getTimeData, getWeekLabel } from '../utils'
import Month from '../month/month.vue'
import { monthPanelProps, type MonthInfo, type MonthPanelTimeType, type MonthPanelExpose } from './types'
Expand Down Expand Up @@ -185,31 +185,31 @@ onMounted(() => {
/**
* 使当前日期或者选中日期滚动到可视区域
*/
function scrollIntoView() {
requestAnimationFrame(() => {
let activeDate: number | null = 0
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
async function scrollIntoView() {
// 等待渲染完毕
await pause()
let activeDate: number | null = 0
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
if (!activeDate) {
activeDate = Date.now()
}
let top: number = 0
for (let index = 0; index < months.value.length; index++) {
if (compareMonth(months.value[index].date, activeDate) === 0) {
break
}
top += months.value[index] ? Number(months.value[index].height) : 0
let top: number = 0
for (let index = 0; index < months.value.length; index++) {
if (compareMonth(months.value[index].date, activeDate) === 0) {
break
}
scrollTop.value = 0
requestAnimationFrame(() => {
scrollTop.value = top
})
})
top += months.value[index] ? Number(months.value[index].height) : 0
}
scrollTop.value = 0
// 等待渲染完毕
await pause()
scrollTop.value = top
}
/**
* 获取时间 picker 的数据
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default {
<script lang="ts" setup>
import { computed, ref, onMounted } from 'vue'
import { compareYear, formatYearTitle, getYears } from '../utils'
import { isArray, isNumber, requestAnimationFrame } from '../../common/util'
import { isArray, isNumber, pause } from '../../common/util'
import Year from '../year/year.vue'
import { yearPanelProps, type YearInfo, type YearPanelExpose } from './types'
Expand Down Expand Up @@ -68,31 +68,29 @@ onMounted(() => {
scrollIntoView()
})
function scrollIntoView() {
requestAnimationFrame(() => {
let activeDate: number | null = null
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
async function scrollIntoView() {
await pause()
let activeDate: number | null = null
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
if (!activeDate) {
activeDate = Date.now()
}
let top: number = 0
for (let index = 0; index < years.value.length; index++) {
if (compareYear(years.value[index].date, activeDate) === 0) {
break
}
top += years.value[index] ? Number(years.value[index].height) : 0
let top: number = 0
for (let index = 0; index < years.value.length; index++) {
if (compareYear(years.value[index].date, activeDate) === 0) {
break
}
scrollTop.value = 0
requestAnimationFrame(() => {
scrollTop.value = top
})
})
top += years.value[index] ? Number(years.value[index].height) : 0
}
scrollTop.value = 0
await pause()
scrollTop.value = top
Comment on lines +71 to +93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议优化异步滚动实现

当前实现存在以下几个问题:

  1. 使用了两次 pause(),可能导致不必要的延迟
  2. 重置 scrollTop 为 0 后再设置实际值可能导致视觉跳动
  3. 缺少异步操作的错误处理

建议按照以下方式优化实现:

 async function scrollIntoView() {
-  await pause()
   let activeDate: number | null = null
   if (isArray(props.value)) {
     activeDate = props.value![0]
   } else if (isNumber(props.value)) {
     activeDate = props.value
   }

   if (!activeDate) {
     activeDate = Date.now()
   }

   let top: number = 0
   for (let index = 0; index < years.value.length; index++) {
     if (compareYear(years.value[index].date, activeDate) === 0) {
       break
     }
     top += years.value[index] ? Number(years.value[index].height) : 0
   }
-  scrollTop.value = 0
-  await pause()
+  try {
+    await pause()
+    scrollTop.value = top
+  } catch (error) {
+    console.error('滚动位置设置失败:', error)
+  }
-  scrollTop.value = top
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function scrollIntoView() {
await pause()
let activeDate: number | null = null
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
if (!activeDate) {
activeDate = Date.now()
}
let top: number = 0
for (let index = 0; index < years.value.length; index++) {
if (compareYear(years.value[index].date, activeDate) === 0) {
break
}
top += years.value[index] ? Number(years.value[index].height) : 0
let top: number = 0
for (let index = 0; index < years.value.length; index++) {
if (compareYear(years.value[index].date, activeDate) === 0) {
break
}
scrollTop.value = 0
requestAnimationFrame(() => {
scrollTop.value = top
})
})
top += years.value[index] ? Number(years.value[index].height) : 0
}
scrollTop.value = 0
await pause()
scrollTop.value = top
async function scrollIntoView() {
let activeDate: number | null = null
if (isArray(props.value)) {
activeDate = props.value![0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
let top: number = 0
for (let index = 0; index < years.value.length; index++) {
if (compareYear(years.value[index].date, activeDate) === 0) {
break
}
top += years.value[index] ? Number(years.value[index].height) : 0
}
try {
await pause()
scrollTop.value = top
} catch (error) {
console.error('滚动位置设置失败:', error)
}
}

}
const yearScroll = (event: { detail: { scrollTop: number } }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ import wdActionSheet from '../wd-action-sheet/wd-action-sheet.vue'
import wdButton from '../wd-button/wd-button.vue'
import { ref, computed, watch } from 'vue'
import { dayjs } from '../common/dayjs'
import { deepClone, isArray, isEqual, padZero, requestAnimationFrame } from '../common/util'
import { deepClone, isArray, isEqual, padZero, pause } from '../common/util'
import { getWeekNumber, isRange } from '../wd-calendar-view/utils'
import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types'
Expand Down Expand Up @@ -313,7 +313,7 @@ function scrollIntoView() {
calendarView.value && calendarView.value && calendarView.value.$.exposed.scrollIntoView()
}
// 对外暴露方法
function open() {
async function open() {
const { disabled, readonly } = props
if (disabled || readonly) return
Expand All @@ -323,10 +323,9 @@ function open() {
lastCalendarValue.value = deepClone(calendarValue.value)
lastTab.value = currentTab.value
lastCurrentType.value = currentType.value
requestAnimationFrame(() => {
scrollIntoView()
})
// 等待渲染完毕
await pause()
scrollIntoView()
setTimeout(() => {
if (props.showTypeSwitch) {
calendarTabs.value.scrollIntoView()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default {
<script lang="ts" setup>
import wdIcon from '../wd-icon/wd-icon.vue'
import { computed, getCurrentInstance, onMounted, ref, watch, type CSSProperties } from 'vue'
import { addUnit, getRect, isArray, isDef, isPromise, isString, objToStyle, requestAnimationFrame, uuid } from '../common/util'
import { addUnit, getRect, isArray, isDef, isPromise, isString, objToStyle, pause, uuid } from '../common/util'
import { useParent } from '../composables/useParent'
import { COLLAPSE_KEY } from '../wd-collapse/types'
import { collapseItemProps, type CollapseItemExpose } from './types'
Expand Down Expand Up @@ -103,19 +103,18 @@ async function updateExpand(useBeforeExpand: boolean = true) {
}
function initRect() {
getRect(`#${collapseId.value}`, false, proxy).then((rect) => {
getRect(`#${collapseId.value}`, false, proxy).then(async (rect) => {
const { height: rectHeight } = rect
height.value = isDef(rectHeight) ? Number(rectHeight) : ''
requestAnimationFrame(() => {
if (isSelected.value) {
expanded.value = true
} else {
expanded.value = false
}
if (!inited.value) {
inited.value = true
}
})
await pause()
if (isSelected.value) {
expanded.value = true
} else {
expanded.value = false
}
if (!inited.value) {
inited.value = true
}
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import type { AnchorIndex } from './type'
import { indexBarInjectionKey, indexBarProps } from './type'
import { ref, getCurrentInstance, onMounted, reactive, nextTick, watch } from 'vue'
import { getRect, isDef, uuid, requestAnimationFrame } from '../common/util'
import { getRect, isDef, uuid, pause } from '../common/util'
import { useChildren } from '../composables/useChildren'
const props = defineProps(indexBarProps)
Expand Down Expand Up @@ -131,13 +131,12 @@ function handleTouchMove(e: TouchEvent) {
setScrollTop(getAnchorByPageY(clientY).$.exposed!.top.value - offsetTop)
}
function handleTouchEnd(e: TouchEvent) {
async function handleTouchEnd(e: TouchEvent) {
const clientY = e.changedTouches[0].pageY
state.activeIndex = getAnchorByPageY(clientY).index
setScrollTop(getAnchorByPageY(clientY).$.exposed!.top.value - offsetTop)
requestAnimationFrame(() => {
scrollState.touching = false
})
await pause()
scrollState.touching = false
}
function setScrollTop(top: number) {
Expand Down
23 changes: 11 additions & 12 deletions src/uni_modules/wot-design-uni/components/wd-input/wd-input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default {
<script lang="ts" setup>
import wdIcon from '../wd-icon/wd-icon.vue'
import { computed, onBeforeMount, ref, watch } from 'vue'
import { isDef, objToStyle, pause, requestAnimationFrame } from '../common/util'
import { isDef, objToStyle, pause } from '../common/util'
import { useCell } from '../composables/useCell'
import { FORM_KEY, type FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent'
Expand Down Expand Up @@ -230,24 +230,23 @@ function formatValue(value: string | number) {
function togglePwdVisible() {
isPwdVisible.value = !isPwdVisible.value
}
function handleClear() {
async function handleClear() {
clearing.value = true
focusing.value = false
inputValue.value = ''
if (props.focusWhenClear) {
focused.value = false
}
requestAnimationFrame(() => {
if (props.focusWhenClear) {
focused.value = true
focusing.value = true
}
emit('change', {
value: ''
})
emit('update:modelValue', inputValue.value)
emit('clear')
await pause()
if (props.focusWhenClear) {
focused.value = true
focusing.value = true
}
emit('change', {
value: ''
})
emit('update:modelValue', inputValue.value)
emit('clear')
}
async function handleBlur() {
// 等待150毫秒,clear执行完毕
Expand Down
60 changes: 22 additions & 38 deletions src/uni_modules/wot-design-uni/components/wd-search/wd-search.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
<template>
<view :class="rootClass" :style="customStyle">
<!--自定义label插槽-->
<!--搜索框主体-->
<view class="wd-search__block">
<slot name="prefix"></slot>
<view class="wd-search__field">
<view v-if="!placeholderLeft" :style="coverStyle" class="wd-search__cover" @click="closeCover">
<wd-icon name="search" custom-class="wd-search__search-icon"></wd-icon>
<text class="wd-search__placeholder-txt">{{ placeholder || translate('search') }}</text>
</view>
<!--icon:search-->
<wd-icon v-if="showInput || str || placeholderLeft" name="search" custom-class="wd-search__search-left-icon"></wd-icon>
<!--搜索框-->
<input
v-if="showInput || str || placeholderLeft"
:placeholder="placeholder || translate('search')"
Expand All @@ -27,14 +23,11 @@
:maxlength="maxlength"
:focus="isFocused"
/>
<!--icon:clear-->
<wd-icon v-if="str" custom-class="wd-search__clear wd-search__clear-icon" name="error-fill" @click="clearSearch" />
</view>
</view>
<!--the button behind input,care for hideCancel without displaying-->

<slot v-if="!hideCancel" name="suffix">
<!--默认button-->
<view class="wd-search__cancel" @click="handleCancel">
{{ cancelTxt || translate('cancel') }}
</view>
Expand All @@ -56,7 +49,7 @@ export default {
<script lang="ts" setup>
import wdIcon from '../wd-icon/wd-icon.vue'
import { type CSSProperties, computed, onMounted, ref, watch } from 'vue'
import { objToStyle, requestAnimationFrame } from '../common/util'
import { objToStyle, pause } from '../common/util'
import { useTranslate } from '../composables/useTranslate'
import { searchProps } from './types'
Expand Down Expand Up @@ -110,22 +103,17 @@ const coverStyle = computed(() => {
return objToStyle(coverStyle)
})
function hackFocus(focus: boolean) {
async function hackFocus(focus: boolean) {
showInput.value = focus
requestAnimationFrame(() => {
isFocused.value = focus
})
await pause()
isFocused.value = focus
}
function closeCover() {
async function closeCover() {
if (props.disabled) return
requestAnimationFrame()
.then(() => requestAnimationFrame())
.then(() => requestAnimationFrame())
.then(() => {
showPlaceHolder.value = false
hackFocus(true)
})
await pause(100)
showPlaceHolder.value = false
hackFocus(true)
}
/**
* @description input的input事件handle
Expand All @@ -141,29 +129,25 @@ function inputValue(event: any) {
/**
* @description 点击清空icon的handle
*/
function clearSearch() {
async function clearSearch() {
str.value = ''
clearing.value = true
if (props.focusWhenClear) {
isFocused.value = false
}
requestAnimationFrame()
.then(() => requestAnimationFrame())
.then(() => requestAnimationFrame())
.then(() => {
if (props.focusWhenClear) {
showPlaceHolder.value = false
hackFocus(true)
} else {
showPlaceHolder.value = true
hackFocus(false)
}
emit('change', {
value: ''
})
emit('update:modelValue', '')
emit('clear')
})
await pause(100)
if (props.focusWhenClear) {
showPlaceHolder.value = false
hackFocus(true)
} else {
showPlaceHolder.value = true
hackFocus(false)
}
emit('change', {
value: ''
})
emit('update:modelValue', '')
emit('clear')
Comment on lines +132 to +150
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

建议重构清空搜索的逻辑

当前的 clearSearch 函数职责过重,建议将其拆分为更小的函数以提高可维护性。同时,建议添加对 clearing 标志的使用说明。

建议重构为:

+/**
+ * 重置搜索框状态
+ */
+async function resetSearchState(shouldFocus: boolean): Promise<void> {
+  await pause(100)
+  showPlaceHolder.value = !shouldFocus
+  if (shouldFocus) {
+    hackFocus(true)
+  } else {
+    hackFocus(false)
+  }
+}

+/**
+ * 触发清空相关事件
+ */
+function emitClearEvents(): void {
+  emit('change', { value: '' })
+  emit('update:modelValue', '')
+  emit('clear')
+}

 /**
+ * 清空搜索内容并重置状态
+ */
 async function clearSearch(): Promise<void> {
   str.value = ''
   clearing.value = true
   if (props.focusWhenClear) {
     isFocused.value = false
   }
-  await pause(100)
-  if (props.focusWhenClear) {
-    showPlaceHolder.value = false
-    hackFocus(true)
-  } else {
-    showPlaceHolder.value = true
-    hackFocus(false)
-  }
-  emit('change', { value: '' })
-  emit('update:modelValue', '')
-  emit('clear')
+  await resetSearchState(props.focusWhenClear)
+  emitClearEvents()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function clearSearch() {
str.value = ''
clearing.value = true
if (props.focusWhenClear) {
isFocused.value = false
}
requestAnimationFrame()
.then(() => requestAnimationFrame())
.then(() => requestAnimationFrame())
.then(() => {
if (props.focusWhenClear) {
showPlaceHolder.value = false
hackFocus(true)
} else {
showPlaceHolder.value = true
hackFocus(false)
}
emit('change', {
value: ''
})
emit('update:modelValue', '')
emit('clear')
})
await pause(100)
if (props.focusWhenClear) {
showPlaceHolder.value = false
hackFocus(true)
} else {
showPlaceHolder.value = true
hackFocus(false)
}
emit('change', {
value: ''
})
emit('update:modelValue', '')
emit('clear')
/**
* 重置搜索框状态
*/
async function resetSearchState(shouldFocus: boolean): Promise<void> {
await pause(100)
showPlaceHolder.value = !shouldFocus
if (shouldFocus) {
hackFocus(true)
} else {
hackFocus(false)
}
}
/**
* 触发清空相关事件
*/
function emitClearEvents(): void {
emit('change', { value: '' })
emit('update:modelValue', '')
emit('clear')
}
/**
* 清空搜索内容并重置状态
*/
async function clearSearch(): Promise<void> {
str.value = ''
clearing.value = true
if (props.focusWhenClear) {
isFocused.value = false
}
await resetSearchState(props.focusWhenClear)
emitClearEvents()
}

}
/**
* @description 点击搜索按钮时的handle
Expand Down
Loading