导入Web Component
在使用一个Web Component前必须先定义它,而定义过程实际上是调用浏览器环境中的customElements函数
在入口文件(main.js或者index.js)直接导入即可
import 'some-component'
模块中的内容会自动执行 并注册自定义元素
ssr框架中要在确保存在浏览器环境的情况下 使用import('xx')动态导入
浏览器会在自定义元素注册完毕后重新解析已经存在的自定义元素
类型声明
拓展JSX.IntrinsicElements以确保类型正确
创建xxx.d.ts文件 或者修改某个已有的类型声明 加入这样的代码
namespace JSX {
interface IntrinsicElements {
'a-a': { a?: string }
}
}
然后就可以合法使用<a-a />组件了 它会接受一个可选的a参数 类型是string
如果想使用slot 还需要加上{ children?: any }
也可以通过已有类型进行拓展
namespace JSX {
interface IntrinsicElements extends SomeComponent {}
}
各个框架对于web compontnent的解析各不相同 可能通过js设置 也可能设置在html标签上 所以字符串最稳妥
命名空间相关
如果当前的JSX并不来自命名空间 则要这样写
// 以solidjs为例
import 'solid-js'
declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements extends SomeComponent {}
}
}
如果想创建一个Web Component库
//components/index.ts
import './a'
export interface SomeComponent {}
//components/a/index.tsx
declare module '../index' {
interface SomeComponent {
'a-a': xxx
}
}
然后可以导入components/index.ts 并用components下全部组件的类型拓展JSX.IntrinsicElements
如果需要自定义事件 还可以拓展GlobalEventHandlersEventMap类型 可以在addEventListener中得到代码提示
// 这里始终是global模块
declare global {
interface GlobalEventHandlersEventMap {
'some-event': CustomEvent<{a:1}>; // 类型参数是e.detail的类型
}
}
此外 还可以以当前框架的类型为基础 将props属性拓展
创建一个能够按需导入的库
上述方法是全量导入 也可以按需导入
// components/index.ts
export * from './a'
// components/a/index.ts
export {} // 不能省略 这是为了让本文件成为模块 否则ts不允许declare global
declare global {
namespace JSX {
interface IntrinsicElements {
'a-a': { a?: string }
}
}
}
此时 导入components/a.ts可以自动拓展jsx类型
这个文件的类型可能会被ts自动收集 不过开发中不影响
测试时 可以排除掉打包后的文件
如果JSX不来自全局命名空间 依旧可以在全局命名空间JSX拓展类型 但要同步到'solid-js'中
import 'solid-js'
declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements extends globalThis.JSX.IntrinsicElements {}
}
}
打包配置(vite)
import tailwindcss from '@tailwindcss/vite'
import { resolve } from 'path'
import { defineConfig } from 'vite'
import dtsPlugin from 'vite-plugin-dts'
import solidPlugin from 'vite-plugin-solid'
import pkg from './package.json'
const outDir = 'leafer-component'
const excludeDeps: any = Object.keys(pkg.dependencies) // 不把任何依赖打包 全采用peerDenpencies的形式
export default defineConfig({
plugins: [
eslintPlugin(),
solidPlugin(),
tailwindcss(),
dtsPlugin({
include: ['src/leafer-component'],
outDir,
}),
],
server: {
port: 3000,
host: '0.0.0.0',
},
build: {
target: 'esnext',
outDir,
lib: {
entry:{
index:'src/leafer-component/index.ts',
`leafer-box/index`:`src/leafer-component/leafer-box/index.tsx`
// 通过fs等方法构建entry
},
formats: ['es'],
name: 'LeaferComponent',
},
rollupOptions: {
external: excludeDeps,
},
},
})
package.json
参考(这里)[juejin.cn/post/721774…]
有关的小知识
使用solid-element创建web component
customElement('my-component', {someProp: 'one', otherProp: 'two'}, (props, { element }) => {
// ... Solid code
})
第二个参数是默认值 没在这里面提及的参数会被忽略
而组件函数的第二个参数的element属性可以取到创建的自定义元素本身