用 watchEffect 实现自动滚动到底部需监听消息列表变化、判断是否在底部、执行 DOM 滚动并清理异步冲突;通过 ref 绑定容器、nextTick 确保 DOM 更新、onInvalidate 防止重复滚动,配合 autoScrollEnabled 开关和精准 flush 时机控制。
用 watchEffect 自动滚动到页面底部,关键在于监听消息列表变化、判断当前是否处于底部、再触发 DOM 滚动操作,并配合清理机制防止异步冲突。
当新消息加入 messages(一个 ref 数组)时,watchEffect 会自动重新执行。无需手动列出依赖项,只要在回调里读取了 messages.value.length 或遍历它,就会被追踪。
ref 绑定 DOM 元素(如 messagesContainerRef)nextTick() 等待 DOM 更新完成,再读取容器的滚动属性container.scrollTop + container.clientHeight >= container.scrollHeight - 2
连续快速发多条消息时,前一次的滚动可能还没执行完,后一次就已开始——这会导致滚动位置异常或卡顿。必须用 onInvalidate 清理未完成的操作。
onInvalidate 会自动调用上一次注册的清理函数scrollTop,而是把滚动逻辑包装进一个可取消的异步流程(例如用 Promise + 标记位,或简单地清除定时器)setTimeout 或 requestAnimationFrame 做节流,但核心是保证“只生效最后一次”自动滚动不该干扰用户主动浏览历史消息。引入一个开关 autoScrollEnabled,在 watchEffect 开头判断其值,为 false 时直接返回,不执行后续逻辑。
watchEffect 会自动追踪它true 并手动滚动一次,不影响日常开关逻辑watchEffect 默认在组件更新前(flush: 'pre')运行,此时 DOM 还没刷新,直接读取 scrollHeight 会得到旧值。必须调整执行时机或等待更新完成。
flush,但在回调内显式调用 await nextTick()
{ flush: 'post' },让回调在 DOM 更新后自动执行watchEffect 外部用 watch 或生命周期钩子同步处理滚动,会破坏响应式联动