
如何做出仿抖音的短视频无限滑动加载拆解 douyin-vue 的 Vue 移动端列表实现【免费下载链接】douyinVue3 Pinia 仿抖音Vue 在移动端的最佳实践 . Imitate TikTok Vue Best practices on Mobile项目地址: https://gitcode.com/GitHub_Trending/do/douyin做移动端列表时上滑自动加载下一页、下拉还能刷新这套体验很少被当成难点——但真上手写的时候重复请求、加载白屏、滚动位置丢失等问题都会冒出来。这篇文章以 douyin-vueGitHub_Trending/do/douyin基于 Vue3 Vite Pinia 的仿抖音短视频项目为样本从用户体验出发把无限滑动列表背后的分页预加载、加载锁、下拉刷新交互和状态反馈逐层拆开。读完后你会拿到一套可以直接落地的 Vue 无限滚动实现思路。1. 先看效果什么叫无限分页列表最常见的两种翻车一种是页尾挂个加载更多按钮逼用户手动点另一种是一次性请求全量数据首屏等待时间拉满。短视频场景对两者都不能忍——用户手指一停内容就该无缝接上。douyin-vue 的首页视频流就是这种效果滑到快见底的位置时下一页数据已经在路上屏幕不出现任何空白期。要复现这种感觉Web 端需要同时解决四件事提前量别等真滑到底部才请求提前几十像素就触发防重发滚动事件每秒触发几十次请求不能跟着重复发状态可见加载中、没数据了用户要能看出来可刷新在顶部下拉松手后拉回并重新拉取第一页。2. 跑起来三条命令先摸到真实手感分析之前建议先把项目跑起来感受一下因为很多取舍比如下拉位移的上限只有亲手拖过才有体感。git clone https://gitcode.com/GitHub_Trending/do/douyin cd douyin npm install npm run devPC 上打开控制台后按CtrlShiftM切到手机模式即可预览。项目不需要后端数据放在本地public/data/下的 JSON 里通过axios-mock-adapter拦截 API 请求后返回所以行为稳定方便对照源码调试。3. 分工拆解Scroll 管手势ScrollList 管数据整个方案就两个组件各管一半src/components/Scroll.vue 负责滚动容器、触摸手势和快到底了的探测src/components/ScrollList.vue 负责页码、分页请求和数据数组业务方只需通过插槽渲染列表项。ScrollList 的全部状态就是一个响应式对象const state reactive({ list: [], total: 0, pageNo: 0, pageSize: 10, loading: false })pageNo/pageSize是分页参数total用来判断是否已经全部拉完loading是一把请求锁。下面两个细节是它能稳定跑的关键。请求锁滚动事件连发也不怕Scroll滚动到阈值时会emit(pulldown)而滚动是连续动作一页请求还没回来时事件可能已经触发了好几轮。ScrollList 的做法是先进锁、再加页码function loadData() { if (state.loading) return // 上一次请求没结束直接放弃 state.pageNo getData() }loading在请求发出前置位、返回后释放getData内部还有一道同样的检查。双重保险保证同一时间最多只有一个在途请求页码也不会跳。预加载距离底部 60px 就动手触发时机写在Scroll的scroll方法里if (this.wrapper.scrollHeight - this.wrapper.clientHeight this.wrapper.scrollTop 60) { this.$emit(pulldown) }scrollHeight - clientHeight是内容超出视口的可滚动范围和scrollTop比较就是在算距离底部还有多远。加上 60 意味着提前 60px 触发——对用户来说感觉是刚好滑到底数据就到了这个提前量既覆盖了一次弱网请求的时间又不会在列表刚铺开时就提前把后几页全拉走。4. 下拉刷新一次触摸的三步走Scroll组件开了useRefresh后用原生触摸事件模拟了弹性下拉整个过程分三步。第一步记录起点。只有当容器滚到顶部scrollTop 0且还没有起点时才记下手指的pageYif (this.wrapper.scrollTop 0 this.startMoveY null) { this.startMoveY e.touches[0].pageY }这一步保证了列表停在中间位置时下拉手势不会误触发。第二步跟随位移并限制松垮感。拖动过程中实时算出偏移量let distance e.touches[0].pageY - this.startMoveY this.distance distance - 40 10 ? distance - 40 : 10distance - 40相当于手指走 40px 才露出底下的加载指示器超过 10px 后钳制在 10px 不动继续往下拖没有收益——这就是再拉也不跟的阻尼手感。位移通过内联样式作用到内容区/* pullUpStyle 计算属性生成的效果 */ transform: translate3d(0, 10px, 0); transition-duration: 300ms; /* 松手回弹时才启用 */拖动时transition是 0ms手指到哪跟到哪松手后才切成 300ms回弹动画才显得弹。第三步松手判定并回调。touchend时只要还带着位移且仍在顶部就锁定刷新态并通知父组件if (this.distance ! null this.wrapper.scrollTop 0) { this.refresh true this.$emit(refresh) }ScrollList 收到refresh后把pageNo归零、整页替换list。整个过程中用户看到的是加载图标跟着手指走、松手后回弹到 10px 的悬停位5. 把加载状态摆在明面上无限列表最怕卡住了还是没数据分不清ScrollList 用三个状态把边界都交代清楚首次为空full-loading直接传!state.list.length全屏 Loading 占位翻页中列表底部挂一个非全屏的 Loading 指示器已经拉完total ! 0 total list.length时渲染NoMore提示没有更多了。注意没有更多依赖接口返回total——只返回当前页的 API 撑不起这个判断这是集成时容易被忽略的约定。新增数据这一步也很讲究state.list state.list.concat(res.data.list)用concat追加而不是整体替换新数组Vue 的 diff 只需处理新增的几条记录已有节点原样保留长列表翻页时的 DOM 开销明显更小——数据量越大这种差异越明显。组件对外的接口就一个函数加一个事件业务页面这样接入ScrollList :apirecommendedShop refreshonRefresh template #default{ list } !-- 渲染 list 里的每一项 -- /template /ScrollList首页视频流recommendedVideo、社区页recommendedPost、商城的商品推荐recommendedShop都是同一个用法还有个容易忽略的细节src/utils/hooks/useScroll.ts 在路由onDeactivated时记录scrollToponActivated时再恢复。配合 keep-alive 的路由缓存用户从详情页返回时列表还停在原处而不是弹回顶部——对列表型页面来说这是体验分水岭。6. 移动端性能的取舍与可以继续挖的方向回到性能这套实现做对了几件小事touch-action: pan-y交给浏览器原生处理纵向滚动不自己算滚轮惯性滚动不掉帧滚动回调里做了条件过滤比如pulldown事件只在绑定了监听器且越过阈值时才发出到底了的重复事件最终也被请求锁挡在门外列表容器本身不加复杂特效滚动期间浏览器只做 transform/位置这类可合成层的计算。长列表的天花板方案是虚拟滚动只渲染可视区域内的条目当前版本没有上但项目里已经预留了 src/components/WaterfallList.vue——它把传入的列表按奇偶下标拆成左右两列做双列瀑布流和 Scroll 组合就能实现瀑布流 无限加载改造成本很低。如果想继续深入三个方向都贴着现有代码改动面不大可视区渲染用IntersectionObserver给每个条目挂哨兵进视口才播放/加载进一步压缩长列表的内存占用横向与纵向双轴给Scroll增加方向控制就能做横滑的作品九宫格这类列表失败重试目前getData请求失败只弹查询失败的 toast可以在失败时回滚pageNo并在底部渲染一个点击重试避免用户只能靠下拉刷新续命。一句话总结无限滑动列表没有魔法核心就是提前 60px 触发 请求锁防重 状态三态反馈 增量拼接数据这四件事。把职责拆给Scroll和ScrollList之后手势归手势、数据归数据每一块都能独立测试和替换——这也是这个项目最值得抄走的部分。【免费下载链接】douyinVue3 Pinia 仿抖音Vue 在移动端的最佳实践 . Imitate TikTok Vue Best practices on Mobile项目地址: https://gitcode.com/GitHub_Trending/do/douyin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考