
本文将介绍组件库在打包发布后, 在实际的项目中使用是否可行,以及如何进行验证
目录
为什么要验证可行性
当我们打算使用一项新技术或新的库时, 最先要做的就是验证可行性; 因为对于新的外部依赖, 我们无法确定是否符合预期, 以及是否会带来新的问题, 进行一次最小规模的验证, 可以 尽早的暴露问题
打包
我们的项目是使用 vite 进行搭建的, vite
提供了 library mode 以支持将项目中的部分文件进行单独打包, 并构建为库以供其他项目使用
TypeScript 配置
# 引入生成 .d.ts 文件的插件pnpm i -D vite-plugin-dts
tsconfig.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:pack
lsd --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.svg
lib.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 dev
cd 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
的资源, 因此我们需要优化引入方式, 改为 在组件库中export
shoelace
的样式 vscode
中的HelloWorld.vue
中编写<hyosan-chat>
时, 没有关于hyosan-chat
组件的类型提示(props
/events
), 也就是vscode
无法识别hyosan-chat
组件的类型; 得益于vue
对自定义组件的支持, 我们可以 生成类型定义文件对vue GlobalComponents
进行扩展, 这里我们可以使用一些第三方库(custom-element-vuejs-integration) 来实现, 详见 非 Vue Web Components 和 TypeScript- 组件库没有 提供多语言支持, 虽然
shoelace
提供了 本地化 和 语言包文件
这些问题我们将在后续章节中一一解决