用 Unbuild 和 mkdist 打造你的第一个 Vue.js 组件库本文将带您探索构建 Vue.js 组件库的简化
TLDR: 本文将带您探索构建 Vue.js 组件库的简化流程,以及背后的设计理念。vue-library 示例库可供参考。
组件库是一组可重复使用的组件,可以用于多个项目。它可以帮助不同项目和团队共享资源,这些组件可以是低级通用组件(例如按钮、输入框和模态框),也可以是特定业务模块。简而言之,组件库是跨项目共享代码的有效方法,能够显著节省时间。
然而,使用 Vue.js 构建组件库并不像看起来那样容易,因为 Vue.js 的单文件组件 (SFC) 带来了挑战。在标准 TypeScript 项目中,通常使用 tsup 或 Vite 将代码转译为 JavaScript,并进行打包。
关键原则: 尽可能以原生方式交付包代码,让用户工具负责转译和优化。这是构建库时应牢记的原则,能极大地简化流程。
Vue.js SFC 的挑战:
让我们以一个使用可组合函数的组件为例:
<script lang="ts" setup>
import { useUser } from '../composables'
const { user } = useUser()
</script>
<template>
<div v-if="user">
{{ user.name }}
</div>
</template>
可组合函数代码如下:
import { ref } from 'vue'
export function useUser() {
const user = ref({ name: 'John Doe' })
return {
user
}
}
这个组件使用了另一个文件中的可组合函数,这看似简单,但它带来了一个显著的挑战。
项目的结构如下:
src/
components/
User.vue
composables/
useUser.ts
index.ts
index.ts
是库的入口文件,内容如下:
import User from './components/User.vue'
export { User }
export { useUser } from './composables/useUser'
package.json
包含一些 exports
配置:
{
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"main": "dist/index.mjs",
"types": "dist/index.d.ts"
}
类似标准 TypeScript 项目
注意: 包是一个包含所有项目代码的单个文件。它通过减少需要加载的文件数量来优化项目的加载速度。在创建 npm 包时,这个包不会进行转译(除了 TypeScript 到 JavaScript)或压缩。
如果尝试打包这个项目,dist
文件夹的结构如下:
dist/
index.cjs
检查 index.cjs
文件后,你会发现可组合函数 useUser
被内联了,但组件却被完全忽略了。如果打包工具运行成功,可能会出现类似 No loader is configured for ".vue" files
的错误。这意味着像 esbuild 或 tsc 这样的工具不知道如何处理 Vue 文件,这是正常的,因为 Vue 文件不是 JavaScript 文件。
问题很明显:我们需要一些配置来处理 Vue 文件。在网上搜索 "vue loader",因为我们使用了 Vue 文件,需要将它们转译为 JavaScript。
搜索结果中最顶端的是 Vue Loader,它是一个 Webpack 加载器。但是,我不打算使用 Webpack,因为 Vite 已经成为行业标准,在这种情况下没有理由使用 Webpack。
不过,在我们深入之前,让我们回顾一下我们的口号:尽可能以原生方式交付包代码,并遵循 KISS 原则。 使用 Webpack 来转译 Vue SFC 文件似乎违反了这个原则。
忽略 Vue 文件
为了不转译 .vue
文件,我们可以指示打包工具忽略它们。这可以使用 UnJS 生态系统中的 unbuild 工具来实现。
Unbuild 是一个简单但高度可配置的工具,因为它构建在 rollup 之上。
对于我们的小项目,我们可以直接进行尝试:
npx unbuild
不幸的是,它会因为与之前相同的原因而失败:
src/components/ShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)
但 unbuild 的潜力远不止此。
为了解决这个问题,让我们尝试显而易见的解决方案。在 index.ts
文件中,移除组件导出,只导出 TypeScript 文件:
export { useUser } from './composables/useUser'
现在,npx unbuild
命令可以正常运行:
➜ npx unbuild
ℹ Automatically detected entries: src/index [esm] [dts]
ℹ Building vue-library
ℹ Cleaning dist directory: ./dist
✔ Build succeeded for vue-library
dist/index.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)
Σ Total dist size (byte size): 531 B
这是一个好的开始,但我们的组件仍然在 src
文件夹中,无论你如何仔细搜索,它们都不会出现在 dist
文件夹中。
复制 Vue 文件
现在 .ts
文件已经正确处理了,我们可以尝试使用简单的 cp
命令将 .vue
文件复制到 dist
文件夹中。
cp -r src/components/ dist/components/
然后,我们在 package.json
中添加一个 exports
字段,告诉用户在哪里可以找到这些组件:
{
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
},
"./components/*": {
"import": "./dist/components/*.vue"
}
},
"main": "dist/index.mjs",
"types": "dist/index.d.ts"
}
看起来很有希望,不是吗?但事实并非如此。 😔
导入路径的困境
在我们的 Vue 组件中,useUser
可组合函数的导入方式如下:
<script lang="ts" setup>
import { useUser } from '../composables'
</script>
而我们的 dist
文件夹的结构如下:
dist/
components/
User.vue
index.d.mts
index.d.ts
index.mjs
你能找出问题所在吗?👀
组件无法使用,并会产生 Cannot find module '../composables' or its corresponding type declarations
错误,因为路径指向错误的位置。我们已经将所有 TypeScript 文件打包到一个名为 index.mjs
的文件中,完全丢失了项目的结构。
打包还是不打包?
从这里,我们有两个选择:
- 使用 Vite、插件和复杂的配置来打包 Vue 文件。
- 保留目录结构,并使用 mkdist 等工具对 TypeScript 文件进行文件到文件的转译(无包构建)。
最终的决定取决于你的具体需求,但我相信简洁是最好的方法。因此,让我们探索第二个选项。
目标是将 TypeScript 文件转换为 JavaScript 文件,同时保留 src
文件夹在 dist
文件夹中的原始结构,并且忽略 Vue 文件。
注意: 使用 <script setup lang="ts">
块,mkdist 会忽略 Vue 文件,以便 Vue 编译器生成运行时 props。否则,它会生成一个 .vue.d.ts
文件,为 Vue 组件提供类型。请记住,@vitejs/plugin-vue 和 Vite 本身就理解 TypeScript 脚本块。有关更多详细信息,请参阅mkdist#14 问题。
配置 Unbuild
没错,我们将配置 unbuild,因为它集成了 mkdist,使其非常易于使用。
首先,在项目的根目录中创建一个 build.config.ts
文件。Unbuild 会读取此配置文件,了解如何处理项目。它假设了一些默认值,并从 package.json
中推断出许多方面,但我们需要告诉它使用 mkdist。
import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
entries: ['./src/'],
declaration: true,
})
这是一个非常简单的配置文件。我很喜欢它。 entries
键告诉 unbuild 从哪个文件开始转译。但是,./src/
是一个目录,由末尾的 /
表示。有了这个提示,unbuild 会放弃默认的打包工具 rollup,转而使用 mkdist。declaration
键提示 unbuild 创建 TypeScript 声明文件(.d.ts
)。
我们也可以移除 package.json
中的 exports
字段,因为我们不再打包文件,并恢复 index.ts
文件中的组件导出。
让我们重新运行 npx unbuild
命令,见证奇迹。
➜ npx unbuild
ℹ Building vue-library
ℹ Cleaning dist directory: ./dist
✔ Build succeeded for vue-library
dist (total size: 600 B)
└─ dist/index.d.ts (108 B)
└─ dist/index.mjs (112 B)
└─ dist/composables/useUser.d.ts (79 B)
└─ dist/composables/useUser.mjs (124 B)
└─ dist/components/User.vue (177 B)
Σ Total dist size (byte size): 600 B
现在,dist
文件夹的结构如下:
dist/
components/
User.vue
composables/
useUser.d.ts
useUser.mjs
index.d.ts
index.mjs
这与 src
文件夹的结构一致,User.vue
组件现在可以在 dist
文件夹中使用。相对导入路径仍然有效,组件可以在任何 Vue 项目中使用。🥳
本地开发
许多教程到此为止,建议你已经准备好将包发布到 npm 了。但是,如果你看不到工作成果,如果你不能在构建过程中进行测试,如何才能创建一个复杂的库呢?
基本上,你无法做到。
让我们看看如何在本地项目中使用该库,为了简单起见,我们将使用同一个仓库。
我将展示使用 pnpm workspace 的最简单方法。
首先,在项目的根目录中创建一个 pnpm-workspace.yaml
文件:
packages:
- .
- playground
然后,在 playground
文件夹中创建一个新的 Vite 项目:
npx create-vite playground --template vue-ts
因为我们使用的是 pnpm workspace,所以要从项目的根目录安装依赖项:
pnpm install
注意: 此步骤不是必需的。你可以创建一个新的 Vite 项目并在其中安装依赖项。Pnpm 通过在根目录中安装所有工作区的依赖项来简化这个过程。一个简单的 pnpm install
命令会安装所有工作区的依赖项。
现在,我们可以在一个 "真实" 项目中使用这个 playground
库来测试我们的库。
例如,打开 src/App.vue
文件,导入 User
组件:
<script setup lang="ts">
import { User } from '../../src'
</script>
<template>
<User />
</template>
然后运行项目:
cd playground && pnpm dev
你将在浏览器中看到 User
组件显示出来。🎉 如此简单,却又如此强大。我几乎在构建的每个库中都使用这种方法,它非常有效。
发布到 npm
这一部分很简单,只要你理解了工作流程。
首先,给你的库命名。在本例中,我将使用 @barbapapazes/vue-library
。
接下来,在 npm 上创建一个帐户,并使用 npm login
命令登录。
然后,安装 changelogen 来生成变更日志,按照语义化版本控制和提交规范更新版本号,并预先填写 GitHub 版本发布信息。
pnpm i -D changeloggen
接下来,在 package.json
中添加两个脚本:
{
"scripts": {
"prepack": "unbuild",
"release": "changelogen --release && npm publish --access public && git push --follow-tags"
}
}
prepack
脚本在将包发布到 npm 之前运行 unbuild
命令。release
脚本生成变更日志,将包发布到 npm,并将标签推送到 GitHub。
注意: 你的项目应该在 GitHub 上才能使用 --release
选项。如果没有,请省略它,并在仓库中手动创建发布。
现在,使用以下命令将包发布到 npm:
npm run release
就这样!你已经将你的第一个 Vue.js 组件库发布到 npm 了。🚀
准备就绪
今天就到这里。我们已经涵盖了许多主题:
- 如何使用 TypeScript 构建 Vue.js 组件库。
- 如何在 TypeScript 项目中管理 Vue 文件。
- 如何使用 unbuild 来转译 TypeScript 文件,而不打包 Vue 文件。
- 如何使用 mkdist 来保留项目结构。
- 如何使用 pnpm workspace 在本地项目中测试库。
- 如何将库发布到 npm。
有关更详细和更复杂的示例,请查看 GitHub: vue-library,但这篇文章中解释的所有内容都来自我的实际经验。
我希望你喜欢本教程,并希望它能帮助你构建自己的 Vue.js 组件库。
转载自:https://juejin.cn/post/7426558944103055396