vue3+vite+JS,使用Echarts封装一个饼图,父子组件联动

本文要在vue3+vite+JS项目中,使用Echarts封装一个饼图子组件,父组件中引入子组件并传入数据。传入的数据包括标题、高度、宽度、饼图数据,resize监听尺寸。子组件监听饼图数据的变化,来更新子组件的显示内容。

文章目录

一、实现思路

  1. 子组件封装:基于 ECharts 封装饼图组件,接收父组件传入的标题、宽高、饼图数据等 props;
  2. 响应式处理:监听饼图数据变化,自动更新图表;监听窗口 resize 事件,自适应调整图表尺寸;
  3. 父组件使用:导入子组件,传入自定义数据,实现灵活配置。

二、完整代码实现

1. 安装 ECharts 依赖
bash 复制代码
npm install echarts --save
# 或 yarn add echarts
2. 饼图子组件(PieChart.vue)
vue 复制代码
<template>
  <!-- 图表容器,宽高由父组件传入 -->
  <div 
    ref="chartRef" 
    class="pie-chart-container"
    :style="{ width: width + 'px', height: height + 'px' }"
  ></div>
</template>

<script setup>
import { ref, watch, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

// 定义接收的 props
const props = defineProps({
  // 饼图标题
  title: {
    type: String,
    default: '饼图示例'
  },
  // 图表宽度(px)
  width: {
    type: Number,
    default: 400
  },
  // 图表高度(px)
  height: {
    type: Number,
    default: 300
  },
  // 饼图数据(格式:[{ name: '分类1', value: 100 }, ...])
  chartData: {
    type: Array,
    required: true,
    validator: (val) => {
      // 简单校验数据格式
      return val.every(item => item.hasOwnProperty('name') && item.hasOwnProperty('value'))
    }
  }
})

// 图表实例引用
const chartRef = ref(null)
// ECharts 实例
let chartInstance = null

// 初始化图表
const initChart = () => {
  // 确保容器存在
  if (!chartRef.value) return
  
  // 初始化 ECharts 实例
  chartInstance = echarts.init(chartRef.value)
  
  // 设置图表配置项
  const option = {
    title: {
      text: props.title,
      left: 'center' // 标题居中
    },
    tooltip: {
      trigger: 'item', // 鼠标悬浮提示
      formatter: '{a} <br/>{b}: {c} ({d}%)' // 提示格式:名称:数值(百分比)
    },
    legend: {
      orient: 'vertical', // 图例垂直排列
      left: 'left' // 图例居左
    },
    series: [
      {
        name: props.title,
        type: 'pie',
        radius: ['40%', '70%'], // 饼图内外半径
        avoidLabelOverlap: false,
        label: {
          show: false,
          position: 'center'
        },
        emphasis: {
          label: {
            show: true,
            fontSize: 16,
            fontWeight: 'bold'
          }
        },
        labelLine: {
          show: false
        },
        data: props.chartData // 饼图数据
      }
    ]
  }
  
  // 渲染图表
  chartInstance.setOption(option)
}

// 更新图表(数据变化时调用)
const updateChart = () => {
  if (!chartInstance) return
  
  // 仅更新数据相关配置,避免重复初始化
  chartInstance.setOption({
    title: { text: props.title },
    series: [{ data: props.chartData }]
  })
}

// 监听窗口 resize,自适应调整图表尺寸
const handleResize = () => {
  if (chartInstance) {
    chartInstance.resize()
  }
}

// 生命周期:挂载时初始化图表 + 监听 resize
onMounted(() => {
  initChart()
  window.addEventListener('resize', handleResize)
})

// 生命周期:卸载时销毁实例 + 移除监听
onUnmounted(() => {
  if (chartInstance) {
    chartInstance.dispose() // 销毁 ECharts 实例,释放内存
    chartInstance = null
  }
  window.removeEventListener('resize', handleResize)
})

// 监听 props 变化,更新图表
watch(
  // 监听标题、宽高、数据的变化
  [() => props.title, () => props.width, () => props.height, () => props.chartData],
  () => {
    // 宽高变化时先销毁再重新初始化,数据/标题变化时仅更新配置
    if (props.width || props.height) {
      chartInstance?.dispose()
      initChart()
    } else {
      updateChart()
    }
  },
  {
    deep: true // 深度监听数组/对象变化
  }
)
</script>

<style scoped>
.pie-chart-container {
  /* 防止容器内边距影响尺寸 */
  box-sizing: border-box;
  /* 解决图表容器闪烁问题 */
  transition: all 0.3s ease;
}
</style>
3. 父组件(ParentPage.vue)
vue 复制代码
<template>
  <div class="parent-page">
    <h2>饼图组件使用示例</h2>
    
    <!-- 操作区:修改数据/尺寸 -->
    <div class="control-panel">
      <button @click="changeChartData">切换饼图数据</button>
      <button @click="changeChartSize">修改图表尺寸</button>
      <button @click="changeChartTitle">修改图表标题</button>
    </div>
    
    <!-- 引入饼图子组件,传入配置 -->
    <PieChart 
      :title="chartTitle"
      :width="chartWidth"
      :height="chartHeight"
      :chart-data="chartData"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
// 导入饼图子组件
import PieChart from './PieChart.vue'

// 定义饼图配置数据(响应式)
const chartTitle = ref('各部门人数分布')
const chartWidth = ref(500)
const chartHeight = ref(400)

// 初始饼图数据
const chartData = ref([
  { name: '技术部', value: 30 },
  { name: '产品部', value: 15 },
  { name: '市场部', value: 20 },
  { name: '财务部', value: 8 },
  { name: '人事部', value: 7 }
])

// 模拟:切换饼图数据
const changeChartData = () => {
  chartData.value = [
    { name: '技术部', value: 40 },
    { name: '产品部', value: 10 },
    { name: '市场部', value: 25 },
    { name: '财务部', value: 5 },
    { name: '人事部', value: 10 },
    { name: '运营部', value: 10 } // 新增分类
  ]
}

// 模拟:修改图表尺寸
const changeChartSize = () => {
  chartWidth.value = 600
  chartHeight.value = 450
}

// 模拟:修改图表标题
const changeChartTitle = () => {
  chartTitle.value = '各部门人数分布(更新后)'
}
</script>

<style scoped>
.parent-page {
  padding: 20px;
}

.control-panel {
  margin-bottom: 20px;
}

.control-panel button {
  margin-right: 10px;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  background: #409eff;
  color: #fff;
  cursor: pointer;
}

.control-panel button:hover {
  background: #66b1ff;
}
</style>

三、关键代码解释

  1. 子组件核心逻辑

    • props 定义 :明确接收标题、宽高、饼图数据,且对 chartData 做格式校验,确保数据合法性;
    • 图表初始化initChart 方法创建 ECharts 实例并配置基础样式,updateChart 方法仅更新数据/标题,避免重复初始化;
    • 响应式监听 :通过 watch 深度监听 props 变化,宽高变化时重新初始化图表,数据/标题变化时仅更新配置;
    • 生命周期管理onMounted 时初始化图表并添加 resize 监听,onUnmounted 时销毁 ECharts 实例、移除监听,防止内存泄漏;
    • resize 适配 :监听窗口尺寸变化,调用 chartInstance.resize() 自动调整图表大小。
  2. 父组件核心逻辑

    • 定义响应式数据(标题、宽高、饼图数据),传入子组件;
    • 提供按钮模拟数据/尺寸/标题变化,验证子组件的响应式更新能力。

四、使用注意事项

  1. 数据格式 :父组件传入的 chartData 必须是 [{ name: 'xxx', value: xxx }, ...] 格式,子组件已做基础校验;
  2. 内存泄漏 :子组件在 onUnmounted 中销毁 ECharts 实例、移除 resize 监听,避免页面卸载后残留事件;
  3. 自适应优化 :如果需要监听父容器尺寸变化(而非窗口),可结合 element-resize-detector 库实现;
  4. 样式适配 :子组件容器的宽高使用 px 单位,若需百分比,可修改 props 类型为 String(如 width: '100%')。

五、封装总结

  1. 子组件封装了 ECharts 饼图的核心逻辑,通过 props 接收父组件配置,具备数据响应式更新、窗口 resize 自适应能力;
  2. 父组件可灵活传入自定义数据,通过修改响应式数据即可触发子组件更新,使用简单;
  3. 关键优化点:销毁 ECharts 实例、移除事件监听,避免内存泄漏;深度监听数据变化,保证图表实时更新。
相关推荐
A黄俊辉A2 小时前
webstorm+vue+esLint+pretter配置
前端·vue.js·webstorm
一叶萩Charles2 小时前
MCP 实战:国家统计局数据查询 Server 从开发到发布
javascript·人工智能·python·node.js
孫治AllenSun2 小时前
【redis】redis重新创建集群
前端·javascript·redis
zhensherlock2 小时前
Protocol Launcher 系列:一键唤起 VSCodium 智能 IDE
javascript·ide·vscode·typescript·开源·编辑器·github
我命由我123452 小时前
React - ref、回调 ref 回调执行次数的问题、createRef 函数、事件处理
前端·javascript·react.js·前端框架·html·html5·js
我命由我123452 小时前
React - 收集表单元素、收集表单元素优化、生命周期(旧)、生命周期(新)
前端·javascript·react.js·前端框架·html·html5·js
SuperEugene2 小时前
Monorepo + pnpm workspace 落地实操:Vue 中后台多项目 / 组件库 / 公共包管理|Vue 工程化篇
前端·javascript·vue.js·pnpm·vite·monorepo
白中白121382 小时前
杂七杂八补充系列
开发语言·前端·javascript
Xingxing?!2 小时前
Vue2 微信小程序:页面间传递数组
前端·vue.js·uni-app