【VUE】el-table表格 实现滚动到底部加载更多数据

'# 【VUE】el-table表格 实现滚动到底部加载更多数据

一、背景与问题

在复杂业务场景中,表格组件常需要处理大量数据展示。传统分页虽然能控制数据量,但用户需要手动切换页码,交互体验不够友好。对于需要实时加载数据的场景(如聊天记录、日志监控等),滚动到底部自动加载数据是一种更自然的交互方式。

但实现这一功能时会遇到多个技术挑战:

  1. 如何准确检测表格容器的滚动位置
  2. 如何避免重复请求和过度请求
  3. 如何处理数据加载状态的展示
  4. 如何在数据量大的情况下保持性能
  5. 如何兼容不同浏览器和移动端

二、基本原理

该功能的核心原理是:通过监听表格容器的滚动事件,当滚动位置接近底部时触发数据加载。具体实现需要解决以下技术点:

  1. 滚动位置计算:需要获取表格容器的滚动高度、内容高度和视口高度,通过比较三者关系判断是否接近底部
  2. 防抖节流:防止因频繁滚动导致的重复请求
  3. 状态管理:需要记录当前页码、是否正在加载、是否已加载完毕等状态
  4. 数据合并:需要将新加载的数据与原有数据进行合并展示
  5. 性能优化:需要考虑虚拟滚动、数据分页等优化策略

三、环境准备

# 安装Element UI
npm install element-ui --save
# 创建Vue项目
vue create el-table-infinite-scroll
cd el-table-infinite-scroll

四、核心实现

1. 基础实现(window.scroll事件)

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div v-if="loading" class="loading">加载中...</div>
    <div v-if="hasMore" class="load-more">滚动到底部加载更多</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      
      if (scrollTop + windowHeight >= tableHeight - 100 && !this.loading && this.hasMore) {
        this.loadMore();
      }
    },
    async loadMore() {
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      // 模拟接口请求
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10 // 模拟最多10页
          });
        }, 500);
      });
    }
  }
};
</script>

<style scoped>
.table-container {
  height: 100vh;
  overflow: hidden;
}
.loading, .load-more {
  text-align: center;
  padding: 10px;
}
</style>

关键代码解释:

  • 使用window.scroll监听滚动事件
  • 通过计算scrollY和scrollHeight判断是否接近底部
  • 通过ref获取表格容器的高度
  • 使用防抖机制避免频繁触发请求

2. Intersection Observer实现(推荐方案)

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initObserver();
  },
  methods: {
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      if (this.loading || !this.hasMore) return;
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10
          });
        }, 500);
      });
    }
  }
};
</script>

<style scoped>
.table-container {
  height: 100vh;
  overflow: hidden;
}
.observer {
  height: 100px;
}
</style>

关键代码解释:

  • 使用Intersection Observer API监控虚拟的观察者元素
  • 设置threshold: 0.1表示当70%元素进入视口时触发
  • 通过ref获取观察者元素进行监控
  • 避免了直接监听窗口滚动带来的性能问题

3. 混合实现(结合滚动事件和观察者)

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
    this.initObserver();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      if (this.loading || !this.hasMore) return;
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      if (scrollTop + windowHeight >= tableHeight - 100) {
        this.loadMore();
      }
    },
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10
          });
        }, 500);
      });
    }
  }
};
</script>

<style scoped>
.table-container {
  height: 100vh;
  overflow: hidden;
}
.observer {
  height: 100px;
}
</style>

关键代码解释:

  • 结合两种实现方式,既监控窗口滚动又监控虚拟元素
  • 在移动端和桌面端都能保持良好性能
  • 通过threshold参数控制触发时机

五、完整案例

1. 案例需求

实现一个聊天消息列表组件,当用户滚动到底部时自动加载更多消息。要求:

  • 支持分页加载
  • 显示加载状态
  • 避免重复请求
  • 兼容移动端和桌面端

2. 项目结构

src/
├── components/
│   └── ChatList.vue
├── services/
│   └── chatService.js
└── App.vue

3. 完整代码实现

<!-- src/components/ChatList.vue -->
<template>
  <div class="chat-list-container">
    <el-table 
      ref="table"
      :data="chatMessages"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="timestamp" label="时间" width="180"></el-table-column>
      <el-table-column prop="sender" label="发送者" width="180"></el-table-column>
      <el-table-column prop="content" label="内容"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
import { getChatMessages } from '@/services/chatService';

export default {
  data() {
    return {
      chatMessages: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
    this.initObserver();
    this.loadMore();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      if (this.loading || !this.hasMore) return;
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      if (scrollTop + windowHeight >= tableHeight - 100) {
        this.loadMore();
      }
    },
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      if (this.loading || !this.hasMore) return;
      this.loading = true;
      const res = await getChatMessages(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.chatMessages = [...this.chatMessages, ...res.data];
      this.loading = false;
    }
  }
};
</script>

