-
Notifications
You must be signed in to change notification settings - Fork 4.9k
feat(taro-components-advanced): 新增 waterflow 组件 #17653
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
Conversation
Walkthrough本次变更在 Changes
Sequence Diagram(s)sequenceDiagram
participant 用户 as User
participant WaterFlow as <WaterFlow />
participant Root as Root
participant Section as Section
participant Node as Node
participant ScrollView as Taro.ScrollView
用户->>WaterFlow: 渲染组件,传入 children
WaterFlow->>Root: 初始化 Root,注册参数
WaterFlow->>Section: 为每个 section 创建 Section 实例
Section->>Node: 创建并注册 Node
Root->>Section: 计算可渲染区间
Section->>Node: 计算节点布局
用户->>ScrollView: 滚动
ScrollView->>WaterFlow: onScroll 事件
WaterFlow->>Root: 更新 scrollOffset、scrollDirection
Root->>Section: 通知可见区间变更
Section->>Node: 更新渲染范围
Node->>WaterFlow: 触发布局、resize 事件
WaterFlow->>用户: 渲染可见 items
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
♻️ Duplicate comments (1)
packages/taro-components-advanced/src/components/water-flow/root.ts (1)
276-315
: 再次出现冗余标签 (loopSeciton
),且大小写拼写错误
同上,应统一去除标签,避免 lint 报警并提升可读性。🧰 Tools
🪛 Biome (1.9.4)
[error] 276-276: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.(lint/complexity/noUselessLabel)
🧹 Nitpick comments (10)
packages/taro-components-advanced/src/components/water-flow/use-unmount.ts (1)
3-9
: 依赖项数组可能需要添加回调函数
useUnmount
钩子实现了组件卸载时执行回调的功能,但存在一个潜在问题:依赖数组为空[]
,没有包含回调函数cb
。如果cb
在组件生命周期内发生变化,钩子将继续使用初始版本的回调函数,可能导致闭包陈旧问题。建议修改实现,将回调函数添加到依赖项中,或者使用 useRef 保存最新的回调引用:
-export function useUnmount(cb: () => void) { - useEffect(() => { - return () => { - cb() - } - }, []) -} +export function useUnmount(cb: () => void) { + const cbRef = useRef(cb); + + useEffect(() => { + cbRef.current = cb; + }); + + useEffect(() => { + return () => { + cbRef.current(); + } + }, []); +}packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts (1)
10-22
: 可简化 useMemoizedFn 实现
useMemoizedFn
钩子实现了函数记忆化,保持引用稳定性同时使用最新的函数版本。但当前实现中useMemo
的使用方式可能不必要地增加了复杂性。建议简化实现,直接更新
fnRef.current
:export function useMemoizedFn<T extends Noop>(fn: T) { const fnRef = useRef<T>(fn) - fnRef.current = useMemo<T>(() => fn, [fn]) + fnRef.current = fn const memoizedFn = useRef<MemoizedFn<T>>() if (!memoizedFn.current) { memoizedFn.current = function (this, ...args) { return fnRef.current.apply(this, args) } } return memoizedFn.current as MemoizedFn<T> }这种方式会更加简洁,同时保留原有功能。
packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts (1)
29-29
: SSR 第三个参数可省略,或改为undefined
以减少冗余React 18 之后
useSyncExternalStore
的第三个参数主要用于 SSR。若当前组件不会在服务端渲染,可直接省略;否则应提供符合服务端环境的快照函数,避免误用同一引用。packages/taro-components-advanced/src/components/water-flow/utils.ts (1)
26-28
:isSameRenderRange
使用JSON.stringify
可能带来性能隐患当渲染区间数组较大或频繁比较时,
JSON.stringify
会产生不必要的字符串分配和 GC 压力。可以改为长度与元素逐一比较,或缓存上一次结果。-export function isSameRenderRange(a: any[], b: any[]) { - return JSON.stringify(a) === JSON.stringify(b) -} +export function isSameRenderRange(a: any[], b: any[]) { + if (a === b) return true + if (a.length !== b.length) return false + for (let i = 0; i < a.length; i++) { + const [as, ae] = a[i] + const [bs, be] = b[i] + if (as !== bs || ae !== be) return false + } + return true +}packages/taro-components-advanced/src/components/water-flow/interface.ts (2)
12-18
:style
的类型来源不一致,建议统一引用以避免潜在冲突
WaterFlowProps
先在Omit<ScrollViewProps, ...>
中去掉了style
,随后通过Pick<BaseProps, 'style'>
重新声明。
然而BaseProps['style']
来自react
的CSSProperties
,而ScrollViewProps['style']
在某些端可能是 Taro 自己的类型别名,二者在部分平台可能并不完全兼容。可改为直接复用
ScrollViewProps['style']
,这样不会丢失 Taro 侧对样式属性的能力倾向:- Pick<BaseProps, 'style'> + { style?: ScrollViewProps['style'] }这能最大限度保持与小程序端
style
语义的一致性。
50-52
:FlowItemContainerProps
强耦合Node
,可通过范型降低循环依赖风险目前直接把
node
写死为Node
类型,导致 interface 层与实现层产生循环引用(interface.ts ←→ node.ts)。
考虑改成:export interface FlowItemContainerProps<T extends Node = Node> extends BaseProps { node: T }这样既兼容现有实现,又允许后续扩展/mock 更灵活,降低圈复杂度。
packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
71-80
:setStateBatch
中重复调用getState()
导致不必要的对象创建在循环里每次都调用
getState()
生成新对象,浪费性能。可先缓存引用:- for (const key in s) { - if (Object.prototype.hasOwnProperty.call(s, key)) { - const rawValue = this.getState()[key] + const current = this.state + for (const key in s) { + if (Object.prototype.hasOwnProperty.call(s, key)) { + const rawValue = current[key]这是一个小优化,但在高频状态批量更新时能减少 GC 压力。
packages/taro-components-advanced/src/components/water-flow/node.ts (1)
101-109
:getRectSizeSync
命名与实现不符,可能误导后续维护者
该函数实际返回 Promise,却以 Sync 结尾;此外,此处无需await
同步版函数。请确认命名与实现一致,或调整调用方式。packages/taro-components-advanced/src/components/water-flow/section.ts (1)
262-306
: 滚动过程频繁JSON.stringify
可能造成性能瓶颈
getNodeRenderRange
每次都对二维数组做 stringify 比较 O(n²)。在大量节点/高帧率滚动下容易造成卡顿。可改为 version 号或 shallow compare。packages/taro-components-advanced/src/components/water-flow/root.ts (1)
480-493
: 位运算右移用于除以 2 有精度风险
clientHeight >>> 1
会把数字截断为 32 位整数,超出 2^32 的高度将溢出负值。直接使用/ 2
更安全且可读。- if (acc >= clientHeight >>> 1) { + if (acc >= clientHeight / 2) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
packages/taro-components-advanced/src/components/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-item.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/interface.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/node.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/root.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-unmount.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/utils.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/water-flow.ts
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/taro-components-advanced/src/components/water-flow/section.ts (5)
packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
StatefulEventBus
(36-86)packages/taro-components-advanced/src/components/water-flow/node.ts (1)
Node
(34-147)packages/taro-components-advanced/src/components/water-flow/utils.ts (3)
createImperativePromise
(38-52)getMatrixPosition
(17-21)isSameRenderRange
(26-28)packages/taro-components-advanced/src/components/water-flow/root.ts (4)
Root
(54-500)RootEvents
(41-47)scrollBoundaryEnd
(356-358)scrollBoundaryStart
(349-351)packages/taro-components-advanced/src/components/water-flow/interface.ts (1)
Size
(54-57)
packages/taro-components-advanced/src/components/water-flow/root.ts (5)
packages/taro-components-advanced/src/components/water-flow/interface.ts (4)
WaterFlowProps
(12-33)BaseProps
(6-10)ScrollDirection
(66-66)Size
(54-57)packages/taro-components-advanced/src/components/water-flow/utils.ts (2)
getSysInfo
(5-9)isSameRenderRange
(26-28)packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
StatefulEventBus
(36-86)packages/taro-components-advanced/src/components/water-flow/section.ts (1)
Section
(56-307)packages/taro-components-advanced/src/components/water-flow/node.ts (1)
Node
(34-147)
🪛 Biome (1.9.4)
packages/taro-components-advanced/src/components/water-flow/root.ts
[error] 276-276: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
[error] 340-340: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
- GitHub Check: Build Rust Binding / stable - aarch64-apple-darwin
- GitHub Check: Build Rust Binding / stable - x86_64-apple-darwin
🔇 Additional comments (5)
packages/taro-components-advanced/src/components/index.ts (1)
3-3
: 组件导出模块已正确更新新增了对
./water-flow
模块的导出,使得瀑布流组件可以被外部模块访问和使用。这符合现有的模块导出模式,与其它组件导出保持一致。packages/taro-components-advanced/src/components/water-flow/index.ts (1)
1-4
: 导出结构清晰完整index 文件正确地导出了所有水流布局组件系统所需的组件和类型。导出结构清晰,包含了组件(FlowItem、FlowSection、WaterFlow)、钩子函数(useFlowItemPositioner)以及必要的类型定义。这种模式使得使用者可以方便地从单一入口引入所需功能。
packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts (1)
19-27
: 避免在找不到state
时直接读取实例属性,降低运行时异常风险
getSnapshot
在无法通过getState()
取到目标字段时,会直接使用inst[attr]
作为兜底。然而StatefulEventBus
的实例本身并不一定暴露同名字段,如果字段不存在将返回undefined
,容易导致 UI 同步失效。建议始终以getState()
作为唯一可靠来源,或在兜底前做显式存在性校验并给出警告日志。packages/taro-components-advanced/src/components/water-flow/water-flow.ts (1)
107-117
: 缺少对getScrollViewContextNode
失败的兜底处理若传入的
selector
未匹配到节点,node
为undefined
,再调用scrollTo
会抛错。建议增加判空:- getScrollViewContextNode(`#${root.id}`).then((node: any) => { - node.scrollTo({ + getScrollViewContextNode(`#${root.id}`).then((node: any) => { + if (!node) return + node.scrollTo({packages/taro-components-advanced/src/components/water-flow/root.ts (1)
329-341
: 阈值判断条件存在逻辑瑕疵
this.lowerThresholdCount !== Infinity
基本恒为true
,若想表达“设置为 Infinity 时关闭下阈值”,应显式比较Number.POSITIVE_INFINITY
或直接判断lowerThresholdCount > 0
。🧰 Tools
🪛 Biome (1.9.4)
[error] 340-340: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.(lint/complexity/noUselessLabel)
packages/taro-components-advanced/src/components/water-flow/flow-section.ts
Show resolved
Hide resolved
packages/taro-components-advanced/src/components/water-flow/flow-section.ts
Outdated
Show resolved
Hide resolved
packages/taro-components-advanced/src/components/water-flow/flow-item.ts
Outdated
Show resolved
Hide resolved
packages/taro-components-advanced/src/components/water-flow/flow-item.ts
Outdated
Show resolved
Hide resolved
packages/taro-components-advanced/src/components/water-flow/flow-item.ts
Outdated
Show resolved
Hide resolved
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #17653 +/- ##
========================================
Coverage 58.37% 58.37%
========================================
Files 469 469
Lines 27230 27230
Branches 5984 5777 -207
========================================
Hits 15895 15895
+ Misses 9965 9839 -126
- Partials 1370 1496 +126
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
927b56b
to
f0420ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (3)
packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
7-14
: 🛠️ Refactor suggestion发布事件时应支持通配符事件监听
当前
pub
方法只向特定事件的订阅者发布事件,没有处理通配符订阅者的逻辑。如果在代码其他部分有使用通配符(如 Symbol.for('broadcast'))来监听所有事件的需求,这些监听器将无法收到事件通知。建议修改实现以支持通配符事件:
pub<P = undefined>(ev: T, payload?: P) { const set = this.eventsMap.get(ev) if (set) { for (const cb of set) { cb(ev, payload) } } + // 支持通配符事件监听 + const broadcast = Symbol.for('broadcast') + const broadcastSet = this.eventsMap.get(broadcast as unknown as T) + if (broadcastSet) { + for (const cb of broadcastSet) { + cb(ev, payload) + } + } }packages/taro-components-advanced/src/components/water-flow/node.ts (1)
83-95
:⚠️ Potential issue节点测量过程缺少超时保护机制
当前
NodeEvents.Resize
事件处理中直接await this.measure()
,根据已知的学习记录,如果节点不存在,getRectSizeSync
可能会一直处于 pending 状态,导致整个布局过程被阻塞。建议添加超时机制:
this.sub(NodeEvents.Resize, async () => { const { width, height } = this.getState() - const newSize = await this.measure() + // 添加超时保护机制 + const timeoutPromise = new Promise<{ width: number; height: number }>((_, reject) => { + setTimeout(() => reject(new Error('Measure timeout')), 300) // 300ms 超时 + }) + + let newSize: { width: number; height: number } + try { + newSize = await Promise.race([this.measure(), timeoutPromise]) + } catch (err) { + console.warn(`测量节点 ${this.id} 失败:`, err) + return + } if (newSize.height === height && newSize.width === width) { return } this.updateBehindNodesPosition() this.section.pub(SectionEvents.Resize, { node: this, newSize, originalSize: { width, height }, }) })packages/taro-components-advanced/src/components/water-flow/root.ts (1)
213-253
: 🛠️ Refactor suggestion不必要的 label 标签使用
在
setUpperThresholdScrollTop
方法中使用了loopSeciton
和loopItem
标签来控制嵌套循环,使代码结构复杂化且被静态分析工具标记为问题。建议重构为更清晰的控制流:
- loopSeciton: for (let i = 0; i < sectionSize; i++) { + for (let i = 0; i < sectionSize; i++) { // ... 其他代码 - loopItem: for (let j = 0; j < columnSize; j++) { + for (let j = 0; j < columnSize; j++) { colTracker.accCount += 1 colTracker.accHeight += column[j].getState().height + (j === 0 ? 0 : section.rowGap) if (colTracker.accCount >= this.upperThresholdCount) { - break loopItem + break } } } for (const [, colTracker] of sectionTracker) { if (colTracker.accCount >= this.upperThresholdCount) { this.upperThresholdScrollTop = colTracker.accHeight - break loopSeciton + return this.upperThresholdScrollTop } } }这样的重构提高了代码可读性,同时消除了静态分析工具的警告。
🧹 Nitpick comments (6)
packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
69-79
: 批量更新状态时未处理深层对象比较当前的
setStateBatch
方法使用Object.is
进行浅层比较,这对于复杂的嵌套对象/数组状态可能导致更新被忽略。考虑添加深度比较选项或文档说明这一限制:
setStateBatch(s: Partial<S>) { for (const key in s) { if (Object.prototype.hasOwnProperty.call(s, key)) { const rawValue = this.getState()[key] const newValue = s[key] - if (!Object.is(rawValue, newValue)) { + // 简单类型使用 Object.is,对象类型可考虑添加深度比较 + if (!Object.is(rawValue, newValue) && + (typeof rawValue !== 'object' || + JSON.stringify(rawValue) !== JSON.stringify(newValue))) { this.setStateIn(key, newValue) } } } }或者在类注释中说明此限制,提醒开发者在使用复杂对象时需手动触发更新。
packages/taro-components-advanced/src/components/water-flow/section.ts (2)
121-131
: Resize 事件处理使用了防抖但未指定延迟时间当前对
SectionEvents.Resize
事件使用了debounce
包装,这对于频繁触发的调整大小事件是很好的性能优化。但是,没有显式指定防抖延迟时间,可能导致在不同设备上的行为不一致。建议明确指定防抖延迟时间:
this.sub<{ node: Node, newSize: Size, originalSize: Size }>( SectionEvents.Resize, - debounce(() => { + debounce(() => { this.setStateIn('height', this.maxColumnHeight) this.updateBehindSectionsPosition() if (this.isInRange) { this.setStateIn('renderRange', this.getNodeRenderRange()) } this.root.pub(RootEvents.Resize) - }) + }, 16) // 约定 16ms 延迟(一帧的时间) )
261-305
: 渲染范围计算逻辑复杂,考虑拆分
getNodeRenderRange
方法包含多层嵌套循环和复杂的条件判断,降低了代码可读性和可维护性。建议将该方法拆分为更小的辅助函数,如:
calculateVisibleNodes(column, columnIndex)
- 计算每列中可见的节点applyOverscan(range, direction)
- 应用预加载范围这样可以提高代码的可读性和可测试性,同时保持功能不变。
packages/taro-components-advanced/src/components/water-flow/root.ts (3)
1-1
: 使用 eslint-disable 忽略标签规则文件开头使用
eslint-disable no-labels
注释禁用了标签检查规则。尽管在某些情况下使用标签可以简化复杂逻辑的控制流,但它也可能降低代码可读性和可维护性。考虑重构使用标签的代码部分,改用更清晰的控制流结构(如提前返回、辅助函数等)。
462-494
: 使用位操作符降低代码可读性在
calcCacheSection
方法中,使用了位移操作符>>>
进行除 2 操作(第 475 和 487 行)。虽然这是一个微小的优化,但降低了代码可读性。建议使用更清晰的表达式:
- if (acc >= clientHeight >>> 1) { + if (acc >= clientHeight / 2) {这样对大多数开发者来说更容易理解,同时现代 JavaScript 引擎通常会优化这类简单的数学运算。
169-194
: 递归渲染可能导致调用栈溢出
renderInitialLayout
方法使用递归方式渲染分组。当分组数量非常大时,可能导致调用栈溢出。考虑使用迭代方法或分批处理:
renderInitialLayout(i = 0) { const processNextSection = () => { Taro.nextTick(() => { const sectionSize = this.sections.length; if (i >= sectionSize || i < 0) { this.pub(RootEvents.InitialRenderCompleted, null); return; } const section = this.sections[i]; section.layoutedSignal.promise.then(() => { this.setStateIn('renderRange', [ 0, i + 1 > sectionSize ? sectionSize - 1 : i + 1, ]); // 容器可视区域已经填满了,没必要再继续 if (section.getState().scrollTop > this.getState().containerSize.height) { this.pub(RootEvents.InitialRenderCompleted, section); return; } i++; processNextSection(); }); }); }; processNextSection(); }这样虽然功能相同,但避免了真正的递归调用导致的潜在栈溢出风险。
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
packages/taro-components-advanced/src/components/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-item.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/interface.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/node.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/root.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-unmount.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/utils.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/water-flow.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/taro-components-advanced/src/components/index.ts
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/taro-components-advanced/src/components/water-flow/use-unmount.ts
- packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts
- packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts
- packages/taro-components-advanced/src/components/water-flow/index.ts
- packages/taro-components-advanced/src/components/water-flow/interface.ts
- packages/taro-components-advanced/src/components/water-flow/flow-section.ts
- packages/taro-components-advanced/src/components/water-flow/flow-item.ts
- packages/taro-components-advanced/src/components/water-flow/water-flow.ts
- packages/taro-components-advanced/src/components/water-flow/utils.ts
🧰 Additional context used
🧠 Learnings (1)
packages/taro-components-advanced/src/components/water-flow/node.ts (3)
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点,会一直处于 pending 状态而不是抛出错误。在这种情况下,应该使用 Promise.race 添加超时机制,而不是仅依赖 try/catch 来处理错误。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点并且重试次数用尽,会一直处于 pending 状态而不是抛出错误,因为它在 retryTimes <= 0 时直接返回而不 resolve 或 reject Promise。应该使用 Promise.race 添加超时机制来解决这个问题。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 getRectSizeSync 函数中,如果找不到指定的节点,函数会一直处于 pending 状态而不是抛出错误。在这种情况下,使用 try/catch 处理不够充分,需要添加超时处理如 Promise.race 来避免无限等待。
🧬 Code Graph Analysis (1)
packages/taro-components-advanced/src/components/water-flow/section.ts (5)
packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
StatefulEventBus
(34-84)packages/taro-components-advanced/src/components/water-flow/node.ts (1)
Node
(34-148)packages/taro-components-advanced/src/components/water-flow/utils.ts (3)
createImperativePromise
(38-52)getMatrixPosition
(17-21)isSameRenderRange
(26-28)packages/taro-components-advanced/src/components/water-flow/root.ts (4)
Root
(54-495)RootEvents
(41-47)scrollBoundaryEnd
(351-353)scrollBoundaryStart
(344-346)packages/taro-components-advanced/src/components/water-flow/interface.ts (1)
Size
(55-58)
🪛 Biome (1.9.4)
packages/taro-components-advanced/src/components/water-flow/root.ts
[error] 273-273: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
[error] 344-344: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: Build Rust Binding / stable - x86_64-apple-darwin
- GitHub Check: Build Rust Binding / stable - aarch64-apple-darwin
- GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
- GitHub Check: Build Rust Binding / stable - x86_64-apple-darwin
- GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
- GitHub Check: Build Rust Binding / stable - aarch64-apple-darwin
🔇 Additional comments (4)
packages/taro-components-advanced/src/components/water-flow/node.ts (2)
114-134
: 更新节点位置时正确考虑了行间距当前实现在
updateBehindNodesPosition
方法中正确地考虑了rowGap
,避免了滚动过程中图片等异步内容拉伸后,后续节点与前节点重叠的问题。这是一个很好的改进。
140-147
: 计算节点可见性的逻辑清晰且高效
isInRange
计算器通过比较节点的滚动位置和高度与根容器的滚动边界,高效地确定节点是否在可视区域内。这种实现避免了每次滚动时对所有节点进行完整的边界计算。packages/taro-components-advanced/src/components/water-flow/section.ts (2)
138-144
: 最大列高计算正确考虑了行间距在
maxColumnHeight
计算中,通过添加(column.length - 1) * this.rowGap
正确地将行间距计入总高度,这确保了布局精确度。
215-238
: 列内节点定位正确应用了行间距在
updateColumnNode
方法中,通过在计算scrollTop
和top
值时加入this.rowGap
,确保了节点之间有适当的间距,防止在滚动和内容调整时出现重叠。
this.setupSubscriptions() | ||
getRectSizeSync(`#${id}`, 100).then( | ||
({ width = windowWidth, height = windowHeight }) => { | ||
this.setStateIn('containerSize', { | ||
width, | ||
height, | ||
}) | ||
} | ||
) | ||
this.renderInitialLayout() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
缺少异步错误处理机制
在构造函数中,对 getRectSizeSync
的调用没有错误处理机制。如果获取容器尺寸失败,可能导致组件初始化不完整。
建议添加错误处理:
- getRectSizeSync(`#${id}`, 100).then(
+ getRectSizeSync(`#${id}`, 100).then(
({ width = windowWidth, height = windowHeight }) => {
this.setStateIn('containerSize', {
width,
height,
})
}
- )
+ ).catch(err => {
+ console.warn(`获取容器 #${id} 尺寸失败:`, err)
+ // 使用默认尺寸
+ this.setStateIn('containerSize', {
+ width: windowWidth,
+ height: windowHeight,
+ })
+ })
📝 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.
this.setupSubscriptions() | |
getRectSizeSync(`#${id}`, 100).then( | |
({ width = windowWidth, height = windowHeight }) => { | |
this.setStateIn('containerSize', { | |
width, | |
height, | |
}) | |
} | |
) | |
this.renderInitialLayout() | |
this.setupSubscriptions() | |
getRectSizeSync(`#${id}`, 100).then( | |
({ width = windowWidth, height = windowHeight }) => { | |
this.setStateIn('containerSize', { | |
width, | |
height, | |
}) | |
} | |
).catch(err => { | |
console.warn(`获取容器 #${id} 尺寸失败:`, err) | |
// 使用默认尺寸 | |
this.setStateIn('containerSize', { | |
width: windowWidth, | |
height: windowHeight, | |
}) | |
}) | |
this.renderInitialLayout() |
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/root.ts around lines 109 to 118, the call to getRectSizeSync lacks error handling for the asynchronous operation. To fix this, add a catch block to the promise chain to handle any errors that occur during the size retrieval, ensuring the component can handle failures gracefully and maintain proper initialization.
loopSeciton: for (let i = sectionSize - 1; i > 0; i--) { | ||
const section = this.sections[i] | ||
const sectionTracker = tracker[i] | ||
const columnMap = section.columnMap | ||
// 扫描当前分组的每一列 | ||
for (let col = 0; col < columnMap.length; col++) { | ||
const column = columnMap[col] | ||
const columnSize = column.length | ||
if (!sectionTracker.has(col)) { | ||
if (i === sectionSize - 1) { | ||
sectionTracker.set(col, { accCount: 0, scrollTop: 0 }) | ||
} else { | ||
const belowSectionTracker = tracker[i + 1] | ||
sectionTracker.set(col, { | ||
accCount: Math.max( | ||
...[...belowSectionTracker.values()].map( | ||
(nodeTracker) => nodeTracker.accCount | ||
) | ||
), | ||
scrollTop: 0, | ||
}) | ||
} | ||
} | ||
const colTracker = sectionTracker.get(col)! | ||
// 从当前列的最后一行开始往前扫描 | ||
loopItem: for (let j = columnSize - 1; j >= 0; j--) { | ||
colTracker.accCount += 1 | ||
colTracker.scrollTop = column[j].getState().scrollTop | ||
if (colTracker.accCount >= this.lowerThresholdCount) { | ||
break loopItem | ||
} | ||
} | ||
} | ||
|
||
for (const [, colTracker] of sectionTracker) { | ||
if (colTracker.accCount >= this.lowerThresholdCount) { | ||
this.lowerThresholdScrollTop = colTracker.scrollTop | ||
break loopSeciton | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
不必要的 label 标签使用
与 setUpperThresholdScrollTop
类似,setLowerThresholdScrollTop
方法也使用了 loopSeciton
和 loopItem
标签控制流,增加了复杂度且触发了静态分析警告。
建议采用相同的重构策略,使用提前返回代替标签:
- loopSeciton: for (let i = sectionSize - 1; i > 0; i--) {
+ for (let i = sectionSize - 1; i > 0; i--) {
// ... 其他代码
- loopItem: for (let j = columnSize - 1; j >= 0; j--) {
+ for (let j = columnSize - 1; j >= 0; j--) {
colTracker.accCount += 1
colTracker.scrollTop = column[j].getState().scrollTop
if (colTracker.accCount >= this.lowerThresholdCount) {
- break loopItem
+ break
}
}
}
for (const [, colTracker] of sectionTracker) {
if (colTracker.accCount >= this.lowerThresholdCount) {
this.lowerThresholdScrollTop = colTracker.scrollTop
- break loopSeciton
+ return this.lowerThresholdScrollTop
}
}
}
📝 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.
loopSeciton: for (let i = sectionSize - 1; i > 0; i--) { | |
const section = this.sections[i] | |
const sectionTracker = tracker[i] | |
const columnMap = section.columnMap | |
// 扫描当前分组的每一列 | |
for (let col = 0; col < columnMap.length; col++) { | |
const column = columnMap[col] | |
const columnSize = column.length | |
if (!sectionTracker.has(col)) { | |
if (i === sectionSize - 1) { | |
sectionTracker.set(col, { accCount: 0, scrollTop: 0 }) | |
} else { | |
const belowSectionTracker = tracker[i + 1] | |
sectionTracker.set(col, { | |
accCount: Math.max( | |
...[...belowSectionTracker.values()].map( | |
(nodeTracker) => nodeTracker.accCount | |
) | |
), | |
scrollTop: 0, | |
}) | |
} | |
} | |
const colTracker = sectionTracker.get(col)! | |
// 从当前列的最后一行开始往前扫描 | |
loopItem: for (let j = columnSize - 1; j >= 0; j--) { | |
colTracker.accCount += 1 | |
colTracker.scrollTop = column[j].getState().scrollTop | |
if (colTracker.accCount >= this.lowerThresholdCount) { | |
break loopItem | |
} | |
} | |
} | |
for (const [, colTracker] of sectionTracker) { | |
if (colTracker.accCount >= this.lowerThresholdCount) { | |
this.lowerThresholdScrollTop = colTracker.scrollTop | |
break loopSeciton | |
} | |
} | |
} | |
for (let i = sectionSize - 1; i > 0; i--) { | |
const section = this.sections[i] | |
const sectionTracker = tracker[i] | |
const columnMap = section.columnMap | |
// 扫描当前分组的每一列 | |
for (let col = 0; col < columnMap.length; col++) { | |
const column = columnMap[col] | |
const columnSize = column.length | |
if (!sectionTracker.has(col)) { | |
if (i === sectionSize - 1) { | |
sectionTracker.set(col, { accCount: 0, scrollTop: 0 }) | |
} else { | |
const belowSectionTracker = tracker[i + 1] | |
sectionTracker.set(col, { | |
accCount: Math.max( | |
...[...belowSectionTracker.values()].map( | |
(nodeTracker) => nodeTracker.accCount | |
) | |
), | |
scrollTop: 0, | |
}) | |
} | |
} | |
const colTracker = sectionTracker.get(col)! | |
// 从当前列的最后一行开始往前扫描 | |
for (let j = columnSize - 1; j >= 0; j--) { | |
colTracker.accCount += 1 | |
colTracker.scrollTop = column[j].getState().scrollTop | |
if (colTracker.accCount >= this.lowerThresholdCount) { | |
break | |
} | |
} | |
} | |
for (const [, colTracker] of sectionTracker) { | |
if (colTracker.accCount >= this.lowerThresholdCount) { | |
this.lowerThresholdScrollTop = colTracker.scrollTop | |
return this.lowerThresholdScrollTop | |
} | |
} | |
} |
🧰 Tools
🪛 Biome (1.9.4)
[error] 273-273: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/root.ts between lines 273 and 313, the method setLowerThresholdScrollTop uses labeled loops 'loopSeciton' and 'loopItem' to control flow, which adds unnecessary complexity and triggers static analysis warnings. Refactor the code by removing these labels and replacing the labeled breaks with early returns or flag variables to exit loops cleanly, simplifying the control flow and improving readability.
f0420ea
to
69e110a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (3)
packages/taro-components-advanced/src/components/water-flow/root.ts (3)
109-118
:⚠️ Potential issue缺少异步错误处理机制
在构造函数中,对
getRectSizeSync
的调用没有错误处理机制。如果获取容器尺寸失败,可能导致组件初始化不完整。建议添加错误处理:
- getRectSizeSync(`#${id}`, 100).then( + getRectSizeSync(`#${id}`, 100).then( ({ width = windowWidth, height = windowHeight }) => { this.setStateIn('containerSize', { width, height, }) } - ) + ).catch(err => { + console.warn(`获取容器 #${id} 尺寸失败:`, err) + // 使用默认尺寸 + this.setStateIn('containerSize', { + width: windowWidth, + height: windowHeight, + }) + })
213-253
: 🛠️ Refactor suggestion不必要的标签使用增加代码复杂度
使用标签
loopSeciton
和loopItem
控制流程使代码复杂化,并触发了静态分析警告。可通过提前返回或使用函数封装来消除标签使用。建议采用以下重构方案:
- loopSeciton: for (let i = 0; i < sectionSize; i++) { + for (let i = 0; i < sectionSize; i++) { const section = this.sections[i] const sectionTracker = tracker[i] const columnMap = section.columnMap // 扫描当前分组的每一列 for (let col = 0; col < columnMap.length; col++) { // ...现有代码... } for (const [, colTracker] of sectionTracker) { if (colTracker.accCount >= this.upperThresholdCount) { this.upperThresholdScrollTop = colTracker.accHeight - break loopSeciton + return this.upperThresholdScrollTop } } } + return this.upperThresholdScrollTop或者考虑提取为独立函数,使逻辑更清晰。
273-313
: 🛠️ Refactor suggestionsetLowerThresholdScrollTop方法也使用了不必要的标签
与
setUpperThresholdScrollTop
类似,setLowerThresholdScrollTop
方法也使用了loopSeciton
和loopItem
标签控制流,增加了复杂度且触发了静态分析警告。建议采用相同的重构策略,使用提前返回代替标签:
- loopSeciton: for (let i = sectionSize - 1; i > 0; i--) { + for (let i = sectionSize - 1; i > 0; i--) { // ... 其他代码 - loopItem: for (let j = columnSize - 1; j >= 0; j--) { + for (let j = columnSize - 1; j >= 0; j--) { colTracker.accCount += 1 colTracker.scrollTop = column[j].getState().scrollTop if (colTracker.accCount >= this.lowerThresholdCount) { - break loopItem + break } } } for (const [, colTracker] of sectionTracker) { if (colTracker.accCount >= this.lowerThresholdCount) { this.lowerThresholdScrollTop = colTracker.scrollTop - break loopSeciton + return this.lowerThresholdScrollTop } } } + return this.lowerThresholdScrollTop🧰 Tools
🪛 Biome (1.9.4)
[error] 273-273: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.(lint/complexity/noUselessLabel)
🧹 Nitpick comments (3)
packages/taro-components-advanced/src/components/water-flow/node.ts (1)
102-102
: measure方法中应添加重试次数参数getRectSizeSync 第三个参数是重试次数,当前值 3 是硬编码的。如果节点加载较慢,可能需要更多的重试次数。
建议将重试次数参数化,以便更灵活地根据不同场景调整:
- public async measure() { + public async measure(retryTimes = 3) { - const { height, width } = await getRectSizeSync(`#${this.id}`, 100, 3) + const { height, width } = await getRectSizeSync(`#${this.id}`, 100, retryTimes) this.setStateBatch({ width, height, layouted: true, }) return { width, height } }packages/taro-components-advanced/src/components/water-flow/section.ts (1)
205-205
: 分组间距处理存在不一致当更新后续分组位置时,使用了
this.rowGap
作为分组间距,但实际上应该使用根容器统一的分组间距,而不是当前分组的行间距。建议考虑添加一个专门的
sectionGap
属性来控制分组之间的间距,或确保使用的rowGap
是合适的:nextSection.setStateIn( 'scrollTop', - currentSection.getState().scrollTop + currentSection.getState().height + this.rowGap + currentSection.getState().scrollTop + currentSection.getState().height + this.root.sectionGap )如果确实需要复用
rowGap
作为分组间的间距,请在代码注释中明确说明这种设计意图。packages/taro-components-advanced/src/components/water-flow/root.ts (1)
213-213
: 标签名称中存在拼写错误标签名
loopSeciton
拼写错误,应为loopSection
。虽然功能上没有问题,但代码可读性会受影响。更正拼写错误:
- loopSeciton: for (let i = 0; i < sectionSize; i++) { + loopSection: for (let i = 0; i < sectionSize; i++) {不过,正如静态分析工具所提示的,更好的解决方案是重构代码以移除不必要的标签用法。
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
packages/taro-components-advanced/src/components/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-item.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/flow-section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/index.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/interface.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/node.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/root.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/section.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/use-unmount.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/utils.ts
(1 hunks)packages/taro-components-advanced/src/components/water-flow/water-flow.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- packages/taro-components-advanced/src/components/index.ts
- packages/taro-components-advanced/src/components/water-flow/use-unmount.ts
- packages/taro-components-advanced/src/components/water-flow/index.ts
- packages/taro-components-advanced/src/components/water-flow/use-observed-attr.ts
- packages/taro-components-advanced/src/components/water-flow/flow-section.ts
- packages/taro-components-advanced/src/components/water-flow/water-flow.ts
- packages/taro-components-advanced/src/components/water-flow/interface.ts
- packages/taro-components-advanced/src/components/water-flow/flow-item.ts
- packages/taro-components-advanced/src/components/water-flow/utils.ts
- packages/taro-components-advanced/src/components/water-flow/use-memoized-fn.ts
- packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts
🧰 Additional context used
🧠 Learnings (2)
packages/taro-components-advanced/src/components/water-flow/node.ts (3)
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点,会一直处于 pending 状态而不是抛出错误。在这种情况下,应该使用 Promise.race 添加超时机制,而不是仅依赖 try/catch 来处理错误。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点并且重试次数用尽,会一直处于 pending 状态而不是抛出错误,因为它在 retryTimes <= 0 时直接返回而不 resolve 或 reject Promise。应该使用 Promise.race 添加超时机制来解决这个问题。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 getRectSizeSync 函数中,如果找不到指定的节点,函数会一直处于 pending 状态而不是抛出错误。在这种情况下,使用 try/catch 处理不够充分,需要添加超时处理如 Promise.race 来避免无限等待。
packages/taro-components-advanced/src/components/water-flow/root.ts (3)
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点,会一直处于 pending 状态而不是抛出错误。在这种情况下,应该使用 Promise.race 添加超时机制,而不是仅依赖 try/catch 来处理错误。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 water-flow 组件中,getRectSizeSync 函数如果找不到指定的节点并且重试次数用尽,会一直处于 pending 状态而不是抛出错误,因为它在 retryTimes <= 0 时直接返回而不 resolve 或 reject Promise。应该使用 Promise.race 添加超时机制来解决这个问题。
Learnt from: Single-Dancer
PR: NervJS/taro#17653
File: packages/taro-components-advanced/src/components/water-flow/node.ts:83-95
Timestamp: 2025-05-06T06:55:44.054Z
Learning: 在 Taro 的 getRectSizeSync 函数中,如果找不到指定的节点,函数会一直处于 pending 状态而不是抛出错误。在这种情况下,使用 try/catch 处理不够充分,需要添加超时处理如 Promise.race 来避免无限等待。
🧬 Code Graph Analysis (1)
packages/taro-components-advanced/src/components/water-flow/root.ts (5)
packages/taro-components-advanced/src/components/water-flow/interface.ts (4)
WaterFlowProps
(13-34)BaseProps
(7-11)ScrollDirection
(67-67)Size
(55-58)packages/taro-components-advanced/src/components/water-flow/utils.ts (2)
getSysInfo
(5-9)isSameRenderRange
(26-28)packages/taro-components-advanced/src/components/water-flow/stateful-event-bus.ts (1)
StatefulEventBus
(34-84)packages/taro-components-advanced/src/components/water-flow/section.ts (1)
Section
(55-306)packages/taro-components-advanced/src/components/water-flow/node.ts (1)
Node
(34-148)
🪛 Biome (1.9.4)
packages/taro-components-advanced/src/components/water-flow/root.ts
[error] 273-273: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
[error] 344-344: Unnecessary label.
Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.
(lint/complexity/noUselessLabel)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
- GitHub Check: Build Rust Binding / stable - aarch64-apple-darwin
- GitHub Check: Build Rust Binding / stable - aarch64-apple-darwin
- GitHub Check: Build Rust Binding / stable - x86_64-pc-windows-msvc
- GitHub Check: Build Rust Binding / stable - x86_64-apple-darwin
- GitHub Check: Build Rust WASM / stable - wasm32-wasi
this.sub(NodeEvents.Resize, async () => { | ||
const { width, height } = this.getState() | ||
const newSize = await this.measure() | ||
if (newSize.height === height && newSize.width === width) { | ||
return | ||
} | ||
this.updateBehindNodesPosition() | ||
this.section.pub(SectionEvents.Resize, { | ||
node: this, | ||
newSize, | ||
originalSize: { width, height }, | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
需要添加超时处理机制以防 measure 挂起
根据您之前提供的反馈,当节点不存在时,getRectSizeSync
会一直处于 pending 状态而不是抛出错误。Resize 事件处理程序中直接 await measure() 可能导致整个流程被阻塞。
建议使用 Promise.race 添加超时机制:
this.sub(NodeEvents.Resize, async () => {
const { width, height } = this.getState()
- const newSize = await this.measure()
+ // 添加超时保护
+ const timeoutPromise = new Promise<{ width: number; height: number }>((_, reject) => {
+ setTimeout(() => reject(new Error('Measure timeout')), 300) // 300ms 超时
+ })
+
+ let newSize: { width: number; height: number }
+ try {
+ newSize = await Promise.race([this.measure(), timeoutPromise])
+ } catch (err) {
+ console.warn(`测量节点 ${this.id} 失败:`, err)
+ return
+ }
if (newSize.height === height && newSize.width === width) {
return
}
this.updateBehindNodesPosition()
this.section.pub(SectionEvents.Resize, {
node: this,
newSize,
originalSize: { width, height },
})
})
📝 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.
this.sub(NodeEvents.Resize, async () => { | |
const { width, height } = this.getState() | |
const newSize = await this.measure() | |
if (newSize.height === height && newSize.width === width) { | |
return | |
} | |
this.updateBehindNodesPosition() | |
this.section.pub(SectionEvents.Resize, { | |
node: this, | |
newSize, | |
originalSize: { width, height }, | |
}) | |
}) | |
this.sub(NodeEvents.Resize, async () => { | |
const { width, height } = this.getState() | |
// 添加超时保护 | |
const timeoutPromise = new Promise<{ width: number; height: number }>((_, reject) => { | |
setTimeout(() => reject(new Error('Measure timeout')), 300) // 300ms 超时 | |
}) | |
let newSize: { width: number; height: number } | |
try { | |
newSize = await Promise.race([this.measure(), timeoutPromise]) | |
} catch (err) { | |
console.warn(`测量节点 ${this.id} 失败:`, err) | |
return | |
} | |
if (newSize.height === height && newSize.width === width) { | |
return | |
} | |
this.updateBehindNodesPosition() | |
this.section.pub(SectionEvents.Resize, { | |
node: this, | |
newSize, | |
originalSize: { width, height }, | |
}) | |
}) |
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/node.ts around lines 83 to 95, the async Resize event handler awaits measure() directly, which can cause blocking if measure hangs due to getRectSizeSync never resolving. To fix this, wrap the measure() call with Promise.race against a timeout promise to ensure the await does not hang indefinitely. Implement a timeout mechanism that rejects or resolves after a set duration, and handle the timeout case appropriately to prevent blocking the Resize event processing.
private calcScrollTop() { | ||
return this.root.sections | ||
.slice(0, this.order) | ||
.reduce((acc, section) => acc + section.maxColumnHeight, 0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calcScrollTop方法计算有误
当前的 calcScrollTop
方法只累加了之前所有分组的高度,但没有考虑分组之间的间距。
需要在分组高度累加时也添加间距:
private calcScrollTop() {
return this.root.sections
.slice(0, this.order)
- .reduce((acc, section) => acc + section.maxColumnHeight, 0)
+ .reduce((acc, section, index, array) => {
+ // 添加分组高度
+ const sectionHeight = section.maxColumnHeight;
+ // 如果不是最后一个分组,还需要加上间距
+ const gap = index < array.length - 1 ? section.rowGap : 0;
+ return acc + sectionHeight + gap;
+ }, 0)
}
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/section.ts around lines 252 to 256, the calcScrollTop method incorrectly calculates the scroll top by summing only the maxColumnHeight of previous sections without including the spacing between sections. To fix this, modify the calculation to add the spacing value for each section in addition to its height during the reduce operation, ensuring the total scroll top accounts for both heights and gaps between sections.
@@ -0,0 +1,495 @@ | |||
/* eslint-disable no-labels */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
不必要的ESLint禁用注释
文件顶部使用了 /* eslint-disable no-labels */
注释,这通常是临时解决方案。标签的使用会增加代码复杂度,应考虑重构代码而不是禁用 lint 规则。
建议删除此注释,并重构使用标签的代码部分,如 setUpperThresholdScrollTop
和 setLowerThresholdScrollTop
方法。
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/root.ts at line 1, there is an unnecessary ESLint disable comment for no-labels. Remove the /* eslint-disable no-labels */ comment and refactor the code in the setUpperThresholdScrollTop and setLowerThresholdScrollTop methods to eliminate the use of labels, restructuring the logic to avoid labeled statements and improve code clarity.
renderInitialLayout(i = 0) { | ||
Taro.nextTick(() => { | ||
const sectionSize = this.sections.length | ||
|
||
if (i >= sectionSize || i < 0) { | ||
this.pub(RootEvents.InitialRenderCompleted, null) | ||
return | ||
} | ||
const section = this.sections[i] | ||
section.layoutedSignal.promise.then(() => { | ||
this.setStateIn('renderRange', [ | ||
0, | ||
i + 1 > sectionSize ? sectionSize - 1 : i + 1, | ||
]) | ||
// 容器可视区域已经填满了,没必要再继续 | ||
if ( | ||
section.getState().scrollTop > this.getState().containerSize.height | ||
) { | ||
this.pub(RootEvents.InitialRenderCompleted, section) | ||
return | ||
} | ||
|
||
this.renderInitialLayout(i + 1) | ||
}) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renderInitialLayout方法缺少错误处理
如果 layoutedSignal.promise
被拒绝,当前实现没有任何错误处理机制,可能导致初始渲染中断且没有提示。
建议添加 catch 块来处理可能的错误:
section.layoutedSignal.promise.then(() => {
this.setStateIn('renderRange', [
0,
i + 1 > sectionSize ? sectionSize - 1 : i + 1,
])
// 容器可视区域已经填满了,没必要再继续
if (
section.getState().scrollTop > this.getState().containerSize.height
) {
this.pub(RootEvents.InitialRenderCompleted, section)
return
}
this.renderInitialLayout(i + 1)
+ }).catch(err => {
+ console.warn(`分组 ${section.id} 布局计算失败:`, err)
+ // 即使某个分组失败也继续后续渲染
+ this.renderInitialLayout(i + 1)
})
📝 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.
renderInitialLayout(i = 0) { | |
Taro.nextTick(() => { | |
const sectionSize = this.sections.length | |
if (i >= sectionSize || i < 0) { | |
this.pub(RootEvents.InitialRenderCompleted, null) | |
return | |
} | |
const section = this.sections[i] | |
section.layoutedSignal.promise.then(() => { | |
this.setStateIn('renderRange', [ | |
0, | |
i + 1 > sectionSize ? sectionSize - 1 : i + 1, | |
]) | |
// 容器可视区域已经填满了,没必要再继续 | |
if ( | |
section.getState().scrollTop > this.getState().containerSize.height | |
) { | |
this.pub(RootEvents.InitialRenderCompleted, section) | |
return | |
} | |
this.renderInitialLayout(i + 1) | |
}) | |
}) | |
} | |
renderInitialLayout(i = 0) { | |
Taro.nextTick(() => { | |
const sectionSize = this.sections.length | |
if (i >= sectionSize || i < 0) { | |
this.pub(RootEvents.InitialRenderCompleted, null) | |
return | |
} | |
const section = this.sections[i] | |
section.layoutedSignal.promise.then(() => { | |
this.setStateIn('renderRange', [ | |
0, | |
i + 1 > sectionSize ? sectionSize - 1 : i + 1, | |
]) | |
// 容器可视区域已经填满了,没必要再继续 | |
if ( | |
section.getState().scrollTop > this.getState().containerSize.height | |
) { | |
this.pub(RootEvents.InitialRenderCompleted, section) | |
return | |
} | |
this.renderInitialLayout(i + 1) | |
}).catch(err => { | |
console.warn(`分组 ${section.id} 布局计算失败:`, err) | |
// 即使某个分组失败也继续后续渲染 | |
this.renderInitialLayout(i + 1) | |
}) | |
}) | |
} |
🤖 Prompt for AI Agents (early access)
In packages/taro-components-advanced/src/components/water-flow/root.ts around lines 169 to 194, the renderInitialLayout method lacks error handling for the layoutedSignal.promise. To fix this, add a catch block after the then call on layoutedSignal.promise to handle any rejected promises. In the catch block, log or handle the error appropriately to prevent the initial rendering process from silently failing.
这个 PR 做了什么? (简要描述所做更改)
新增 waterflow 组件
这个 PR 是什么类型? (至少选择一个)
这个 PR 涉及以下平台:
Summary by CodeRabbit