前言
前边我们已经介绍了十个组件的基本实现,包括Button基础交互组件、Collapse展示组件、Dropdown下拉菜单导航组件、Message反馈组件以及Form表单组件,后续组件将会逐步更新。那么既然实现了这些组件,怎么样才能让用户看到或者是使用呢?那么这个时候就应该考虑到组件的集成和部署,我们需要将这些组件打包成一个可供用户下载的库,或者是集成到现有的框架或者系统中。
打包类型
在打包之前,先要确定一下打包成什么样的类型,是ES Moudle、Common Js、或者是AMD、CMD等其他类型,那么就要分析下各种模块类型的优缺点吧:
- ES Moudle:
- 优点:
- 支持代码分割和按需加载,减少首次加载时间
- 原生支持,无需额外的打包工具或插件
- 可以与Webpack、Rollup等现代打包工具无缝集成
- 缺点:
- 在Node.js环境中,需要较新的版本才能支持ES Modules
- 对于不支持ES Modules的旧浏览器,需要使用构建工具进行转换
- 优点:
- Common Js:
- 优点:
- 在Node.js环境中广泛使用,无需额外配置
- 可以与Webpack等打包工具配合使用
- 缺点:
- 不支持代码分割和按需加载。
- 对于浏览器环境,需要额外的转换步骤
- 优点:
- UMD:
- 优点:
- 兼容性好,适用于各种环境
- 易于通过
<script>
标签直接引入使用
- 缺点:
- 打包后的文件通常较大,因为需要包含所有可能的模块加载逻辑
- 不利于代码分割和按需加载
- 优点:
- AMD: 好像有点过时了?
基于以上分析,我们知道 Common Js是没法在浏览器中直接运行的,而ES Moudle和UMD是可以的,并且现在构建项目一般都会用到webpack或Vite等构建工具,对于不支持ES Moudle的旧浏览器也不用担心。那么我们的打包模块就暂定ES Moulde和UMD两种格式。
Vue的插件系统
确定完打包类型,不着急去打包,先来看一下 ,那么什么是插件呢?简单来说就是:一段给Vue实例添加全局功能的代码。那么怎样写插件呢?如下:
自定义定义一个插件:
const myPlugin = {
install(app, options) {
// 配置此应用
console.log("插件被注册了")
}
}
使用插件:
import { createApp } from 'vue'
const app = createApp(App)
app.use(myPlugin)
所以插件的格式就是一个对象暴露出一个install
方法或者一个function
,那么可以在插件上做什么呢?一般来说可以做以下几种事情:
- 通过 和 注册一到多个全局组件或自定义指令
- 通过 使一个资源进整个应用
- 向 中添加一些全局实例属性或方法
很显然,我们这里需要用插件来注册我们的组件库组件。
注册Button组件
这里以Button组件为例,创建一个入口文件用于将Button组件注册到全局进行使用:
// Button/index.ts
import type { App } from 'vue'
import Button from './Button.vue'
// Button本身就是一个object,这里可以直接添加install方法
Button.install = (app: App) => {
app.component(Button.name!, Button)
}
export default Button
// 将所有的类型导入进来
export * from './types'
这样你可以在全局单独注册Button以使用,其他组件类似,不再一一列出
全局注册所有组件
如果想全局注册所有组件,可以将组件保存到一个数组里,然后遍历注册,这里仅以两个组件为例:
// src/index.ts
import type { App } from 'vue'
import Button from '@/components/Button'
import Collapse, { CollapseItem } from '@/components/Collapse'
import './styles/index.css'
const components = [
Button,
Collapse,
CollapseItem,
]
// 全局注册所组件
const install = (app: App) => {
components.forEach((compoment) => {
app.component(compoment.name!, compoment)
})
}
export {
Button,
Collapse,
CollapseItem
}
export default {
install
}
其他组件也类似,不过需要注意的是,除了组件,其他用到的东西也要引入,否则打包的时候会丢失,比如样式;另外,Message组件创建的方式是调用函数,所以用到的函数也要引进来,方法如上。
打包配置
万事俱备,只欠打包,接下来就来看一下打包配置吧。这里用到的是Rollup,当然,不需要重新安装Rollup了,因为Vite本身就集成了Rollup。因为要打包成两种格式,所以需要分别配置ES Module和UMD模式的打包配置。
ES Module
仅展示部分配置,其他配置与vite.config.js差别不大:
// vite.es.config.js
build: {
outDir: 'dist/es',
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'YvElement',
fileName: 'yv-element',
formats: ['es']
},
rollupOptions: {
external: [
'vue',
'@fortawesome/fontawesome-svg-core',
'@fortawesome/free-solid-svg-icons',
'@fortawesome/vue-fontawesome',
'async-validator',
'@popperjs/core'
],
output: {
assetFileNames: (chunkInfo) => {
if (chunkInfo.name === 'style.css') {
return 'index.css'
}
return chunkInfo.name as string
}
}
}
}
其中,outDir为输出文件夹;name为暴露的全局变量,且在formats包含'umd'
或'iife'
时是必须的;fileName为输出包的文件名;formats即表示格式,这里就是es格式。
rollupOptions选项中,external
表示在打包时不想打包进去的文件,这些将作为外部依赖来处理; assetFileNames函数用于自定义 asset 文件名生成,这里style.css
在打包时生成名为index.css
。
UMD
仅展示部分配置,其他配置与vite.config.js差别不大:
build: {
outDir: 'dist/umd',
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'YvElement',
fileName: 'yv-element',
formats: ['umd']
},
rollupOptions: {
external: ['vue'],
output: {
exports: 'named',
globals: {
vue: 'Vue'
},
assetFileNames: (chunkInfo) => {
if (chunkInfo.name === 'style.css') {
return 'index.css'
}
return chunkInfo.name as string
}
}
}
}
这个配置中,exports: 'named'
表示'named'
表示使用命名导出(named exports),这意味着 Rollup 会保留源代码中的命名导出,并在生成的 UMD 模块中使用它们。
vite.config.json
再来看一下vite.config.json的配置:
import dts from 'vite-plugin-dts'
export default defineConfig({
plugins: [
vue(),
vueJsx(),
dts({
tsconfigPath: './tsconfig.build.json'
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'YvElement',
fileName: 'yv-element'
},
rollupOptions: {
external: [
'vue',
'@fortawesome/fontawesome-svg-core',
'@fortawesome/free-solid-svg-icons',
'@fortawesome/vue-fontawesome'
],
output: {
exports: 'named',
globals: {
vue: 'Vue'
},
assetFileNames: (chunkInfo) => {
if (chunkInfo.name === 'style.css') {
return 'index.css'
}
return chunkInfo.name as string
}
}
}
}
})
这里需要一个第三方的插件 ,一款用于在 中从 .ts(x)
或 .vue
源文件生成类型文件(*.d.ts
)的 Vite 插件,详情见官方文档。
这里还需要配置你要处理的文件:
// tsconfig.build.json
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["src/index.ts", "src/components/**/*", "src/hooks/**/*"],
"exclude": ["src/components/**/__test__/*"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"jsx": "preserve"
},
"references": [
{
"path": "./tsconfig.json"
}
]
}
package.json
然后就是配置package.json中相关的打包发布信息了,这里仅展示关键部分:
"type": "module",
"private": false,
"homepage": "https://yv-ui.y-y-y.online",
"repository": {
"type": "git",
"url": "https://github.com/xiaoniu666888/yv-ui"
},
"files": [
"dist"
],
"sideEffects": [
"dist/index.css"
],
"main": "./dist/umd/yv-element.umd.cjs",
"module": "./dist/es/yv-element.js",
"types": "./dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/es/yv-element.js",
"require": "./dist/umd/yv-element.umd.cjs"
},
"./dist/": {
"import": "./dist/",
"require": "./dist/"
}
}
比较多,不过从键名也能知道是什么意思,简单挑几个说明一下:
"type": "module"
: 指示这个文件是一个 ES 模块"private": false
: 表示包是公开的(npm发布私有包好像是要收费的)files
: 当包被发布时,应包含哪些文件,确保只发布必要的文件"sideEffects": [ "dist/index.css" ]
:这意味着 "dist/index.css" 文件可能有副作用,不应该被 tree shaking 掉"main": "./dist/umd/yv-element.umd.cjs"
: 当使用 CommonJS 的require
方法时,这是包的入口点"module": "./dist/es/yv-element.js"
: 当使用 ES6+ 的import
语句时,这是包的入口点"types": "./dist/types/index.d.ts"
: TypeScript 的类型定义文件的位置\"exports"
: 定义包的导出映射,允许更精细地控制当使用 ES6+import
或 CommonJSrequire
时应解析哪个文件"./dist/"
: 表示允许从 "./dist/" 路径直接导入或 require 文件
另外,还有其他配置:
"peerDependencies": {
"vue": "^3.4.21"
},
peerDependencies
字段用于指定与当前包一起使用的其他包的版本。这些依赖项不是自动安装的,而是由使用当前包的项目来管理和安装的,也就是说在使用该组件库的时候,核心依赖库Vue
必须先下载安装,组件库不能脱离Vue
而被单独依赖并引用。
脚本
再来看一下打包时的脚本命令:
"build": "npm run build-only",
"build-only": "run-p build-es build-umd",
"build-umd": "vite build --config vite.umd.config.ts",
"build-es": "vite build --config vite.es.config.ts",
"prepublishOnly": "npm run build"
"build" :
- 这个命令是主要的构建命令,当你运行
npm run build
时,它会执行两个子命令:build-only
和move-style
。
- 这个命令是主要的构建命令,当你运行
"build-only" :
-
build-es
和build-umd
同时开始执行
-
"build-umd" 和 "build-es" :
build-umd
使用vite.umd.config.ts
作为配置文件build-es
使用vite.es.config.ts
作为配置文件
"prepublishOnly" :
- 这是一个 npm 生命周期钩子,当运行
npm publish
命令时,prepublishOnly
会在发布之前执行 - 这里,设置为运行
npm run build
,也就是说在发布包之前,它会先执行构建过程,这样就不用手动执行打包命令了
- 这是一个 npm 生命周期钩子,当运行
打包发布
最后,完成上述配置之后,终于可以打包发布了,执行命令:
npm publish --access public
打包之前需要注册npm账号然后登录,这里就不再赘述了。
总结
本文主要介绍对组件库的打包发布,需要注意的是Vue的插件系统,利用插件全局注册所有组件,然后配置打包文件,这个过程较为繁琐,要注意最终会打包成两种格式,ES Module 和 UMD 格式,需要分别配置打包文件,最后注意在发布到npm上的时候要将包设置为公开的。后续组件将会逐渐更新,最近还在找实习,只能暂停了,呜呜呜~
最后,这里是组件库的使用文档:,这里是仓库地址:,以上仅为本菜鸟的拙见,如有失误的地方,还请大佬评论区批评指正。