本文将介绍组件库在打包发布后, 在实际的项目中使用是否可行,以及如何进行验证
目录
为什么要验证可行性
当我们打算使用一项新技术或新的库时, 最先要做的就是验证可行性; 因为对于新的外部依赖, 我们无法确定是否符合预期, 以及是否会带来新的问题, 进行一次最小规模的验证, 可以 尽早的暴露问题
打包
我们的项目是使用 vite 进行搭建的, vite 提供了 library mode 以支持将项目中的部分文件进行单独打包, 并构建为库以供其他项目使用
TypeScript 配置
# 引入生成 .d.ts 文件的插件pnpm i -D vite-plugin-dtstsconfig.json:
{ "compilerOptions": { // ... "declaration": true, "outDir": "dist", }}创建 src/lib.ts 作为组件库的入口文件:
export * from './components'我们的项目是使用 ts 编写的, 为了让我们的组件库可以支持 ts 项目, 我们需要使用 vite-plugin-dts 将 .d.ts 文件生成到 dist 目录下
vite.config.ts:
import dts from 'vite-plugin-dts'
export default defineConfig({ // ... plugins: [ // 用于生成 `d.ts` 文件, refer https://github.com/qmhc/vite-plugin-dts/blob/HEAD/README.zh-CN.md dts({ tsconfigPath: './tsconfig.lib.json' }) ], build: { lib: { name: 'hyosan-chat', entry: ['src/lib.ts'], fileName: 'hyosan-chat', }, },})创建 tsconfig.lib.json:
{ "extends": "./tsconfig.json", // 继承已有的 tsconfig.json "include": [ // 添加需要在打包时生产 .d.ts 文件的文件 "./src/lib.ts", "./src/components/**/*.ts", ]}TIP为什么要额外创建一个
tsconfig.json?在打包时,
vite-plugin-dts会将tsconfig.json中的include中的文件进行打包(这里指生成dts), 但tsconfig.json中include包含了项目中的所有文件, 也包括了不应该出现在dist目录中的文件, 但我们不能修改tsconfig.json, 因为它适用于整个项目的开发环境, 所以我们需要创建单独的tsconfig.lib.json用于生成d.ts也就是说,
tsconfig.json用于开发环境,tsconfig.lib.json用于打包时生成d.ts
我们将 src/lib.ts 中导出的组件作为整个组件库对外提供的组件, 并通过 vite-plugin-dts 生成了类型描述文件
package.json 配置
当我们的项目作为组件库供外部使用时, 需要在 package.json 中声明 组件库的基本信息和对外提供的文件:
{ "description": "A library of web components for AI conversations based on lit and shoelace", "keywords": ["hyosan-chat", "Lit", "Shoelace", "AI", "chat", "Web Components", "web chat"], "author": { "name": "Ryan" }, "license": "MIT", "version": "0.0.0", "version": "0.0.1", "private": true, "files": [ "dist" ], "main": "./dist/hyosan-chat.umd.cjs", "module": "./dist/hyosan-chat.js", "exports": { ".": { "import": "./dist/hyosan-chat.js", "require": "./dist/hyosan-chat.umd.cjs" } }, "scripts": { "build:pack": "pnpm run build && pnpm pack", },}build & pack
修改完打包所需的配置后, 我们就可以使用 pnpm run build 进行打包了:
# 打包pnpm run build
# 打包并生成 .tgz 文件pnpm run build:packlsd --tree dist/ dist├── components│ ├── hyosan-chat.d.ts│ └── index.d.ts├── hyosan-chat.js├── hyosan-chat.umd.cjs├── lib.d.ts└── vite.svg我们观察一下打包后的产物:
| 文件 | 说明 |
|---|---|
components/hyosan-chat.d.ts | 对话组件 |
components/index.d.ts | 组件库中包含的组件的入口文件 |
hyosan-chat.js | es 打包格式产物 |
hyosan-chat.umd.cjs | umd 打包格式产物 |
lib.d.ts | 组件库的入口文件 |
vite.svg | vite logo |
- 其中
vite.svg不应该出现在dist目录中, 因此我们将其删除
rm public/vite.svglib.d.ts是组件库的入口文件的类型描述文件, 我们需要在package.json中声明:
{ "types": "./dist/lib.d.ts" "exports": { ".": { "types": "./dist/lib.d.ts", "import": "./dist/hyosan-chat.js", "require": "./dist/hyosan-chat.umd.cjs" } },}dist / hyosan-chat-0.0.1.tgz 文件作为打包后的产物, 不应该提交到仓库中, 我们将其添加到 .gitignore 中:
disthyosan-chat-*.tgz验证
打包完成后, 我们需要创建一个新的 vue 项目来引入组件库进行测试:
pnpm create vite.../1954f95e4b3-1078 | +1 +.../1954f95e4b3-1078 | Progress: resolved 1, reused 0, downloaded 1, added 1, done│◇ Project name:│ vite-project│◇ Select a framework:│ Vue│◇ Select a variant:│ TypeScript│◇ Scaffolding project in /Users/xxx/projects/vite-project...│└ Done. Now run:
cd vite-project pnpm install pnpm run devcd vite-projectpnpm i && pnpm i ./hyosan-chat/hyosan-chat-0.0.1.tgzcode .我们使用 vscode 打开项目, 检查有无 报错 / 引入失败 / 类型缺失 等问题:

在 vscode 中打开 HelloWorrld.vue 尝试引入组件库并添加 <hyosan-chat> 组件, 发现组件可以成功引入, 也有正确的类型定义
下面我们启动项目查看页面显示是否正常:
pnpm run dev
页面没有显示出组件, 这里的报错(Class constructor Y cannot be invoked without 'new')是因为 vue 把它当做了 vue 组件进行了渲染, 但其实它应该被当做 自定义元素, 通过查看 在 vue 中使用自定义元素, 发现应该在 vite.config.ts 中添加 isCustomElement:
import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement: tag => tag.includes('hyosan-') } } }) ], server: { port: 35195, }})
添加 isCustomElement 后, 组件渲染出来了, 但是样式没有, 这是因为我们在搭建项目引入 shoelace 时, 使用最简单的 <link> cdn URL 的方式引入了 shoelace 的样式, 在新创建的项目中并没有这个 <link>, 我们为了进行验证, 先引入 <link>, 在后续文章中我们会优化引入方式
index.html:
<link rel="stylesheet" media="(prefers-color-scheme:light)" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.0/cdn/themes/light.css"/><link rel="stylesheet" media="(prefers-color-scheme:dark)" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.0/cdn/themes/dark.css" onload="document.documentElement.classList.add('sl-theme-dark');"/>
再次查看页面, 组件正常渲染, 接下来我们测试一下 v-model 是否可用:
<input v-model="message" /><hyosan-chat></hyosan-chat><hyosan-chat :message="message"></hyosan-chat>const message = ref('message')
在 input 中修改 message, 页面中的 hyosan-chat 组件也会同步修改
看起来一切正常! 🎉 但在我们现在依然有几个需要解决的问题:
- 组件的样式现在依然使用的是
<link>cdn的方式实现, 但我们的用户使用环境可能是内网, 无法访问cdn的资源, 因此我们需要优化引入方式, 改为 在组件库中exportshoelace的样式 vscode中的HelloWorld.vue中编写<hyosan-chat>时, 没有关于hyosan-chat组件的类型提示(props/events), 也就是vscode无法识别hyosan-chat组件的类型; 得益于vue对自定义组件的支持, 我们可以 生成类型定义文件对vue GlobalComponents进行扩展, 这里我们可以使用一些第三方库(custom-element-vuejs-integration) 来实现, 详见 非 Vue Web Components 和 TypeScript- 组件库没有 提供多语言支持, 虽然
shoelace提供了 本地化 和 语言包文件
这些问题我们将在后续章节中一一解决