<style scoped>
.chat-list-container {
  height: 100vh;
  overflow: hidden;
}
.observer {
  height: 100px;
}
</style>
// src/services/chatService.js
export async function getChatMessages(page) {
  return new Promise((resolve) => {
    setTimeout(() => {
      const messages = Array.from({ length: 20 }).map((_, i) => ({
        id: Date.now() + i,
        timestamp: new Date().toISOString(),
        sender: `User${Math.floor(Math.random() * 10) + 1}`,
        content: `这是第${this.chatMessages.length + i + 1}条消息`
      }));
      resolve({
        data: messages,
        currentPage: page,
        hasMore: page < 5
      });
    }, 500);
  });
}

4. 案例说明

该案例模拟了一个聊天消息列表,当用户滚动到底部时会自动加载更多消息。通过结合窗口滚动事件和Intersection Observer,实现了跨平台兼容性。在真实项目中可以替换为实际的API接口。

六、源码解析

1. 滚动事件监听

window.addEventListener('scroll', this.handleScroll);
  • 使用全局事件监听
  • 需要主动移除事件监听防止内存泄漏
  • 适用于简单场景

2. Intersection Observer

const observer = new IntersectionObserver(
  (entries) => {
    if (entries[0].isIntersecting) {
      this.loadMore();
    }
  },
  {
    threshold: 0.1
  }
);
  • 创建观察器实例
  • 设置threshold参数控制触发时机
  • 可以观察多个元素
  • 适用于需要精确控制触发时机的场景

3. 数据加载逻辑

async loadMore() {
  if (this.loading || !this.hasMore) return;
  this.loading = true;
  const res = await this.fetchData(this.currentPage + 1);
  this.currentPage = res.currentPage;
  this.hasMore = res.hasMore;
  this.tableData = [...this.tableData, ...res.data];
  this.loading = false;
}
  • 使用async/await处理异步请求
  • 通过hasMore控制是否继续加载
  • 使用currentPage记录当前页码
  • 使用loading状态防止重复请求

七、进阶使用

1. 懒加载实现

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
    this.initObserver();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      if (this.loading || !this.hasMore) return;
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      if (scrollTop + windowHeight >= tableHeight - 100) {
        this.loadMore();
      }
    },
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10
          });
        }, 500);
      });
    }
  }
};
</script>

2. 虚拟滚动优化

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
    this.initObserver();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      if (this.loading || !this.hasMore) return;
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      if (scrollTop + windowHeight >= tableHeight - 100) {
        this.loadMore();
      }
    },
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10
          });
        }, 500);
      });
    }
  }
};
</script>

3. 混合实现方案

<template>
  <div class="table-container">
    <el-table 
      ref="table"
      :data="tableData"
      border
      style="width: 100%"
      :height="tableHeight"
    >
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column prop="address" label="地址"></el-table-column>
    </el-table>
    <div ref="observer" class="observer"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [],
      currentPage: 1,
      pageSize: 20,
      loading: false,
      hasMore: true,
      tableHeight: 400
    };
  },
  mounted() {
    this.initScrollEvent();
    this.initObserver();
  },
  methods: {
    initScrollEvent() {
      window.addEventListener('scroll', this.handleScroll);
    },
    handleScroll() {
      if (this.loading || !this.hasMore) return;
      const scrollTop = window.scrollY;
      const windowHeight = window.innerHeight;
      const tableHeight = this.$refs.table.$el.scrollHeight;
      if (scrollTop + windowHeight >= tableHeight - 100) {
        this.loadMore();
      }
    },
    initObserver() {
      const observer = new IntersectionObserver(
        (entries) => {
          if (entries[0].isIntersecting) {
            this.loadMore();
          }
        },
        {
          threshold: 0.1
        }
      );
      observer.observe(this.$refs.observer[0]);
    },
    async loadMore() {
      this.loading = true;
      const res = await this.fetchData(this.currentPage + 1);
      this.currentPage = res.currentPage;
      this.hasMore = res.hasMore;
      this.tableData = [...this.tableData, ...res.data];
      this.loading = false;
    },
    async fetchData(page) {
      return new Promise(resolve => {
        setTimeout(() => {
          const data = Array.from({ length: this.pageSize }).map((_, i) => ({
            id: this.tableData.length + i + 1,
            date: new Date().toISOString(),
            name: `用户${this.tableData.length + i + 1}`,
            address: `地址${this.tableData.length + i + 1}`
          }));
          resolve({
            data,
            currentPage: page,
            hasMore: page < 10
          });
        }, 500);
      });
    }
  }
};
</script>

