uni-app小程序分包中使用 Echarts, 并在分包里加载依赖

5,415 阅读4分钟

这篇笔记主要记录uni-app小程序, 在分包中使用Echarts,并在分包里加载Echarts依赖,减少主包的大小,提升小程序的加载速度.

  1. 在分包中使用Echarts
  2. 在分包里加载Echarts依赖

先看下效果,图表正常渲染,主包大小小于1.5M,主包存在仅被其他分包依赖的JS文件也通过✅

image.png

在分包中使用Echarts

我们要用的Echarts插件是lime-chart, 我们看下文档, 插件下载页面下面的描述

image.png

我们是Vue3小程序,先下载插件,下载好插件后,插件会安装在项目根目录下的uni_modules文件夹,下载插件截图 image.png

根据文档,我们require相对引入echarts.min文件,我们要渲染的图表数据来自接口

  1. 在onMounted里请求接口数据
  2. 接口数据返回后调用渲染图表的方法
  3. 渲染图表要用setTimeout,确保渲染图表时,组件的节点已经被渲染到页面上
<script setup>
import { onMounted } from 'vue'
// 根据项目目录相对引入
const echarts = require("../../uni_modules/lime-echart/static/echarts.min.js");

const getData = async () => {
   const chartData = await getChartData() // 获取图表数据
   setTimeout(() => {
     renderChart() // 数据返回后渲染图表
   }, 500)
}

onMounted(() => {
  getData()
})
</script>

这样就能渲染了 image.png

刚开始我没渲染出来,对比文档,发现我没用setTimeout,用了之后就渲染出来了,看起来一切正常,但是,我们发布的时候,提示

image.png

主包超过1.5M, 主包有只被其他子包依赖的JS文件,都没通过,并且还告诉我们是uni_modules/lime-chart/static/echarts.min.js这个文件

虽然我们是在分包里渲染Echarts,但是插件默认下载到主包的uni_modules,我们需要把Echarts依赖引入到分包里

在分包里加载Echarts依赖

我们把主包里的Echarts文件整个移入到分包里pages-me-dashboard, 我在分包里建了一个文件夹uni_modules, 告诉自己这是一个插件,

image.png

可是却发现Echarts渲染不出来了,调试后发现chart组件不渲染了

<l-echart ref="pieChartRef"></l-echart>

于是就又手动引入lEchart组件,Echart在主包的时候,没引入lEchart组件就渲染了,发现可以正常渲染

import lEchart from "../uni_modules/lime-echart/components/l-echart/l-echart.vue";

再次发布下,主包大小通过,主包存在仅被其他分包依赖的JS文件也通过✅

image.png

完整代码

<template>
  <view class="container">
    <view class="stats-card">
      <view class="header">
        <view class="date-select">
          <picker
            mode="selector"
            :value="selectedYearIndex"
            :range="yearOptions"
            @change="onYearChange">
            <view class="picker">{{ yearOptions[selectedYearIndex] }} 年</view>
          </picker>

          <picker
            mode="selector"
            :value="selectedMonthIndex"
            :range="monthOptions"
            @change="onMonthChange">
            <view class="picker">{{ monthOptions[selectedMonthIndex] }}</view>
          </picker>
        </view>
      </view>

      <view v-if="loading" class="loading">
        <uni-load-more status="loading"></uni-load-more>
      </view>

      <view v-else class="stats-content">
        <view class="stat-item">
          <text class="label">总课程节数</text>
          <text class="value">
            {{ statistics.totalCourses }}
            <text class="label">节</text>
          </text>
        </view>
        <view class="stat-item mb-20">
          <text class="label">出勤统计</text>
          <text class="value">
            {{ statistics.trainingDays }}
            <text class="label">天</text>
          </text>
        </view>

        <!-- Line Chart -->
        <view style="width: 90vw; height: 750rpx">
          <l-echart ref="lineChartRef"></l-echart>
        </view>

        <!-- Pie Chart -->
        <view
          style="
            width: 85vw;
            height: 550rpx;
            margin-top: 20px;
            overflow: hidden;
          ">
          <l-echart ref="pieChartRef"></l-echart>
        </view>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { ref, onMounted, nextTick } from "vue";
// Import echarts
import lEchart from "../uni_modules/lime-echart/components/l-echart/l-echart.vue";
const echarts = require("../uni_modules/lime-echart/static/echarts.min");

interface Statistics {
  totalCourses: number;
  trainingDays: number;
  courseDistribution: { name: string; value: number }[];
  dailyCourses: { date: string; count: number }[];
}

const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentMonth = currentDate.getMonth();

const yearOptions = Array.from({ length: 5 }, (_, i) => `${currentYear - i}`);
const monthOptions = Array.from({ length: 12 }, (_, i) => `${i + 1} 月`);

const selectedYearIndex = ref(0);
const selectedMonthIndex = ref(currentMonth);

const statistics = ref<Statistics>({
  totalCourses: 0,
  trainingDays: 0,
  courseDistribution: [],
  dailyCourses: [],
});
const loading = ref(false);

const onYearChange = (e: any) => {
  selectedYearIndex.value = e.detail.value;
  fetchStatistics();
};

