记录vue3 + jsx + vite开发package遇到的问题
最近在开发项目的过程中需要封装额外的功能性开发组件,于是在当前开发目录下我又创建了一个文件夹进行单独打包处理
目录
在基础的src目录下,又创建了组件库目录
---src
|
|___ components
| |
| |____ui(组件库)
| |
| |____其它项目组件
|
|____pages
|
|____router
|
|____store
|
|......
---index.html
---vite.config.js
---package.json
tip:开发组件库package建议
组件在设计的时候应该要能够高度与项目集成,也要能够高度与项目解耦和,所以在设计的时候要要注意几点
-
不要和主体项目使用共同的pina来进行状态管理,可以使用轻量化的发布订阅库mitt,这是由vue内部自动集成的一个通信方式,相当于vue2中的eventBus
import mitt from "mitt" const emitter = mitt() export default emitter在组建中引入并且像eventBus那样使用就可以了
emitter.emit() emitter.on() -
组件开发的过程中引入的资源路径推荐使用当前组件库的相对路径进行引用,当然也可以在构建的vite的js配置中配置别名,使用相对路径的话可以减少不必要的麻烦
resolve:{ alias:{ '@':path.resolve(__dirname,"src","components","ui") //填入自己的组件库根目录 } } -
开发的时候最好提前规范好组件目录,这不是必须的,但是规范好有助于更好的理解和阅读代码
ui
- dist //构建之后的目录
- lib //可以存放一些css和其它静态资源
- src //源代码核心目录
- types // 类型目录
- utils //工具目录
- index.js //入口
- vite.config.js //vite配置
- package.json
- tsconfig.json //ts类型配置
- readme.md //文档
配置:
- 一份我当前的package中vite配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import path from 'path';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite';
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ArcoResolver()],
dts: 'src/auto-imports.d.ts', // 生成类型声明文件,确保类型正确
imports: ['vue', '@vueuse/head', '@vueuse/core'],
}),
vueJsx({
transformOn: true, // 支持 v-on 事件语法
}),
Components({
resolvers: [
ArcoResolver({
resolveIcons: true, // 确保解析 Arco 图标
sideEffect: true, // 移除副作用
})
]
})
],
build: {
outDir: 'dist', // 输出目录
lib: {
entry: path.resolve(__dirname, './index.js'), // 组件库入口文件
name: 'SoulTable', // 全局变量名称(适用于 UMD 格式)
fileName: (format) => `soul-table.${format}.js`, // 输出文件名
formats: ['es', 'umd', 'cjs'], // 输出格式
},
cssCodeSplit: true, // 分离 CSS 文件
rollupOptions: {
external: ['vue', '@vueuse/core', '@icon-park/vue-next', 'axios', 'vuedraggable', 'uuid'],
output: {
globals: {
vue: 'Vue',
'@vueuse/core': 'VueUse',
'@icon-park/vue-next': 'IconPark',
'axios': 'Axios',
'vuedraggable': 'VueDraggable',
'uuid': 'UUID'
},
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'index.css') {
return 'soul-table.css'; // 自定义样式文件名
}
return assetInfo.name;
},
// exports: 'named'
},
},
declaration: true, // 生成类型声明文件
declarationDir: './types', // 类型声明的输出目录
},
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env': JSON.stringify({}),
},
});
- tsconfig配置
{
"compilerOptions": {
"typeRoots": [
"./types",
"./node_modules/@types"
]
},
"include": [
"./**/*",
"types/soul-table.d.ts"
]
}
- package.json配置
{
"name": "soul-table",
"version": "0.6.5",
"description": "无需编写多余代码,直接通过可视化绑定数据的方式生成表格和表单数据,快捷方便",
"main": "dist/soul-table.cjs.js",
"module": "dist/soul-table.es.js",
"type": "module",
"exports": {
".": {
"import": "./dist/soul-table.es.js",
"require": "./dist/soul-table.cjs.js"
},
"./style.css": "./dist/soul-table.css"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "vite build --config ./build-soul-table.js"
},
"repository": {
"type": "git",
"url": "https://gitee.com/d718781500/soul-table"
},
"keywords": [
"js",
"vue",
"jsx",
"low-code"
],
"peerDependencies": {
"@arco-design/web-vue": "^2.55.3",
"@he-tree/vue": "^2.8.6",
"@icon-park/vue-next": "^1.4.2",
"@vitejs/plugin-legacy": "^5.4.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^9.0.0",
"@vueuse/core": "^10.11.0",
"@vueuse/head": "^2.0.0",
"axios": "^1.7.2",
"sass": "^1.77.6",
"unplugin-auto-import": "^0.17.6",
"unplugin-vue-components": "^0.27.0",
"uuid": "^10.0.0",
"vite": "^5.2.8",
"vue": "~3.4.0",
"vuedraggable": "^4.1.0"
},
"author": "没雨溪",
"license": "ISC"
}
我在package.json中指定了构建这些代码的配置文件,使用--config来进行指定,其中./build-soul-table.js就是我的vite的组件库配置
"scripts": {
"build": "vite build --config ./build-soul-table.js"
},
peerDependencies解释:这个是用于定义当前组件库依赖的外部模块的版本,叫做对等依赖,简单来说就是当前开发的组件库package和外部项目的依赖关系,这里比如定义了vue的版本是3.4.21.但是外部项目的版本是3.0.0那么npm安装依赖的时候会提示问题,这个是必要的,举个例子来说
当前开发的
ui组件package中使用了defineModel这样的api,这个api是vue的3.4+版本具有的特性,但是外部项目的vue版本却是3.0.0,是无法使用我们ui组件库package中的defineModel这样的api的,所以需要通过peerDependencies对等依赖来约束外部项目的依赖,所以我在代码中使用了~来约束主版本和次版本
问题一:
在打包的时候选择了es umd cjs三中包的构建,但是在构建完成之后,使用npm的link进行调试,发现一直报一个错误
SyntaxError: The requested module '/node_modules/.vite/deps/vue.js?v=5b530aa5' does not provide an export named 'default' at )
这是一条让人崩溃的错误,耗费了我比较长的时间
在我的理解我使用的vue3都是name export这种形式,为什么构建之后会出现直接从vue的default引入内容,我检查了构建之后的es文件的代码发现一行
import QR,{ ref as j, reactive as ze, inject as Ae, computed as d, getCurrentInstance as Zn, cloneVNode as xl, defineComponent as Y, watch as _e, onMounted as We, onUnmounted as _n, openBlock as P, createElementBlock as D, normalizeClass as M, renderSlot as ce, normalizeStyle as ye, createElementVNode as H, resolveComponent as ve, createBlock as ae, createCommentVNode as oe, toRef as Kn, toRefs as Ee, createVNode as f, mergeProps as Ce, createTextVNode as ke, nextTick as He, provide as ht, Fragment as Me, createSlots as Vl, withCtx as ee, withModifiers as Gt, onUpdated as on, onBeforeUnmount as gt, readonly as pc, onDeactivated as mc, Teleport as Tl, Transition as jt, withDirectives as Pt, vShow as Et, resolveDynamicComponent as rt, toDisplayString as je, renderList as st, createStaticVNode as hc, TransitionGroup as Cs, watchEffect as mn, isVNode as an, render as hn, h as un, normalizeProps as gc, unref as K, useModel as mt, mergeModels as Ft, isRef as lt, pushScopeId as wn, popScopeId as kn, markRaw as St } from "vue";
其它的api都是正确的,唯独这个所谓的QR当然这是构建之后混淆的名字,我们都知道vue3是没有默认导出的,也就是所谓的export default,只有vue2可以,所以构建之后从vue3引入一个叫做QR的模块必然是有问题的,所以导致package构建之后一直报错
解决问题的思路:
- 梳理每个文件的代码,检查是否有api直接使用了
import xxx from "vue"这种写法- 如果
1没有问题,那么关键来了,在入口文件index.js里面,单独引入UI库中的每一个独立组件,每引入一个进行一次build,检查构建之后的es的第一行代码是否有默认导入问题的来源:
最终在使用2的方法,耗费1个小时的循环build=>检查=>导入=>build=>检查之后找到了罪魁祸首,这个问题的根源是拖拽库
vuedraggable,虽然我使用的是4.x的版本,就是因为这个库,导致构建之后一直有内容从vue里面默认导入解决方法:
直接让
vuedraggable不再参与构建,而是让它变成外部依赖,在当前ui库的package中的vite配置文件中(前文提到的:build-soul-table.js) 加入external属性
build :{
rollupOptions:{
external:['vuedraggable'] //关键配置
},
output:{
globals:{ //配置UMD的全局变量名
vuedraggable:'vuedraggable'
}
}
}
再次构建,这个问题就会消失了
问题二:
构建之后又提示错误,这个问题很诡异,明明使用的是vue,怎么会出现React这样的字眼
Uncaught (in promise) ReferenceError: React is not defined
原因:
由于我在vue中使用了JSX,而jsx默认将React作为上下文,所以出现这个问题,说白了组件使用了 JSX 语法,但未正确处理 JSX 转换的依赖。
解决方案:
在
vite配置文件中(你当前的package包,就是自己的Ui库),安装并且引入@vitejs/plugin-vue-jsx
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import path from 'path';
export default defineConfig({
plugins: [
vue(),
vueJsx(), // 添加 JSX 支持
]
})
问题三
解决了问题一二之后,又出现了新的问题,打包完成引入使用的时候提示错误
Uncaught ReferenceError: ref is not defined
会很疑惑,为什么明明开发的时候不会有这个问题,打包构建之后出现这个问题呢
原因:
因为我在开发的时候使用了
unplugin-auto-import,所有的vue的api都没有在代码中显式引入,所以出现这个问题解决方案:
对等的配置unplugin-auto-import,找到自己的UI库package中的
vite配置,安装import AutoImport from 'unplugin-auto-import/vite'并且引入,把自己开发的时候所有被自动导入的库都配置进来
import AutoImport from 'unplugin-auto-import/vite'
//...省略代码
export default defineConfig({
plugins: [
vue(),
vueJsx(), // 添加 JSX 支持
AutoImport({
imports: ['vue', '@vueuse/head', '@vueuse/core'], //解决问题的代码
}),
]
})
问题四:
构建之后,arco-design组件库组件没有被解析的问题,会提示警告,各种组件没有被注册
原因:
其实和问题三是同一个问题,主要也是
arco-design官方推荐使用自动导入解决方案:
不需要显式引入的方式,所以增加下面的配置就可以了,注意,主项目需要安装
unplugin-auto-import/vite,unplugin-vue-components/resolvers,unplugin-vue-components/vite还有arco-design
import AutoImport from 'unplugin-auto-import/vite'
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
//...省略代码
export default defineConfig({
plugins: [
vue(),
vueJsx(), // 添加 JSX 支持
AutoImport({
imports: ['vue', '@vueuse/head', '@vueuse/core'], //解决问题的代码
resolvers: [ArcoResolver()],
}),
Components({
resolvers: [
ArcoResolver({
resolveIcons: true, // 确保解析 Arco 图标
sideEffect: true, // 移除副作用
})
]
})
]
})
总结:
以上是我开发过程中遇到的一些问题,暂时先记录这么多