八、性能与工程实践

1. 性能优化策略

优化策略说明适用场景
节流处理使用requestAnimationFrame控制刷新频率高频滚动场景
防抖处理使用setTimeout设置延迟短时频繁触发场景
虚拟滚动只渲染可见区域数据数据量极大时
数据分页避免一次性加载过多数据网络传输优化
响应式更新使用Vue的响应式系统更新数据避免直接操作DOM

2. 数据结构优化

// 响应式数据结构
data() {
  return {
    tableData: [], // 当前显示的数据
    currentPage: 1, // 当前页码
    pageSize: 20, // 每页数据量
    loading: false, // 加载状态
    hasMore: true, // 是否还有更多数据
    tableHeight: 400 // 表格高度
  };
}

3. 异常处理

async loadMore() {
  this.loading = true;
  try {
    const res = await this.fetchData(this.currentPage + 1);
    this.currentPage = res.currentPage;
    this.hasMore = res.hasMore;
    this.tableData = [...this.tableData, ...res.data];
  } catch (error) {
    console.error('加载数据失败:', error);
    this.loading = false;
  } finally {
    this.loading = false;
  }
}

4. 安全考虑

  • 接口防滥用:设置请求频率限制
  • 防止重复请求:使用loading状态锁
  • 数据校验:确保返回数据格式正确
  • 前端分页:避免越权访问后端接口

九、常见问题与踩坑

1. 滚动事件触发频繁

错误代码:

window.addEventListener('scroll', this.handleScroll);

错误原因:频繁触发导致性能问题

解决办法:

function debounce(func, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => func.apply(this, args), delay);
  };
}

initScrollEvent() {
  window.addEventListener('scroll', debounce(this.handleScroll, 200));
}

2. 数据加载后表格高度不更新

错误代码:

this.tableData = [...this.tableData, ...res.data];

错误原因:未更新表格高度导致滚动位置异常

解决办法:

this.tableHeight = this.$refs.table.$el.scrollHeight;

3. 移动端兼容性问题

错误现象:在移动端无法触发加载

解决办法:

  • 使用Intersection Observer替代window.scroll
  • 使用@touchstart和@touchmove处理移动端滚动
  • 设置overflow: auto确保滚动行为

4. 数据重复加载

错误代码:

this.tableData = [...this.tableData, ...res.data];

错误原因:未正确处理数据合并

解决办法:

this.tableData = [...this.tableData, ...res.data];

5. 加载状态未正确更新

错误代码:

this.loading = false;

错误原因:未在finally块中更新状态

解决办法:

async loadMore() {
  this.loading = true;
  try {
    const res = await this.fetchData(this.currentPage + 1);
    this.currentPage = res.currentPage;
    this.hasMore = res.hasMore;
    this.tableData = [...this.tableData, ...res.data];
  } catch (error) {
    console.error('加载数据失败:', error);
  } finally {
    this.loading = false;
  }
}

十、最佳实践

1. 推荐方案

场景推荐方案说明
基础需求Intersection Observer精确控制触发时机
高频滚动节流+Intersection Observer平衡性能和准确性
大数据量虚拟滚动+分页优化内存和性能
简单场景window.scroll实现简单,但性能较差

2. 使用建议

  • 使用Intersection Observer替代window.scroll,性能更好
  • 使用debounce处理高频事件
  • 在数据加载后更新表格高度
  • 设置合理的threshold参数
  • 添加加载状态提示
  • 使用loading状态防止重复请求

3. 注意事项

  • 避免直接操作DOM,使用Vue响应式系统
  • 在mounted和beforeUnmount中管理事件监听
  • 处理网络错误和异常
  • 设置合理的分页参数
  • 避免过度使用该技术

十一、总结

通过本文的深入探讨,我们了解到el-table实现滚动到底部加载更多数据的核心原理和多种实现方式。在实际开发中,需要根据具体场景选择合适的方案,既要考虑性能优化,又要保证良好的用户体验。在实现过程中需要注意避免重复请求、正确更新状态、处理异常情况等关键点。

推荐在需要实时加载数据、数据量较大、希望提供自然交互体验的场景中使用该技术。但在数据量较小、需要精确控制分页、或对性能要求不高的场景中,应该谨慎使用。

通过合理使用Intersection Observer、节流处理、响应式数据更新等技术,可以构建一个既高效又稳定的滚动加载方案。同时,注意处理常见的错误和边界情况,确保在各种设备和浏览器上都能正常工作。

VUE
最后修改于:2026年10月06日 13:28

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日