1254 字
6 分钟
使用 Lit 创建一个 AI 对话组件库 06 补录总结 篇
WARNING

本文是断档式记录, 不记录所有改动, 只作为总结, 具体更新可参考 源码

举例上一章发布过去了半个月, 没错, 这期间实在是太忙了, 所以这半个月的开发过程并没有做记录 😭, 不过好消息是这个项目顺利发布, 可以在 hyosan-chat - npm 查看这个 package, 或直接前往 github 阅读 源码

前言#

阅读本章内容需要你熟悉 Lit / Web Components / Shoelace

demo#

组件使用了 netlify 部署 demo 页面, 点击查看

夜间模式#

shoelace文档 中, 建议使用 link 标签的形式实现 light / dark 样式的引入和切换, 但作为一个组件库, 我们并不能要求所有项目都通过这种方式引入主题样式, 原因是这种方式太过原始, 我们并没有直接提供一种切换主题的方式, 而是让用户直接引入主题的 css, 既没有做到灵活简单, 也没有扩展性和响应式;

因此组件内部实现了动态切换主题样式的功能, 在 hyosan-chat 组件上提供一个主题名称参数 shoelaceTheme, 并通过 HyosanChatTheme class 来实现切换不同的主题

src/utils/HyosanChatTheme.ts:

import shoelaceDarkCss from '@shoelace-style/shoelace/dist/themes/dark.css?inline'
import shoelaceLightCss from '@shoelace-style/shoelace/dist/themes/light.css?inline'
/** shoelace 组件库的主题属性 */
export enum HyosanChatShoelaceTheme {
shoelaceLight = 'sl-theme-light',
shoelaceDark = 'sl-theme-dark',
}
/** shoelace 组件库的主题 css 内容 */
export const HyosanChatShoelaceThemes = {
/** light theme */
[HyosanChatShoelaceTheme.shoelaceLight]: shoelaceLightCss,
/** dark theme */
[HyosanChatShoelaceTheme.shoelaceDark]: shoelaceDarkCss,
}
/**
* 设置 hyosan-chat 主题
* @description 用于切换底层组件库 `shoelace` 的主题, `<hyosan-chat>` 组件会根据 `shoelaceTheme` 的值自动切换主题
*/
export class HyosanChatTheme {
static TAG_ATTRIBUTE = 'data-hyosan-chat-theme'
static getStyleElement() {
return document.querySelector(`style[${HyosanChatTheme.TAG_ATTRIBUTE}]`)
}
static setStyleElement(theme: HyosanChatShoelaceTheme) {
const styleElement = HyosanChatTheme.getStyleElement()
const cssText = HyosanChatShoelaceThemes[theme]
const cssNode = document.createTextNode(cssText)
if (styleElement) {
styleElement.innerHTML = ''
styleElement.appendChild(cssNode)
} else {
const style = document.createElement('style')
style.setAttribute(HyosanChatTheme.TAG_ATTRIBUTE, theme)
style.setAttribute('type', 'text/css')
style.appendChild(cssNode)
document.head.appendChild(style) // 将包含 shoelace 主题样式的标签插入到 head 中
}
HyosanChatTheme._updateThemeClass(theme)
}
/** 在根元素上切换 shoelace 主题类 */
private static _updateThemeClass(theme: HyosanChatShoelaceTheme) {
document.documentElement.classList.add(theme)
Object.values(HyosanChatShoelaceTheme).forEach((c) => {
console.log(c)
if (c !== theme) {
document.documentElement.classList.remove(c)
}
})
}
}

src/components/HyosanChat.ts:

import {
HyosanChatShoelaceTheme,
HyosanChatTheme,
} from '@/utils/HyosanChatTheme'
@customElement('hyosan-chat')
export class HyosanChat extends ShoelaceElement {
// ...
/** shoelace 主题, 可用于切换夜间模式 */
@property({ reflect: true })
shoelaceTheme: HyosanChatShoelaceTheme = HyosanChatShoelaceTheme.shoelaceLight
}

相关改动可参考: feat: 增加切换主题功能

框架适配#

最初在做调研时, 发现 web components 是一个 原生技术, 这就代表了它适用于所有框架, 包括 vue / react / angular / ..., 现在组件的基础功能开发完成, 是不是就意味着可以直接在使用这些框架的项目中使用呢? 是也不是

  • 自定义标签没有声明属性和事件
  • IDE 无法识别自定义标签
  • 各个框架的模板语法不同

一方面 web components 的确是原生技术, 它使用的技术 不依赖于任何框架, 可以直接使用; 但另一方面, 在一个现代化的前端开发环境中, TypeScript 是必不可少的一环, web components没有提供定义自定义标签类型的功能, 编辑器更不会知道当前项目有哪些自定义标签, 以及标签上有哪些属性, 而且更加复杂的是, 在不同的框架中 HTML 的语法都不一样, 例如 vue 的模板语法与 jsx 的模板语法不同, angular 的模板语法与前两者也有差别 …

为此社区出现了一个 custom-elements-manifest 的库, 用于为不同的框架提供 manifest 信息, 简而言之它可以 让编辑器和不同的框架准确的识别自定义标签

具体配置可参考: custom-elements-manifest.config.mjs

示例项目#

配置好不同框架的适配之后, 我们直接创建 demo 项目:

发布#

package.json:

{
"scripts": {
"npm:login": "pnpm login --registry=https://registry.npmjs.org",
"npm:publish": "pnpm run build:lib && pnpm publish --registry=https://registry.npmjs.org",
}
}

这里为什么要指定 registry 呢? 因为在国内的网络环境下, 一般会设置淘宝的 npm 镜像, 或内部私有镜像, 但在发布时是需要连接到官方源的

Terminal window
# 只需执行一次, 之后可以直接执行 pnpm run npm:publish
pnpm run npm:login
# 发布新版本包
pnpm run npm:publish

参考#

使用 Lit 创建一个 AI 对话组件库 06 补录总结 篇
http://blog.xiaban.run/posts/2025/hyosan-chat-06-bubble/
作者
Ryan
发布于
2025-03-18
许可协议
CC BY-NC-SA 4.0