vue3+vite+ts+pinia+elplus那些不得不填的坑
本次公司运营系统重构,刚好有机会使用一下新语法新框架,顺带学习一波(😒我绝对不说是老大让用vue3的),当然避免不了的,又遇到了很多新坑,本着有坑必填的宗旨,接下来让我们开始挖坑埋土吧~
一、关于element-plus
后台管理平台,当然是vue+element的组合最香。关于引入element-plus
官网已经说的很清楚了,这里就不在赘述,墙裂推荐按需引入配置 按需引入element-plus。配置成功之后,大部分组件我们都不需要手动进行引入了,系统会自动引入组件,我们直接使用就可以。
问题一:有几个组件,必须要单独引入才可以使用:
ElMessage/ElMessageBox/ElLoading
需要在使用的组件里单独引入后再使用(ps: 本项目只涉及到这三个需要单独引入的组件,不排除还有其他组件),同时对应组件的样式也需要单独引入到main.ts里,这里我们可以通过配置解决样式需要手动引入问题。
// vite.config.ts
import { createStyleImportPlugin, ElementPlusResolve, } from 'vite-plugin-style-import'
export default defineConfig({
plugins: [
createStyleImportPlugin({
resolves: [ElementPlusResolve()],
libs: [
{
libraryName: 'element-plus',
esModule: true,
resolveStyle: (name: string) => {
return `element-plus/theme-chalk/${name}.css`
}
}
]
})
]
})
解决了上述组件和样式问题,我们又发现ElMessage
在使用过程中,不会自动消失,同时会报一个错误 Cannot set properties of null(setting visible)
这个时候我们需要升级下vue的版本,升级到 3.2.32
以上的版本,就可以正常关闭消息提示了。
问题二:关于Icon的使用
关于Icon组件的使用,官网上也有说明,但是个人觉得说的不够详细,这里贴出本人使用方法,采取的是全局注册组件方式。
// main.ts
import * as Icons from '@element-plus/icons-vue'
// 定义icon组件
const Icon = (props: { icon: string }) => {
const { icon } = props
return createVNode(Icons[icon as keyof typeof Icons])
}
let app = createApp(App)
app.component('Icon', Icon)
// 组件中使用,使用时可以去官网查询需要的icon名字,同时需要设置样式,主要是尺寸
<Icon class="el-button-icon" :icon="'Refresh'" />
问题三:关于table组件的封装
在之前的文章 新系统开发问题记录 里有一部分内容是关于 element-ui table
的封装,升级到 element-plus
以后,封装方法大体一致,主要区别就是关于插槽的使用,因为vue3本身也对插槽做了修改。
// ==== 原封装关于插槽的使用
// table组件内部
<template slot-scope="scope">
<template v-if="item.slot">
<!-- 需要使用插槽展示的数据 -->
<slot :name="item.prop" :scope="scope.row"></slot>
</template>
<template v-else>
<!-- 直接获取属性展示内容 -->
{{scope.row[item.prop]}}
</template>
</template>
// 组件内使用
<template slot="status" slot-scope="data">
<el-tag v-if="data.scope.status === 0" size="small">启用</el-tag>
<el-tag v-else-if="data.scope.status === 1" size="small" type="danger">禁用</el-tag>
</template>
// ==== 新封装关于插槽的使用
// table组件内部
<template #default="scope">
<template v-if="item.slot">
<slot :name="item.prop" :row="scope.row"></slot>
</template>
<template v-else>
{{ scope.row[item.prop] }}
</template>
</template>
// 组件内使用
<template #status="scope">
<el-tag v-if="scope.row.status === 0" size="small">正常</el-tag>
<el-tag v-else size="small" type="danger">禁用</el-tag>
</template>
问题四:关于element-plus组件类型别名
引入ts以后,在使用组件对应的方法的时候经常会出现经典的ts红
也就是ts提示错误了,这时候我们可以引入element-plus组件对应的类型,这里简单说几种类型。
// ElTree
const elTreeRef = ref<InstanceType<type ElTree>>()
// 使用对应方法时
elTreeRef.value?.setCheckedKeys()
// Form
const formRef = ref<FormInstance>()
// table
const tableRef = ref<InstanceType<type ElTable>>()
二、 关于vue-router4
更新后的路由,有些方法和之前有所区别,甚至是有很大的出入,这里简单总结如下。
(一)、路由监听
在组件内部监听路由变化时,我们可以使用两种方式:
(1)、onBeforeRouterUpdate
onBeforeRouterUpdate(to => {})
(2)、watch监听,可以监听整个路由,也可以监听其中某个参数变化
watch(()=> router.currentRoute.value, (newval, oldVal)=>{
console.log(newval, oldVal)
}, { immediate: true})
(二)、动态路由引入组件
在普通的路由配置中,我们通常会这么配置
{ path: '/index', component: () => import('@/pages/index.vue'), name: 'index'}
但是在此框架中,动态路由直接采用上述方式引入会提示报错The above dynamic import cannot be analyzed by vite
,这其实不是router的问题,而是vite
的一个警告,要消除警告有两种方式。
第一种:依旧使用import方式
{ path: item.path, component: () => defineAsyncComponent(()=>import(/* @vite-ignore */`@/pages/${item.name}.vue`)), name: item.name}
第二种:使用vite的glob导入
const modules = import.meta.glob('../pages/*.vue')
{ path: item.path, component: modules[`../pages/${menuItem.url}.vue`], name: item.name}
(三)、404路由配置
路由配置 404 跳转,传统方法中我们这样配置
{ path: '*', redirect: { name: '404' } }
但是在router4中,它又报错了Catch all routes ("*") must now be defined using a param with a custom regexp
,我们需要这么修改。
{ path: '/:pathMatch(.*)', redirect: { name: '404' } }
三、关于变量的定义
(一)reactive定义数组
对于vue3的变量定义,我们已经知道可以使用reactive定义对象类型,ref定义基础类型。对于数组,使用两种方式均可,但是如果使用reactive定义数组,会发现我们直接对数组赋值或者用concat方法都无法获取到响应数组,此时我们可以采用以下几种方式进行修改。
// 方法一:对于已经获取的数组进行遍历,逐项push进变量里,简单示例:
let list = reactive([])
for(let i = 0; i < arr.length; i++){
list.push(arr[i])
}
// 方法二:方法一太过麻烦,可以采用下面解构方法:
let list = reactive([])
list.push(...arr)
// 方法三:既然reactive可以用在对象上,那我们就嵌套一层对象在外层
let obj = reactive({
list: []
})
obj.list = arr
// 方法四:我们可以使用ref去定义
let list = ref([])
list.value = arr
(二)关于props设置类型和默认值
vue3+ts接收props变量,我们可以直接使用defineProps进行定义
let props = defineProps({
id: {
type: number
}
})
interface PropsType{
id: number,
text: string
}
let props1 = defineProps<PropsType>()
但是如果传递过来的props值,既有类型限制又需要设置初始默认值,此时我们就需要使用另一个方法withDefaults
进行设置。
interface PropsType {
id: number,
text?: number,
isAuth?: boolean
}
let props = withDefaults(defineProps<PropsType>(), {
text: 'this is text',
isAuth: false
})
(三)setup语法糖如何导出组件名字
在使用element-plus的menu组件的时候,因为动态菜单循环遍历,会存在调用组件自身的情况。如果在组件中直接引用自身,项目启动包括各种展示交互都是没问题的,但是编辑器里ts会报错,会提示找不到组件名字,这时候就需要导出组件的名字。
在2语法中,我们可以直接通过export default
里的name
属性进行导出,但是3里边没有这个导出了,结合网上的各种方法,总结如下方法解决。
方法一: 添加script标签导出,`此方法测试过,但是ts报错依旧存在,不知道是否是配置不对
<script lang='ts'>
export default {
name: 'test'
}
</script>
<script lang='ts' setup>
</script>
方法二:使用unplugin-vue-define-options插件
npm i unplugin-vue-define-options -D
// vite.config.ts
import DefineOptions from 'unplugin-vue-define-options/vite'
export default defineConfig({
plugins: [
vue(),
DefineOptions()
]
})
// tsconfig.json
{
"compilerOptions": {
"types": [
"vite/client",
"node",
"unplugin-vue-define-options"
]
}
}
// 组件内部
<script lang="ts" setup>
defineOptions({
name: 'MainSlideSub'
});
</script>
这样配置之后组件内ts错误就消失啦~✿✿ヽ(°▽°)ノ✿
四、关于vite打包问题
凭实力开发的程序(bug)终于完成了,赶紧打包测试一下吧~果然不出意外地出现了各种error...warning...
问题一:打包时ts校验node_modules
众所周知,node_modules是肯定不接受任何校验和提交的,在这里需要对打包命令加一个–skipLibCheck
即可:
// 原打包命令
"build": "vue-tsc --noEmit && vite build --mode production"
// 修改后
"build": "vue-tsc --noEmit --skipLibCheck && vite build --mode production",
问题二:调用自身组件提示props变量不存在
这个问题着实困扰了很久,整体程序运行不受影响,但是打包就有问题,而且只在问题三里我们使用的menu调用自身的组件里存在这个问题。说简单点,就是调用自身的组件传递props,在组件内使用的时候打包提示不存在这个props,属于ts提示的错误,最终采用下面方式解决。
// env.d.ts
declare let propsVal: any
declare let valFun: Function
问题三:"@charset" must be the first rule in the file
这个问题根本原因postcss会给含有中文的scss添加@charset=UTF-8
,同时element-plus的index.scss文件里也有@charset=UTF-8
,在打包合并的时候,两者就会合并,导致@charset=UTF-8
不是出现在最前面而提示的警告⚠️。
解决方法可以禁止项目scss里添加@charset=UTF-8
,同时配置删除库里的@charset=UTF-8
// vite.config.ts
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
charset: false
}
},
postcss: {
plugins: [
{
postcssPlugin: 'internal:charset-removal',
AtRule: {
charset: (atRule) => {
if (atRule.name === 'charset') {
atRule.remove();
}
}
}
}
]
}
},
})
以上四点就是本次所填的坑
所谓开发不止填坑不止,
填坑的道路
呸!
开发的道路任重而道远
还望各位大佬多多指教!
大佬们这首屎尿体诗如何,欢迎评论区留言秀才艺~
转载自:https://juejin.cn/post/7135696241245028382