const onMonthChange = (e: any) => {
  selectedMonthIndex.value = e.detail.value;
  fetchStatistics();
};

const fetchStatistics = async () => {
  loading.value = true;

  try {
    console.log(
      "year-month",
      yearOptions[selectedYearIndex.value],
      Number(selectedMonthIndex.value) + 1
    );
    const res = await uniCloud.callFunction({
      name: "getMonthlyStatistics",
      data: {
        userId: uni.getStorageSync("userInfo").userId,
        year: yearOptions[selectedYearIndex.value],
        month: Number(selectedMonthIndex.value) + 1,
      },
    });

    if (res.result.code === 0) {
      statistics.value = res.result.data;
      console.log("charts-----", res.result.data);
      renderCharts();
    }
  } catch (error) {
    console.error("获取统计数据失败", error);
  } finally {
    loading.value = false;
  }
};

const lineChartRef = ref(null);
const pieChartRef = ref(null);

const renderCharts = async () => {
  // Line Chart
  setTimeout(async () => {
    console.log("charts111-----", echarts, lineChartRef.value);
    if (!lineChartRef.value) return;

    const dailyCourses = statistics.value.dailyCourses;
    const dates = dailyCourses.map((item) => item.date);
    const counts = dailyCourses.map((item) => item.count);

    const lineChartOption = {
      title: {
        text: "每日上课统计",
        left: "left",
        top: 10,
        textStyle: {
          fontSize: 18,
          fontWeight: "bold",
          color: "#333",
        },
      },
      tooltip: {
        trigger: "axis",
        axisPointer: {
          type: "line",
        },
      },
      xAxis: {
        type: "category",
        data: dates,
        axisLine: {
          lineStyle: {
            color: "#999999",
          },
        },
        axisLabel: {
          color: "#666666",
        },
        axisTick: {
          show: false,
        },
      },
      yAxis: {
        type: "value",
        axisLine: {
          lineStyle: {
            color: "#999999",
          },
        },
        axisLabel: {
          color: "#666666",
        },
        axisTick: {
          show: false,
        },
      },
      series: [
        {
          name: "课程节数",
          type: "line",
          data: counts,
          smooth: true,
        },
      ],
    };
    console.log("echarts", echarts);
    const lineChart = await lineChartRef.value.init(echarts);
    lineChart.setOption(lineChartOption);
  }, 500);

  // Pie Chart
  setTimeout(async () => {
    if (!pieChartRef.value) return;

    const courseDistribution = statistics.value.courseDistribution;

    const pieChartOption = {
      title: {
        text: "课程分布",
        left: "left",
        top: 10,
        textStyle: {
          fontSize: 18,
          fontWeight: "bold",
          color: "#333",
        },
      },
      tooltip: {
        trigger: "item",
        formatter: "{b}: {c} ({d}%)",
      },
      series: [
        {
          name: "课程分布",
          type: "pie",
          radius: ["30%", "50%"],
          label: {
            show: true,
            position: "outside",
            formatter: "{b}: {c} ({d}%)",
          },
          data: courseDistribution.map((item) => ({
            name: item.name,
            value: item.value,
          })),
        },
      ],
    };
    const pieChart = await pieChartRef.value.init(echarts);
    pieChart.setOption(pieChartOption);
  }, 600);
};

onMounted(() => {
  fetchStatistics();
});
</script>

<style lang="scss" scoped>
.container {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  // height: 100vh;
  background-color: #f5f5f5;
  padding: 20px;
  box-sizing: border-box;
}

.stats-card {
  width: 100%;
  max-width: 650px;
  background-color: #fff;
  border-radius: 10px;
  padding: 20px;
  box-sizing: border-box;
}

.header {
  display: flex;
  justify-content: flex-start;
  margin-bottom: 20px;
}

.date-select {
  display: flex;
  gap: 15px;
}

.picker {
  padding: 8px 20px;
  background-color: #f0f0f0;
  border-radius: 8px;
  font-size: 16px;
  text-align: center;
}

.stats-content {
  display: flex;
  flex-direction: column;
  // gap: 20px;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0; // 减小 padding 让行距更紧凑
  border-bottom: 1px solid #eee;

  .label {
    font-size: 14px; // 标签字体调小
    color: #666;
  }

  .value {
    font-size: 22px; // 数值字体加大
    font-weight: bold; // 加粗数值
    color: #333;
  }
}

.chart {
  height: 300px;
  margin-top: 20px;
}
</style>

效果图页面渲染的数据

{
  "totalCourses": 5,
  "trainingDays": 3,
  "dailyCourses": [
    {
      "date": "2025-01-01",
      "count": 2
    },
    {
      "date": "2025-01-02",
      "count": 2
    },
    {
      "date": "2025-01-03",
      "count": 1
    }
  ],
  "courseDistribution": [
    {
      "name": "编舞基础",
      "value": 2
    },
    {
      "name": "Kpop基础",
      "value": 1
    },
    {
      "name": "Hiphop基础",
      "value": 1
    },
    {
      "name": "Jazz进阶",
      "value": 1
    }
  ]
}