動態(按需)加載異步子組件
之前說過 ECharts 如何封裝,今天來講一講 ECharts 如何做性能優化。
對于之前 ECharts 的封裝子組件,我們可以使用 component 動態組件的方式進行渲染,并傳參。
并且使用 import 動態導入搭配 defineAsyncComponent 實現打包代碼分割,import 實現組件按需加載, defineAsyncComponent 實現將這些異步組件單獨打包,減小主包的體積。
而且統一封裝圖表組件,統一管理圖表配置 options,可以實現業務數據與圖表樣式配置分離。
以下代碼均只保留核心代碼,已脫敏。
index.vue 中去實現動態渲染:
<componentclass="w-full":is="echartsComponent"v-if="echartsComponent"v-bind="echartsProps"/>
// 接收參數const props = defineProps({confgData: {type: Object,required: true,default: () => ({}),},boardData: {type: Object,required: true,default: () => ({}),},// componentKey: {// type: String,// required: true,// },});// ...// 所有的圖表按需加載const allEcharts = [{type: 'bar',component: defineAsyncComponent(() => import('./BarEcharts/index.vue')),},{type: 'pie',component: defineAsyncComponent(() => import('./PieCharts/index.vue')),},{type: 'line',component: defineAsyncComponent(() => import('./LineCharts/index.vue')),},{type: 'progress',component: defineAsyncComponent(() => import('./Progress/index.vue')),},];// 拋出一個父組件使用該組件修改內部配置的方式const emit = defineEmits(['updates']);// 針對不同圖表做不同的參數傳遞const echartsProps = computed(() => {if (confgData.value?.type === 'bar') {return {xAxisLabels: props.boardData.x_axis_labels,yAxisLabels: props.boardData.y_axis_labels,seriesData: props.boardData.series_data,isHorizontal: props.boardData?.y_axis_labels?.length >= 1,};} else if (confgData.value?.type === 'pie') {return {data: props.boardData.data,title: props.boardData?.title,};} else if (confgData.value?.type === 'progress') {return {percent: props.boardData.percent,title: props.boardData?.title,};} else {// 折線圖// obj 特殊化配置return {xAxisLabels: props.boardData.x_axis_labels,yAxisLabels: props.boardData.y_axis_labels,seriesData: props.boardData.series_data,isHorizontal: props.boardData?.y_axis_labels?.length > 1,...obj,};}});// 計算得到所需組件const echartsComponent = computed(() => {return allEcharts.find((item) => item.type === confgData.value?.type)?.component;});
按需導入 ECharts 包
按需引入 ECharts,而非全量引入,減小打包體積大小。
在全局入口文件中引用。
lib/echarts.ts
import * as echarts from 'echarts/core';import {BarChart,LineChart,// ...
} from 'echarts/charts';import {TitleComponent,TooltipComponent,// ...
} from 'echarts/components';import { SVGRenderer } from 'echarts/renderers';echarts.use([LegendComponent,TitleComponent,// ...
]);export default echarts;
依賴預構建
依賴預構建 (Dependency Pre-Bundling)。
vite.config.ts
文件中,相關的配置是 optimizeDeps.include
:
// vite.config.ts
export default defineApplicationConfig({overrides: {optimizeDeps: {include: ['echarts/core','echarts/charts','echarts/components','echarts/renderers',// ... other dependencies],},// ...},
});
這個配置是如何優化 ECharts 的?
-
優化對象:此優化主要針對 開發環境 (
dev server
),旨在提升開發時的頁面加載速度和熱更新性能。 -
工作原理:
- 問題背景:
- ECharts 庫本身是由大量的小模塊組成的(例如,
core
是核心,charts
目錄下有各種圖表類型,components
目錄下有提示框、圖例等組件)。 - 當我們在代碼中按需引入時(如
import { BarChart } from 'echarts/charts'
),在開發模式下,瀏覽器需要根據import
語句逐個去請求這些零散的模塊文件。 - 如果一個復雜的圖表需要幾十個模塊,就會導致瀏覽器發起大量的網絡請求,形成“請求瀑布流”,嚴重拖慢頁面首次加載速度。
- ECharts 庫本身是由大量的小模塊組成的(例如,
- Vite 的解決方案:Vite 在啟動開發服務器時,會先掃描項目代碼,找出所有依賴。對于
optimizeDeps.include
中明確列出的依賴,Vite 會使用速度極快的esbuild
工具,提前將這些零散的模塊 “預構建” 成一個或少數幾個大的 JavaScript 文件。- 具體到 ECharts:配置中的
'echarts/core'
,'echarts/charts'
,'echarts/components'
,'echarts/renderers'
告訴 Vite:“請提前把這幾個路徑下的所有 ECharts 模塊都找到,并將它們打包成一個整體。”
- 具體到 ECharts:配置中的
- 問題背景:
-
帶來的好處:
- 減少網絡請求:經過預構建,當瀏覽器需要加載 ECharts 時,不再是請求幾十個零散的小文件,而是只請求一個或幾個已經打包好的大文件。這極大地減少了 HTTP 請求開銷。
- 模塊格式轉換:
esbuild
會將可能存在的 CommonJS 或 UMD 格式的模塊(一些舊的庫可能還在使用)統一轉換為瀏覽器原生支持的 ESM (ES Modules) 格式,避免了在瀏覽器端進行復雜的模塊解析。 - 更快的頁面加載:最終結果就是,在開發環境中,包含 ECharts 圖表的頁面加載速度會得到顯著提升,讓開發體驗更加流暢。
注意:這個優化主要作用于 開發環境。在 生產環境打包 (
build
) 時,Vite 會利用 Rollup 和 Tree Shaking 機制,確保最終打包出的代碼只包含實際用到的 ECharts 模塊,以實現生產環境包體積的最小化。這里的optimizeDeps
配置與最終的打包體積沒有直接關系。
圖表的屏幕適配
統一解決了窗口縮放時圖表的適應問題,避免因窗口縮放導致的 ECharts 圖表變形。(主要針對大屏)
- 使用 scale,縮放比例比較小,那么左右留白比較大。
- 推薦使用 vw/vh,動態計算寬高比例,字體等,比較靈活,避免大片留白。缺點:每個圖表都需要單獨做字體、間距、位移的適配,比較麻煩。