element-plus
安装依赖(确保版本适配 Vue 3)
npm install element-plus --save
plugins/element.js 实现按需引入组件
javascript
import { ElButton } from 'element-plus' // 导入 Element Plus 的 Button(Vue 3 版本)
import 'element-plus/dist/index.css' //全局引入样式,避免找不到
// 接收 main.js 传入的 app 实例
export default function setupElement(app) {
// 注册 Button 组件(Vue 3 用 app.component 注册单个组件)
app.component('ElButton', ElButton)
}
main.js
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import setupElement from './plugins/element.js'
//创建应用实例
const app = createApp(App)
//注册 Element Plus 插件
setupElement(app)
app.use(router)
//挂载应用
app.mount('#app')
App.vue
xml
<template>
<div id="app">
<el-button type="danger">Danger</el-button>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
html,body,#app{
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
</style>
代码是从上往下一行一行执行的
-
执行
main.js中引入代码 -
import { createApp } from 'vue'→ 从 Vue 库导入createApp方法; -
import App from './App.vue'→ 导入根组件App.vue(仅加载,未渲染); -
import router from './router'→ 导入路由(如果有配置); -
当执行到import setupElement from './plugins/element.js'会立即进入
element.js执行里面的代码 ,但注意:element.js中只有「导入组件 + 定义函数」的逻辑会执行,app.component注册组件的逻辑要等main.js中调用setupElement(app)时才执行。
从 Element Plus 加载按钮组件的代码;
执行 import 'element-plus/dist/index.css' → 加载 Element Plus 的全局样式(CSS 生效);
执行 export default function setupElement(app) {...} → 定义 setupElement 函数(仅定义,不执行函数内部代码);
执行完 element.js 后,将 setupElement 函数作为返回值,赋值给 main.js 中的 setupElement 变量
-
执行
main.js中的实例创建和注册逻辑 -
const app = createApp(App)→ 创建 Vue 应用实例(此时还未挂载组件); -
setupElement(app)→ 调用element.js中定义的函数:- 将 Vue 实例
app传入函数,执行app.component('ElButton', ElButton)→ 全局注册ElButton组件;
- 将 Vue 实例
-
app.use(router)→ 注册路由(如果有); -
app.mount('#app')→ 将 Vue 实例挂载到页面的#app元素上:- 渲染
App.vue组件; - 解析
App.vue中的<el-button>标签 → 匹配到全局注册的ElButton组件 → 渲染出带样式的危险按钮。
- 渲染
javascript
import setupElement from './plugins/element.js'
//创建应用实例
const app = createApp(App)
//注册 Element Plus 插件
setupElement(app)
相当于这里要引用setupElement这个方法,创建实例时,调用setupElement这个方法,并将创建的实例当成参数传入,实现vue组件的注册。
简单说:setupElement 是一个 "组件注册工具函数",调用它并传入 Vue 实例 app,本质就是把 ElButton 组件挂载到 Vue 应用实例上 ,这样整个项目的所有组件(比如 App.vue)都能使用 <el-button> 标签。
element.js
javascript
import { ElCard,ElCol,ElRow } from 'element-plus'
import 'element-plus/dist/index.css'
export default function setupElement(app) {
app.component('ElCard', ElCard)
app.component('ElCol', ElCol)
app.component('ElRow', ElRow)
}
进行优化
javascript
import { ElCard,ElCol,ElRow } from 'element-plus'
import 'element-plus/dist/index.css'
export default function setupElement(app) {
//批量注册
const components = { ElCard, ElCol, ElRow }
Object.entries(components).forEach(([name, component]) => {
app.component(name, component)
})
}
将需要注册的组件放入对象中,这里是简写语法。 const components = { ElCard, ElCol, ElRow }
const components = { ElCard: ElCard, ElCol: ElCol, ElRow: ElRow }
Object.entries(components)把对象转成「键值对数组」:转换成「二维数组」,每个子数组包含「键、值」
css
[ ['ElCard', ElCard],
['ElCol', ElCol],
['ElRow', ElRow]
]
.forEach(...) → 遍历这个二维数组
([name, component]):ES6 数组解构,把遍历到的子数组 ['ElCard', ElCard] 拆成两个变量:
name='ElCard'(组件注册名);component=ElCard(组件对象)
app.component(name, component):调用 Vue 3 的组件注册方法,等价于 app.component('ElCard', ElCard)
echarts
安装依赖
ESLint 相关的配置包版本太旧,和新版的 Vue ESLint 插件不兼容,连带导致 echarts 安装失败.直接在安装命令后加 --legacy-peer-deps,强制忽略版本冲突,快速安装 echarts
css
npm i -S echarts --legacy-peer-dep
正常安装
css
npm i -S echarts
彻底解决依赖冲突
如果想从根本上修复版本冲突,执行以下步骤(不影响已安装的 Element Plus):
步骤 1:卸载旧的 ESLint 配置包
bash
运行
lua
npm uninstall @vue/eslint-config-standard --save-dev
步骤 2:安装兼容新版 eslint-plugin-vue@8.x 的配置包
bash
运行
sql
npm install @vue/eslint-config-standard@latest --save-dev --legacy-peer-deps
步骤 3:重新安装 echarts
bash
运行
css
npm i -S echarts
main.js
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import setupElement from './plugins/element.js'
import * as echarts from 'echarts'
const app = createApp(App)
setupElement(app)
app.config.globalProperties.$echarts = echarts
app.use(router)
app.mount('#app')
Vue 3 中移除 Vue.prototype,改用 app.config.globalProperties 的本质是:Vue 3 不再基于构造函数原型链扩展,而是基于应用实例的全局配置来挂载全局属性 / 方法 ,更贴合 Vue 3 的「实例化」设计(每个 createApp() 都是独立的应用实例)。app.config.globalProperties 挂载到这个对象上的属性 / 方法,会被注入到所有组件的「选项式 API」上下文(即 this)中,效果和 Vue 2 的 Vue.prototype 完全一致,但作用域仅限当前应用实例。
arduino
// 创建应用实例
const app = createApp(App)
// 挂载全局属性/方法
app.config.globalProperties.自定义属性名 = 要挂载的内容
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 核心载体 | 全局构造函数 Vue(所有组件共享同一个原型链) |
应用实例 app(createApp() 创建,多实例隔离) |
| 全局挂载 | Vue.prototype.$xxx = xxx(挂载到构造函数原型,全局共享) |
app.config.globalProperties.$xxx = xxx(挂载到单个应用实例,实例隔离) |
-
多实例隔离 :Vue 3 支持一个页面创建多个独立的 Vue 应用实例(比如
app1 = createApp(),app2 = createApp()),如果用原型链,会导致多个实例的全局属性互相污染;而globalProperties是绑定到单个app实例的,不同实例的全局属性互不影响。 -
更符合模块化 :Vue 3 推崇「模块化」「按需使用」,原型链扩展是侵入式的(修改全局构造函数),而
globalProperties是配置式的(仅修改当前应用实例),更灵活。
import * as echarts from 'echarts'
import * as echarts from 'echarts' 的作用是:把 echarts 库中所有导出的内容,整体导入并挂载到 echarts 这个变量上 。这么写的根本原因是:echarts v5+ 采用「命名导出」(Named Export),而非「默认导出」(Default Export),所以不能用 import echarts from 'echarts' 这种默认导入方式。
| 导出方式 | 语法示例(库作者写的代码) | 导入方式(你写的代码) | 适用场景 |
|---|---|---|---|
| 默认导出(Default Export) | export default { init: () => {} } |
import echarts from 'echarts' |
库只有一个核心导出(比如 Vue、React) |
| 命名导出(Named Export) | export const init = () => {}``export const dispose = () => {} |
import * as echarts from 'echarts'或 import { init } from 'echarts' |
库有多个独立导出(比如 echarts、lodash) |
echarts 源码的核心导出逻辑类似这样(简化版):
javascript
// echarts 源码中的导出逻辑(模拟)
export const init = (dom) => { /* 初始化图表 */ }
export const dispose = (instance) => { /* 销毁图表 */ }
export const registerMap = (name, data) => { /* 注册地图 */ }
// ... 还有上百个命名导出的方法/对象
:echarts 没有写 export default,只有一堆 export const/function 的「命名导出」------ 这就是为什么你用 import echarts from 'echarts' 会报错(找不到默认导出)
import * as echarts from 'echarts' 语法拆解
| 语法片段 | 含义 |
|---|---|
import * |
导入目标模块中所有 命名导出的内容(init/dispose/registerMap 等) |
as echarts |
把这些导入的内容,统一挂载到一个名为 echarts 的命名空间对象上 |
from 'echarts' |
从 echarts 这个模块导入 |
相当于给 echarts 库的所有导出内容做了一个 "收纳箱",箱子名字叫 echarts:
- 原本分散的
init方法 → 现在是echarts.init; - 原本分散的
dispose方法 → 现在是echarts.dispose; - 所有 echarts 的功能,都可以通过
echarts.xxx访问,既整洁又不会污染全局变量。
如果想只导入部分方法,也可以写:
csharp
// 只导入 init 方法(命名导出的精准导入)
import { init } from 'echarts'
// 使用时直接写 init(),而非 echarts.init()
const chart = init(document.getElementById('chart'))
echarts 极致按需导入
javascript
// 极致按需导入(推荐生产环境用)
import { init } from 'echarts/core'
import { BarChart } from 'echarts/charts'
import { TitleComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
// 注册需要的模块
init.use([BarChart, TitleComponent, TooltipComponent, CanvasRenderer])
// 使用 init 方法创建图表
const chart = init(document.getElementById('chart'))
echarts使用
php
<div ref="totalOrderRef" :style="{width:'100%',height:'100px'}" />
<script setup>
import { onMounted,ref,getCurrentInstance } from 'vue'
// 1. 定义 ref 变量,和模板中的 ref="totalOrderRef" 对应
const totalOrderRef = ref(null)
// 2. 在 onMounted 中获取元素(DOM 渲染完成)
onMounted(() => {
// 通过 ref 获取元素(Vue3 推荐方式)
const chartDom = totalOrderRef.value
if (chartDom) {
const instance = getCurrentInstance()
const $echarts = instance.appContext.config.globalProperties.$echarts
const chart = $echarts.init(chartDom)
chart.setOption({
xAxis: {
type: 'value',
show: false
},
yAxis: {
type: 'category',
show: false
},
series: [{
name: '上月平台用户数',
type: 'bar',
stack: '总量',
data: [300],
barWidth: 10,
itemStyle: {
color: '#45c946'
}
},{
name: '今日平台用户数',
type: 'bar',
stack: '总量',
data: [200],
itemStyle: {
color: '#ddd'
}
},{
type: 'custom',
stack: '总量',
data: [300],
renderItem: (params,api) => {
const value = api.value(0)
const point = api.coord([value,0])
return {
type: 'group',
position: [point[0] - 10, point[1]] ,
children: [{
type: 'path',
shape: {
d: 'M0 767.909l512.029-511.913L1024 767.909 0 767.909z',
x: 0,
y: 2,
width: 20,
height: 20
},
style: {
fill: '#45c946'
}
},{
type: 'path',
shape: {
d: 'M1024 255.996 511.971 767.909 0 255.996 1024 255.996z',
x: 0,
y: -22,
width: 20,
height: 20
},
style: {
fill: '#45c946'
}
}]
}
}
}],
grid: {
top: 0,
bottom: 0,
left: 0,
right: 0
}
})
}
})
</script>
getCurrentInstance()
在 <script setup> 中 "接触" 到组件底层实例的唯一官方入口(<script setup> 是封闭作用域,无法直接访问 this,所以无法像 Vue2 那样用 this.$echarts,只能通过 getCurrentInstance() 获取 appContext(应用上下文),进而访问全局挂载的 $echarts。必须在组件生命周期内调用,onMounted、onCreated 或 <script setup> 顶层调用。异步回调(如 setTimeout、接口请求回调)中调用可能获取不到实例。
创建 ECharts 实例
$echarts.init() 方法会创建一个独立的 ECharts 实例 ,并返回给变量 chart。这个实例是操作图表的 "唯一入口"
-
每个 DOM 容器对应一个独立的 ECharts 实例(避免多个图表冲突);
-
实例包含图表的所有配置、数据、渲染状态等核心信息。
setOption
向 ECharts 实例传入图表的配置项(Option),让 ECharts 根据配置渲染 / 更新图表
php
chart.setOption({
xAxis: { type: 'value', show: false }, // x轴配置
yAxis: { type: 'category', show: false }, // y轴配置
series: [/* 柱状图/自定义图形系列配置 */], // 图表数据和样式
grid: { top: 0, bottom: 0 } // 网格布局
})
配置合并规则
arduino
// 完全替换旧配置,重新渲染图表
chart.setOption(newOption, true)
-
默认:
setOption会合并新旧配置(新配置覆盖旧配置,未修改的保留); -
强制替换:如需完全替换配置(而非合并),可传第二个参数
true:
renderItem
它是 ECharts 「自定义系列(custom series)」的核心渲染函数,作用是「告诉 ECharts 如何手动绘制每一个数据项的图形」
renderItem 是自定义系列(type: 'custom')的必填配置,ECharts 渲染自定义系列时,会对每一个数据项调用一次 renderItem,你需要在这个函数中返回「图形描述对象」,ECharts 会根据这个描述画出对应的图形。
当 ECharts 渲染 type: 'custom' 的系列时,会遍历该系列的 data 数组(比如写的 data: [200]),对每一个数据项(这里是 200)执行一次 renderItem 函数。
通过 renderItem,你可以突破 ECharts 内置图表(如柱状图、折线图)的限制,实现:
- 自定义形状(如三角形、五角星、不规则路径);
- 精准控制图形位置(如定位到 200 数值处);
- 组合多个图形(如一个三角形 + 一个文本标签);
- 动态调整图形样式(如根据数据值改变颜色 / 大小)。
api对象提供 ECharts 内置的「坐标转换 / 数据获取」工具方法:
api.value(dimIndex):获取当前数据项指定维度的值(如 api.value(0) 取 200);
api.coord([x, y]):把逻辑坐标(如 [200, 0])转换成画布像素坐标;
api.size([width, height]):把逻辑尺寸转换成像素尺寸;
api.style():获取系列默认样式
params对象包含当前数据项的上下文信息:
params.dataIndex:当前数据项的索引(如 0)
params.value:当前数据项的原始值(如 200);
params.seriesIndex:当前系列的索引;
params.coordSys:坐标系信息(如 x/y 轴类型)
renderItem 必须返回一个「图形描述对象」(或数组),ECharts 会根据这个对象绘制图形
arduino
return {
type: 'group', // 图形类型:组合图形(可包含多个子图形)
position: point, // 图形的基准位置(像素坐标)
children: [{ // 子图形列表
type: 'path', // 图形类型:路径(自定义形状)
shape: { // 形状配置
d: 'M0 767.909...', // 路径指令(三角形的绘制路径)
x: 0,//路径的偏移
y: 0, //路径的偏移
width: 20, // 尺寸
height: 20 // 尺寸
},
style: { fill: 'red' } // 样式:填充红色
}]
}
| 类型 | 作用 | 示例场景 |
|---|---|---|
path |
绘制自定义路径(如三角形、不规则图形) | 你画红色三角形的核心 |
rect |
绘制矩形 | 自定义柱状图 |
circle |
绘制圆形 | 自定义散点图 |
text |
绘制文本 | 给图形加标签 |
group |
组合多个图形 | 三角形 + 文本标签 |
和前面用 type: 'bar' 画柱状图,ECharts 会自动渲染;而 type: 'custom' 则是把渲染权完全交给你,核心差异
| 维度 | 内置系列(bar/line) | 自定义系列(custom) |
|---|---|---|
| 渲染逻辑 | ECharts 自动绘制(固定形状) | 你通过 renderItem 手动定义 |
| 灵活性 | 低(只能改样式,不能改形状) | 高(可画任意形状) |
| 复杂度 | 简单(只需配置 data/style) | 稍高(需手动计算坐标 / 形状) |
| 适用场景 | 标准图表(柱状图、折线图) | 非标准图形(自定义标记、组合图形) |
配置
stack
它是 ECharts 中用于实现「堆叠式图表」的关键配置,作用是「将多个同系列类型(如 bar)、同 stack 值的系列,在同一类目下按数值累加堆叠显示」 ------ 两个柱状图系列因为都配置了 stack: '总量',才会从 0 开始依次累加(200+260),形成绿色 + 灰色的分段堆叠效果
组件引用
把导入的 TopView 组件 "注册" 到 Home 组件的作用域中,只有注册后,才能在 <template> 中使用;
xml
<template>
<div class="home">
<!-- 引用组件 -->
<top-view/>
</div>
</template>
<script>
import TopView from '../components/TopView'
export default {
name: 'Home',
components: {
TopView,// 局部注册组件
}
}
</script>
<style>
.home{
width: 100%;
height: 100%;
padding: 0 20px;
background: #eee;
box-sizing: border-box;
}
</style>
仅在当前 Home 组件内可用,其他组件(如 About.vue)不能直接用 <top-view>,需重新导入 + 注册。不会污染全局作用域,适合只在单个页面使用的组件
公共组件提取
如CommonCard这个组件很多地方用到,进行提取
创建一个mixins文件夹,创建对应的文件名card.js
javascript
import CommonCard from '../components/CommonCard/index'
export default {
components: {
CommonCard
}
}
需要调用时引入card.js
xml
<template>
<common-card/>
</template>
<script>
import CommonCard from '../../mixins/card'
export default {
mixins: [CommonCard]
}
</script>
<style>
</style>
vue3调用公共组件
xml
<template>
<common-card
title="累计订单量"
value="2,124,223"
>
</common-card>
</template>
<script setup>
import { onMounted, ref,defineOptions } from 'vue'
import commonCardMixin from '../../mixins/commonCardMixin'
defineOptions({
mixins: [commonCardMixin]
})
</script>
需要使用defineOptions
Mixin 是 Vue2 的经典写法,Vue3 更推荐用 "组件导入函数" 或 "全局注册组件" 替代 mixin(减少隐式依赖)
javascript
// utils/importComponents.js
export const useCommonCard = () => {
const CommonCard = defineAsyncComponent(() => import('../components/CommonCard/index'))
return { CommonCard }
}
业务组件中使用:
xml
<script setup>
import { useCommonCard } from '../../utils/importComponents'
const { CommonCard } = useCommonCard() // 显式获取组件
</script>
全局注册组件(适合全项目高频使用的组件)
javascript
// main.js
import { createApp } from 'vue'
import CommonCard from './components/CommonCard/index'
const app = createApp(App)
app.component('CommonCard', CommonCard) // 全局注册,所有组件可直接使用
插槽
引用子组件common-card时,自定义两个插槽的内容及样式。下面代码存在有误地方,
xml
<template>
<div class="compare-wrapper">
<div class="compare">
<span>同比</span>
<span class="emphasis">7.3%</span>
</div>
</div>
</template>
这段代码无法正常显示出默认插槽中的内容,<template> 无任何指令(如 v-slot)直接包裹默认插槽 → 编译时会被忽略,内容不渲染.
xml
父组件:
<template>
<common-card
title="销售额"
>
<template>
<div class="compare-wrapper">
<div class="compare">
<span>同比</span>
<span class="emphasis">7.3%</span>
</div>
</div>
</template>
<!-- footer 具名插槽 -->
<template v-slot:footer>
<span>昨日销售额:</span>
<span class="money">¥40,123</span>
</template>
</common-card>
</template>
子组件:
<template>
<div class="common-card">
<div class="title">{{title}}</div>
<div>
<slot></slot>
</div>
<div class="total">
<slot name="footer"></slot>
</div>
</div>
</template>
<template>...</template>,Vue 不知道这是 "默认插槽",就不会把内容插入到 <slot></slot> 位置;加 v-slot 指令后,Vue 才会识别并渲染。
xml
<template v-slot>
<div class="compare-wrapper">
<div class="compare">
<span>同比</span>
<span class="emphasis">7.3%</span>
</div>
</div>
</template>
或者不需要template包裹
xml
<div class="compare-wrapper">
<div class="compare">
<span>同比</span>
<span class="emphasis">7.3%</span>
</div>
</div>
| 写法 | 是否生效 | 原因 |
|---|---|---|
<template>内容</template> |
❌ 不生效 | Vue 无法识别这是默认插槽,编译时忽略该 template |
<template v-slot>内容</template> |
✅ 生效 | v-slot 指令明确标识这是「默认插槽」,Vue 会把内容插入到 <slot></slot> 位置 |
| 直接写内容(无 template) | ✅ 生效 | Vue 自动把未包裹的内容识别为默认插槽,是最简洁的写法 |
Vue2 和 Vue3 在「<template> 包裹默认插槽」的语法上确实有差别 ------Vue2 中 <template> 包裹默认插槽无需加 v-slot 就能生效,而 Vue3 必须显式加 v-slot 指令
| 场景 | Vue2 写法(生效) | Vue3 写法(生效) | Vue3 错误写法(不生效) |
|---|---|---|---|
| template 包裹默认插槽 | <template>内容</template> |
<template v-slot>内容</template> |
<template>内容</template> |
| 直接写默认插槽内容 | 直接写内容(生效) | 直接写内容(生效) | - |
| 具名插槽 | <template slot="footer"> / <template v-slot:footer> |
<template #footer> / <template v-slot:footer> |
- |
vue3这样都通过 v-slot 指令管理,逻辑更清晰,也避免了 "无指令 template 被误解析" 的问题。
slot作用
Vue3 中的插槽,本质是组件对外暴露的 "自定义渲染接口" ------ 组件开发者给组件预留 "空白位置"(<slot>),组件使用者可以往这个位置插入任意内容(HTML、子组件、逻辑渲染的内容),实现「组件固定结构复用 + 自定义内容灵活定制」。
插槽就像你买的 "定制化蛋糕胚",蛋糕胚的大小、形状(组件的基础 UI / 逻辑)是固定的,但你可以往上面加水果、奶油、巧克力(插槽内容),做出不同样式的蛋糕,而不用重新做蛋糕胚。
复用性:
没有插槽的组件,只能显示固定内容
xml
<!-- 无插槽的 CommonCard 组件 -->
<template>
<div class="card">
<div class="title">累计销售额</div>
<div class="content">¥1,211,312</div>
</div>
</template>
有插槽,复用性拉满
xml
<!-- 有插槽的 CommonCard 组件 -->
<template>
<div class="card">
<div class="title">{{ title }}</div>
<!-- 插槽:预留自定义位置 -->
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script>
export default { props: ['title'] }
</script>
可自定义内容,同一个组件适配多场景
xml
<!-- 场景1:显示销售额 -->
<common-card title="累计销售额">
<div>¥1,211,312</div>
</common-card>
<!-- 场景2:显示订单数 -->
<common-card title="累计订单数">
<div>123,456 单</div>
</common-card>
<!-- 场景3:显示复杂内容(同比数据+箭头) -->
<common-card title="累计销售额">
<div class="compare">
<span>日同比 7.3%</span>
<div class="arrow-up"></div>
</div>
</common-card>
逻辑与 UI 解耦,降低维护成本,比如修改卡片的基础样式只需改 CommonCard,修改销售额的显示格式只需改插槽内容。
插槽可以插入任意内容:
- 普通 HTML 标签(
<div>/<span>); - 其他 Vue 组件(
<el-button>/<chart>); - 带逻辑的渲染内容(
v-if/v-for/{{ 变量 }}); - 甚至是 JSX/TSX(复杂场景)。
Vue3 把插槽分为 3 类,覆盖所有自定义场景,且废弃了 Vue2 的 slot 属性,统一用 v-slot 指令(简写 #)
1. 默认插槽(匿名插槽)
-
定义 :无
name属性的<slot>,是组件的 "默认自定义位置"; -
用法:
- 简洁写法(推荐):直接把内容写在组件标签内,无需
<template>; - 完整写法:用
<template v-slot>包裹(Vue3 必须加v-slot)。
- 简洁写法(推荐):直接把内容写在组件标签内,无需
2. 具名插槽
-
定义 :有
name属性的<slot>,用于组件内多位置自定义(比如卡片的 header、body、footer); -
用法 :用
<template #插槽名>(或<template v-slot:插槽名>)包裹内容,和组件内的name一一对应。xml<!-- 组件内定义多个具名插槽 --> <template> <div class="card"> <div class="header"> <slot name="header"></slot> <!-- 具名插槽:header --> </div> <div class="body"> <slot></slot> <!-- 默认插槽 --> </div> <div class="footer"> <slot name="footer"></slot> <!-- 具名插槽:footer --> </div> </div> </template> <!-- 使用具名插槽 --> <common-card> <template #header> <h3>累计销售额</h3> <!-- 对应 header 插槽 --> </template> <div>¥1,211,312</div> <!-- 对应默认插槽 --> <template #footer> <span>昨日销售额:¥40,123</span> <!-- 对应 footer 插槽 --> </template> </common-card>
3. 作用域插槽(带数据的插槽)
-
定义:组件可以给插槽传递数据(作用域数据),使用者可以接收并基于这些数据自定义渲染 ------ 这是插槽的 "高级玩法",实现「组件传数据 + 使用者自定义渲染」;
-
用法:
-
组件内:给
<slot>绑定属性(:数据名="数据值"); -
使用者:用
<template v-slot="插槽变量">接收数据,在插槽内使用。xml<!-- 组件内定义作用域插槽(给插槽传数据) --> <template> <div class="card"> <!-- 给默认插槽传数据:salesData --> <slot :salesData="sales"></slot> <!-- 给 footer 插槽传数据:yesterdaySales --> <slot name="footer" :yesterdaySales="yesterday"></slot> </div> </template> <script> export default { data() { return { sales: { total: 1211312, dayRatio: 7.3 }, // 组件内部数据 yesterday: 40123 } } } </script> <!-- 使用作用域插槽(接收并使用数据) --> <common-card> <!-- 接收默认插槽的 data,自定义渲染 --> <template v-slot="slotProps"> <div> 累计销售额:¥{{ slotProps.salesData.total.toLocaleString() }} <span>日同比:{{ slotProps.salesData.dayRatio }}%</span> </div> </template> <!-- 接收 footer 插槽的 data,自定义渲染 --> <template #footer="footerProps"> <span>昨日销售额:¥{{ footerProps.yesterdaySales.toLocaleString() }}</span> </template> </common-card>
-
Vue3 支持对插槽变量解构,让代码更简洁:
xml
<!-- 解构默认插槽数据 -->
<template v-slot="{ salesData }">
<div>累计销售额:¥{{ salesData.total }}</div>
</template>
<!-- 解构具名插槽数据 + 重命名 -->
<template #footer="{ yesterdaySales: ys }">
<span>昨日销售额:¥{{ ys }}</span>
</template>
4.支持动态插槽名
可以用变量作为插槽名
xml
<template #[dynamicSlotName]>
<div>动态插槽内容</div>
</template>
<script>
export default {
data() {
return { dynamicSlotName: 'footer' } // 动态指定插槽名
}
}
</script>
插槽的实战场景
数据可视化组件:封装图表组件时,用作用域插槽传递图表数据,使用者自定义 tooltip、图例的显示样式
列表渲染组件:装通用列表组件时,用作用域插槽传递列表项数据,使用者自定义列表项的渲染样式
通用组件封装:封装卡片、表格、弹窗、导航栏等通用组件时,用插槽预留自定义位置
vue-echarts
vue-echarts 完全兼容 Vue3,且专门为 Vue3 做了适配(支持 <script setup>、组合式 API 等),相比直接使用原生 ECharts,它的优势是:
- 自动处理 ECharts 实例的创建 / 销毁,避免内存泄漏;
- 响应式更新配置,数据变化时自动重绘图表;
- 无需手动获取 DOM 元素,直接通过组件属性传参。
markdown
npm install echarts vue-echarts -S
或
css
npm install vue-echarts echarts --save
全局注册
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import setupElement from './plugins/element.js'
// 1. 核心补充:按需导入 ECharts 核心模块(必填)
import { use } from 'echarts/core'
// 导入你需要的图表类型(根据业务场景添加,比如柱状图、自定义系列)
import { BarChart, CustomChart, LineChart } from 'echarts/charts'
// 导入渲染器(必须,推荐 CanvasRenderer)
import { CanvasRenderer } from 'echarts/renderers'
// 可选:导入交互组件(如提示框、图例,按需添加)
import { TooltipComponent, LegendComponent } from 'echarts/components'
// 2. 核心补充:注册 ECharts 模块(必填)
use([
BarChart, CustomChart, LineChart, // 图表类型
CanvasRenderer, // 渲染器(必须)
TooltipComponent, LegendComponent // 可选交互组件
])
// 3. 导入 vue-echarts 组件(你的原有代码)
import VueECharts from 'vue-echarts'
const app = createApp(App)
setupElement(app)
// 4. 全局注册 <v-chart> 组件(你的原有代码,正确)
app.component('v-chart', VueECharts)
app.use(router)
app.mount('#app')
v-echarts
css
npm i v-charts echarts -S
或安装(直接执行以下命令,纠正包名 + 忽略 peer 依赖冲突)
css
npm install vue-echarts echarts -S --legacy-peer-deps
vue2和vue3支持状态
| 维度 | v-charts(第三方) |
vue-echarts(官方) |
|---|---|---|
| Vue3 适配 | ❌ 无官方支持 | ✅ 完全适配(v6+ 版本专为 Vue3 设计) |
| 维护状态 | ❌ 停止维护 | ✅ 持续更新,和 ECharts 版本同步 |
| 打包体积 | ❌ 内置全量 ECharts,体积大 | ✅ 按需导入模块,体积可控 |
| 功能完整性 | ❌ 仅封装部分 ECharts 功能 | ✅ 支持 ECharts 所有功能(包括自定义系列、交互) |
| 文档 / 社区 | ❌ 文档陈旧,无社区支持 | ✅ 官方文档完善,问题可在 ECharts 社区解决 |
v-charts(第三方)和 vue-echarts(官方),前者是 Vue2 专属,后者是 Vue3 首选
Element Plus
el-menu
<el-menu> 组件的基础使用方式,用于实现水平导航菜单(如页面顶部的销售额 / 访问量切换)
ini
<el-menu
mode="horizontal"
:default-active="'1'"
@select="pnSelect"
>
| 配置项 | 类型 | 作用 & 意义 | |
|---|---|---|---|
mode="horizontal" |
字符串 | 定义菜单的布局模式: horizontal:水平布局(顶部导航); vertical:垂直布局(侧边栏); inline:内嵌布局(侧边栏子菜单展开) |
|
:default-active="'1'" |
字符串 / 数字(响应式绑定) | 设置菜单的默认选中项 ,值需和 <el-menu-item> 的 index 匹配;注意:index 本质是字符串,所以这里用 '1'(加引号)更规范(也可写 1,Element Plus 会自动转换) |
默认选中 "销售额"(index="1"),页面加载后该菜单项会高亮 |
@select="pnSelect" |
事件绑定 | 监听菜单选中事件:点击 <el-menu-item> 时触发,回调函数会接收当前选中项的 index 值 |
点击 "销售额"/"访问量" 时,pnSelect 方法会拿到 index(1/2),用于后续业务逻辑(如切换图表数据) |