PC上拉滚动优化

🔥 上拉加载:从底层原理到实战技巧(Vue/React全场景)

背景 上拉加载作为提升用户体验的「隐形神器」,在聊天界面、商品列表、动态流等场景中无处不在。本文将手把手带你掌握三种主流实现方案,从底层原理到实战技巧一网打尽!

方法一:观察者模式 -- 优雅的现代方案

利用 IntersectionObserver API 实现轻量级上拉加载,完美适配 Vue/React 等框架。

核心代码

vue{4-25} 复制代码
<template>
    <div class="list-container">
        <div v-for="item in list" :key="item.id">{{ item }}</div>
        <!-- 触发器:关键在最后一位 -->
        <div id="bottom-trigger" class="trigger"></div>
    </div>
</template>
<script>
export default {
    mounted() {
       const bottomTrigger = document.getElementById('bottom-trigger');
        // 创建一个 IntersectionObserver 实例
        const observer = new IntersectionObserver(
            (entries, observer) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        // 当触底检测元素进入视口时
                        console.log('Bottom reached!');
                        // do something 加载更多数据
                        this.loadMore()
                    }
                });
            },
            {
                // 配置观察选项
                threshold: 0.5 // 当触底检测元素至少 50% 进入视口时触发
            }
        );
        // 开始观察触底检测元素
        observer.observe(bottomTrigger);
    },
    methods: {
        async loadMore() {
            // 模拟异步加载
            const newData = await fetchData();
            this.list.push(...newData);
        }
    }
}
</script>   

💡 关键技巧

  1. 触发器定位:必须放在列表最后一位,确保滚动到底部时触发
  2. 性能优化 :设置 threshold 控制触发灵敏度(0.1=10%进入视口)
  3. 防抖机制 :通过 IntersectionObserver 内置防抖,无需额外处理

方法二:Element UI InfiniteScroll -- 企业级方案

Element UI 的 v-infinite-scroll 指令封装了复杂逻辑,适合快速开发。

📌相关api

  • v-infinite-scroll:赋值相应的加载方法,可实现滚动到底部时自动执行加载方法
  • infinite-scroll-distance:触发加载的距离阈值,单位为px;可以根据开发实际情况调整

📌 核心代码

vue 复制代码
<template>
    <div 
      v-infinite-scroll="load" 
      :infinite-scroll-disabled="loading || !hasMore" 
      :infinite-scroll-distance="50"
      style="height: 600px; overflow: auto;"
    >
        <div v-for="item in list" :key="item.id" class="list-item">
            {{ item.content }}
        </div>
        <div v-if="loading" class="loading">正在加载...</div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            list: [],
            loading: false,
            hasMore: true
        };
    },
    methods: {
        async load() {
            if (this.loading) return; // 防止重复加载
            this.loading = true;
            try {
                const res = await fetchData();
                this.list.push(...res);
                this.hasMore = res.length > 0;
            } finally {
                this.loading = false;
            }
        }
    }
};
</script>

🔍 源码解析彩蛋

在 Element UI 的 getScrollParent 方法中:

js 复制代码
if (['scroll', 'auto'].includes(overflow) || ['scroll', 'auto'].includes(overflowY)) { 
return parent; 
}
  • 原理 :通过检查 overflow 属性判断滚动容器
  • 注意 :若父容器 overflow: visible,会退化到全局滚动(window
  • 解决方案 :强制设置容器 overflow: auto

方法三:React实战 -- react-infinite-scroll-component

React社区的 react-infinite-scroll-component 提供了更灵活的配置。本项目使用版本为:"react-infinite-scroll-component": "6.1.0"

🚀 核心代码

jsx 复制代码
import InfiniteScroll from 'react-infinite-scroll-component'
import { Empty } from 'antd'

const MyList = () => {
  const [list, setList] = useState([])
  const [hasMore, setHasMore] = useState(true)
  const loadingRef = useRef(false); // 标记是否在加载中

  
  const fetchData = async () => {
    if (loadingRef.current) return; // 如果正在加载,直接返回
    loadingRef.current = true;
    try{
      const res = await fetch('/api/data')
      const data = await res.json()
      setList((prev) => [...prev, ...data])
      setHasMore(data.length > 0)
    } finally {
      loadingRef.current = false; // 无论成功失败,重置状态
    }
   
  }
 
  return (
    <InfiniteScroll
      dataLength={list.length}
      next={fetchData}
      hasMore={hasMore}
      loader={<h4>正在加载...</h4>}
      endMessage={<p>没有更多数据啦~</p>}
      height={window.innerHeight - 100} // 自适应高度
    >
      {list?.length ? list.map((item) => (
        <div
          key={item.id}
          className="list-item"
        >
          {item.content}
        </div>
      )) : <Empty className="empty" />
      }
    </InfiniteScroll>
  )
}

📌相关api

  • dataLength: 已加载的数据条数,用于判断是否需要加载更多数据,这里用页码乘以每页条数计算
  • next: 当滚动到底部时会触发这个函数
  • hasMore: true表示还可以加载更多,false表示已经加载完所有数据
  • loader: 加载中时显示的内容
  • endMessage: 如果没有数据则显示"没有更多了",否则不显示任何内容
  • height: 固定高度可以创建一个独立的滚动区域, 赋值数字即可

💡 高阶技巧

  1. 动态高度 :通过 height={window.innerHeight - 100} 实现自适应
  2. 防抖优化 :配合 useRef 缓存请求,避免高频滚动时重复触发
  3. 错误处理 :添加 errorBoundary 组件处理加载异常

📌 总结对比表

方法 适用场景 优势 劣势
IntersectionObserver 现代浏览器项目 轻量、标准API 需手动处理边界条件
Element UI 企业级Vue项目 零配置快速集成 需注意容器样式限制
react-infinite-scroll React生态项目 配置灵活 需处理组件生命周期

选择方案时优先考虑:

  1. 浏览器兼容性要求
  2. 项目技术栈特性
  3. 数据量级(超大数据建议结合虚拟滚动)

🌟 本文代码已通过 Vue2/React18 测试,实战中记得添加 loading 状态和错误提示!

相关推荐
在无清风16 分钟前
Java实现Redis
前端·windows·bootstrap
_一条咸鱼_2 小时前
Vue 配置模块深度剖析(十一)
前端·javascript·面试
yechaoa2 小时前
Widget开发实践指南
android·前端
前端切图仔0013 小时前
WebSocket 技术详解
前端·网络·websocket·网络协议
JarvanMo3 小时前
关于Flutter架构的小小探讨
前端·flutter
前端开发张小七4 小时前
每日一练:4.有效的括号
前端·python
顾林海4 小时前
Flutter 图标和按钮组件
android·开发语言·前端·flutter·面试
雯0609~4 小时前
js:循环查询数组对象中的某一项的值是否为空
开发语言·前端·javascript
bingbingyihao4 小时前
个人博客系统
前端·javascript·vue.js
尘寰ya4 小时前
前端面试-HTML5与CSS3
前端·面试·css3·html5