element-plus的el-tabs组件内容太多,加载太慢的解决方案

文章介绍了如何仿写ElementPlus的el-tabs组件,通过将tab和内容分离,以提高页面渲染速度,特别针对内容过多导致加载缓慢的问题。作者展示了如何在components文件夹下创建tabs组件,并提供了详细的代码实现和使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仿写element-plus的el-tabs组件,主要为了将tab和内容分开,提高渲染速度

如果tab下的内容太多会导致页面加载异常缓慢

效果和官网一致
在这里插入图片描述

在components文件下新建tabs文件,tabs中新建index.vue

index.vue文件
<template>
  <el-card shadow="Never" body-class="cardCss-table" class="mt-8">
    <div class="tabs__nav-wrap">
      <div class="tabs__nav-scroll">
        <div class="tabs__nav">
          <div class="tabs_active-bar" :style="highlightStyle"></div>
          <div
            v-for="(item, index) in props.tabList"
            :key="item.id"
            @click="changeTabs(item, index)"
            class="c-p tabs__item"
            ref="tabItem"
            :class="{
              p1: index === 0,
              p2: index > 0,
              p3: index === tabList.length - 1 && tabList.length > 1,
              'active-tab': currentId == item.id,
            }"
          >
            <span> {{ item.label }} </span>
            <span v-if="item.num"> {{ "(" + item.num + ")" }}</span>
          </div>
        </div>
      </div>
    </div>
  </el-card>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, nextTick } from "vue";
const props = defineProps({
  tabList: {
    type: Array as any,
    default: () => {
      return [];
    },
  },
});
// props.tabList

const emits = defineEmits(["changeTab"]);
const highlightStyle = ref<any>({
  width: "0px", // 初始化宽度
  transform: "translateX(0px)", // 默认位置
});
const currentId = ref<any>(null);// 当前的高亮id
const tabItem = ref<any>(null); // tabs的ref数组
const changeTabs = (item: any, val: number) => {
  currentId.value = item.id;
  if (val !== 0) {
    let percentage = 0;
    for (let i = 0; i < val; i++) {
      percentage += leftWidth.value[i].leftwidth;
    }
    highlightStyle.value.transform = `translateX(${percentage + 20}px)`;
    highlightStyle.value.width = leftWidth.value[val].width + "px";
  } else {
    highlightStyle.value.transform = `translateX(0px)`;
    highlightStyle.value.width = leftWidth.value[0].width + "px";
  }

  emits("changeTab", item.id);
};

const leftWidth = ref<any[]>([]);// 将每个tabitem长度存储到这个数组中
const calculateWidth = () => {
  const tabItems = tabItem.value;
  tabItems.forEach((item, index) => {
    // console.log(item.offsetWidth, index);
    const offsetWidth = item.offsetWidth;
    let baseWidth = index == 0 || index == tabItems.length - 1 ? offsetWidth - 20 : offsetWidth - 40;
    const width = baseWidth;
    leftWidth.value.push({ width: Number(width), leftwidth: Number(offsetWidth) });
  });
};
onMounted(() => {
// 必须使用setTimeout
  setTimeout(() => {
    currentId.value = props.tabList[0].id; // 默认选中第一个
    calculateWidth(); // 计算tab宽度
    highlightStyle.value.width = leftWidth.value[0].width + "px"; // 设置默认宽度
  }, 200);
});
</script>
<style lang="scss" scoped>
.tabs__nav-wrap {
  height: 40px;
  margin-bottom: -1px;
  overflow: hidden;
  position: relative;
  font-size: 14px;
  color: #303133;
}
.tabs__nav-wrap::after {
  background-color: #e4e7ed;
  bottom: 0;
  content: "";
  height: 2px;
  left: 0;
  position: absolute;
  width: 100%;
  z-index: 1;
}
.tabs__nav-scroll {
  overflow: hidden;
}
.tabs__nav {
  display: flex;
  float: left;
  position: relative;
  transition: transform 0.3s;
  white-space: nowrap;
  z-index: 2;
}
.tabs__item {
  align-items: center;
  box-sizing: border-box;
  display: flex;
  font-weight: 500;
  height: 40px;
  justify-content: center;
  list-style: none;
  position: relative;
}
.p1 {
  padding: 0 20px 0 0;
}
.p2 {
  padding: 0 20px;
}
.p3 {
  padding: 0 0 0 20px;
}

.tabs_active-bar {
  background-color: #409eff;
  bottom: 0;
  height: 2px;
  left: 0;
  list-style: none;
  position: absolute;
  transition: width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  z-index: 1;
}
.active-tab {
  color: #409eff;
}
</style>

在使用此组件的.vue文件中使用Tabs组件

<template>
  <div>
    <!-- Tabs组件-->
    <Tabs :tabList="ebTabsList" @changeTab="changeTab" />
    ...其他内容
  </div>
</template>
<script setup lang="ts">
import Tabs from "@/components/tabs/index.vue";
interface Tabs {
  label: string;
  id: string;
}
const tabData = ref<Tabs[]>([
  { label: "待下单", id: "1" },
  { label: "异常订单", id: "2" },
]);
</script>

适用于使用el-tabs组件但是内容太多的场景,如果想要包裹到一起再在外层包裹el-card,ui样式基本和element-plus的一样

### 实现 `el-tabs` 组件固定在页面顶部的解决方案 为了使 `el-tabs` 固定在页面顶部,可以通过 CSS 的定位属性来实现。以下是具体的实现方法: #### HTML 结构 ```html <template> <div class="app-container"> <!-- 使用 el-affix 或者纯 CSS 定位 --> <el-affix :offset-top="0"> <el-tabs v-model="activeName" @tab-click="handleClick" stretch> <el-tab-pane label="示例1" name="first">内容一</el-tab-pane> <el-tab-pane label="示例2" name="second">内容二</el-tab-pane> </el-tabs> </el-affix> <div class="content-area"> 这里是主要内容区域... </div> </div> </template> ``` #### 样式部分 如果不想使用 `el-affix`,可以手动通过 CSS 来设置固定的头部效果。 ```css <style scoped> .app-container { position: relative; } /* 设置 el-tabs 固定到顶部 */ .el-tabs { position: fixed; top: 0; left: 0; right: 0; z-index: 1000; /* 确保覆盖其他内容 */ background-color: white; /* 背景颜色防止透明 */ border-bottom: none; /* 如果需要移除下划线可加上此行 */ } .content-area { margin-top: 60px; /* 根据 el-tabs 的高度调整间距 */ } </style> ``` #### Vue 方法逻辑 ```javascript <script> export default { data() { return { activeName: 'first' // 默认选中第一个 Tab }; }, methods: { handleClick(tab) { console.log('当前点击的 Tab:', tab.name); } } }; </script> ``` --- ### 关键点解析 1. **使用 `el-affix` 插件** - Element Plus 提供了一个内置组件 `el-affix`[^5],它可以轻松实现吸顶效果。 - 只需包裹住 `el-tabs` 并指定偏移量即可完成固定功能。 2. **CSS 手动方式** - 利用 `position: fixed` 将 `el-tabs` 锁定在页面顶部。 - 同时需要注意给主体内容留出足够的空间(如 `margin-top`),以免被遮挡。 3. **优化用户体验** - 添加背景色以避免滚动时内容穿透。 - 移除不必要的边框或线条,提升视觉体验[^3]。 --- ### 注意事项 - 若页面存在动态加载内容,则可能需要重新计算 `content-area` 的外边距。 - 对于响应式设计,建议测试不同屏幕尺寸下的表现并适当调整样式。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值