项目的简单介绍
- 使用vue3 typescript pina ElementPlus vueuse vue-router
- 使用vite构建工具,yarn包管理工具,node版本 14.5.7
- 使用了 pinia-plugin-persist 做store数据的本地持久化管理
- 使用到了封装成hook形式的Echarts
- 数据请求使用基本的axios封装。
- 使用webSocket长链接协议。
用到的知识点封装等
1. pinia 数据持久化方案pinia-plugin-persist
下面demo简化ts使用了any
- 在main.ts中初始化pinia等。
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
const pinia = createPinia()
pinia.use(piniaPersist)
app.use(pinia)
- 在store index.ts文件中
import { createPinia } from 'pinia'
export * from './modules'
const pinia = createPinia()
export default pinia
- 每个store模块分开所以在 modules中新建不同模块的store
import { defineStore } from 'pinia'
export const useCategory = defineStore('category', {
persist: {
enabled: true,
},
state: (): { currentCategory: {}; category: {} } => ({
category: {},
currentCategory: {},
}),
getters: {},
actions: {
setCategory(data: any) {
this.category = data
},
setCurrentCategory(data: any) {
this.currentCategory = data
},
},
})
- 调用方法
import { useCategory } from '@/store/modules/category'
const store = useCategory()
// 设置数据
store.setCategory(data)
// 获取数据
store.category
2. vue3中Echarts的使用
- 引入封装好的hook
import { useDebounceFn } from '@vueuse/core'
import * as echarts from 'echarts'
import { ref, onMounted, onBeforeUnmount, markRaw } from 'vue'
import useTimeout from './useTimeout'
interface TypeParams {
id: string
option: any
mouseover?: any
mouseout?: any
isInit?: boolean
isResize?: boolean
width?: number | string
height?: number | string
}
export default ({ id, option, mouseover, mouseout, isInit = true, isResize = true, width, height }: TypeParams) => {
const myChart = ref<echarts.ECharts | null>(null)
const initOption = ref(option)
const { timeout } = useTimeout()
// 初始化chart
const chartInit = (): void => {
destroyChart()
const el = document.getElementById(id) as HTMLDivElement
if (!myChart.value && el) {
el.style.width = width ? `${width}${typeof width === 'number' ? 'px' : ''}` : '100%'
el.style.height = height ? `${height}${typeof height === 'number' ? 'px' : ''}` : '100%'
// Uncaught TypeError: Cannot read properties of undefined (reading 'type')
// 解决图例点击的时候报错,或者缩放的时候报错
// 在实例化echart时,将其指定为非响应式的即可
myChart.value = markRaw(echarts.init(el))
chartSetOption()
myChart.value && mouseover && myChart.value.on('mouseover', mouseover)
myChart.value && mouseout && myChart.value.on('mouseout', mouseout)
}
}
// 设置option
const chartSetOption = (option: any = initOption.value): void => {
myChart.value && myChart.value.setOption(option)
}
// 销毁
const destroyChart = (): void => {
myChart.value && myChart.value.off('mouseover')
myChart.value && myChart.value.off('mouseout')
myChart.value && myChart.value.clear()
timeout(() => {
myChart.value = null
})
}
// 设置series数据
const setData = (data: any[] = []): void => {
option.series = data
chartSetOption(option)
}
// 设置x轴数据
const setXData = (data: any[] = []): void => {
option.xAxis.data = data
destroyChart()
}
// 重置
const chartResize = (): void => {
myChart.value && myChart.value.resize()
}
// 窗口缩放重新渲染
window.addEventListener(
'resize',
useDebounceFn(() => isResize && timeout(chartResize), 1000)
)
onMounted(() => isInit && chartInit())
// 销毁
onBeforeUnmount(() => {
// chart.dispose()
window.removeEventListener('resize', chartResize)
destroyChart()
})
return {
chartInit,
setXData,
setData,
destroyChart,
}
}
- 使用如下
// 需要注意的地方 option需要使用computed返回
const { chartInit } = useEcharts({ id: 'detailChart', option, width: props.width, height: props.height, isInit: false })
watch(
() => props.data,
(value) => {
if (value.xaxis || value.yaxis) {
chartInit()
}
},
{
deep: true,
immediate: true,
}
)
3. 遇到过的问题
- 判断图片是否加载成功
排查问题的时候,有些时候会存在一些图片损坏也要注意留意。
1. 最简单的方案是使用 elementplus中自带的
<el-image
:src="item.image"
:preview-src-list="item.image"
fit="cover"
class="table-image"
preview-teleported
>
<template #error>
<img class="errImg slotImg" src="@/assets/images/default.png" alt="" />
</template>
</el-image>
2. 可以利用img标签的失败回调。
<img
:id="el.id"
:src="item.image"
class="carousel-image"
@error="loadFail(el.id)"
/>
const loadFail = (id: string) => {
const image: any = document.getElementById(id)
image.src = new URL(`../../assets/images/default.png`, import.meta.url).href
image.className = 'defaultImg'
image.οnerrοr = null
}
- 动态引入图片的问题
1. 在vite中没有办法使用 require来引入动态图片。可能很重要的一个问题是图片本身就是损坏的。
2. 使用
new URL(`../../assets/images/default.png`, import.meta.url).href
可以避免打包的时候文件路径不对。
- 本地开发的时候组件切换没有问题,线上出现 Cannot read properties of null (reading 'insertBefore')这种问题。
1. v-if导致,当值为false的时候操作了其中的DOM,简单的办法使用v-show
使用了watchEffect,同时使用了同一个组件,导致值相互影响。
2. el-table中需要注意
el-table-column渲染时报错,比如某些值为null
- reactive的使用需要注意
1. const data = reactive({
result: []
})
不要直接 const data = reactive({}) 然后给data赋值
2. 解构会失去响应式需要注意
const { result } = data
result = xxxx
可以使用toRefs或者toRef
const { result } = toRefs(data)
- 动态路由引入的问题
使用 @引入的路由出现的问题。解决方案有很多种,但是实际可能还是vite版本的问题目前探索下来在2.9.10版本未发现问题。
- 打包需要做的某些优化
import viteCompression from 'vite-plugin-compression'
import Components from 'unplugin-vue-components/vite'
import AutoImport from 'unplugin-auto-import/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
vite.config.ts中的plugin使用到了一下
VueSetupExtend(),
AutoImport({
// @ts-ignore
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
Icons({ compiler: 'vue3', autoInstall: true }),
viteCompression({
//生成压缩包gz
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
// build的一些打包配置
build: {
sourcemap: mode !== 'production',
// minify: 'esbuild', // 启用这个下面的才会生效,打包速度会慢很多
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
// 取消计算文件大小,加快打包速度
reportCompressedSize: false,
// 文件按照不同的文件夹打包
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: '[ext]/[name]-[hash].[ext]',
},
},
},