Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/holyhigh2/compelem
A modern, reactive, fast, lightweight and flexible lib for building web components
https://github.com/holyhigh2/compelem
Last synced: 23 days ago
JSON representation
A modern, reactive, fast, lightweight and flexible lib for building web components
- Host: GitHub
- URL: https://github.com/holyhigh2/compelem
- Owner: holyhigh2
- License: mit
- Created: 2024-11-02T10:08:26.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2024-11-13T15:25:59.000Z (about 1 month ago)
- Last Synced: 2024-11-13T16:31:39.438Z (about 1 month ago)
- Language: JavaScript
- Size: 121 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# CompElem
一个现代化、响应式、快速、轻量的WebComponent开发库。为开发者提供丰富、灵活、可扩展的声明式接口## 概览
CompElem 基于 Class 进行构建,该模型允许开发者使用装饰器进行声明式编码,核心特性包括:
- 类JSX的原生模板系统
- 丰富的装饰器及指令
- 可选的生命周期
- 原生插槽系统
- 响应式域样式
- ...创建一个WebComponent总会从声明一个组件元素(CompElem子类)开始
```ts
const Slogan = ['complete', 'componentize', 'compact', 'companion']
@tag("page-test")
export class PageTest extends CompElem {
//////////////////////////////////// props
@prop arg:any@state colorR = Math.random() * 255 % 255 >> 0;
@state colorG = Math.random() * 255 % 255 >> 0;
@state colorB = Math.random() * 255 % 255 >> 0;
@state rotation = 0//////////////////////////////////// computed
@computed
get color() {
return `linear-gradient(90deg,rgb(${this.colorR},${this.colorG},${this.colorB}), rgb(${255 - this.colorR},${255 - this.colorG},${255 - this.colorB}));`
}//////////////////////////////////// watch
@watch('rotation')
function(nv:number) {
console.log(nv)
}//////////////////////////////////// styles
//静态样式
static get styles(): Array {
return [`:host{
font-size:16px;
}...`];
}
//动态样式
get css() {
return `h2,p,i,h3{
background-image:${this.color}
filter:hue-rotate(${this.rotation}deg)
}`
}@query('i[name="text"]')
text: HTMLElement
sloganIndex = 0//////////////////////////////////// lifecycles
mounted(): void {
setInterval(() => {
this.rotation += 1
}, 24);setInterval(() => {
this.text.classList.add('hide')
setTimeout(() => {
this.text.innerHTML = Slogan[this.sloganIndex % 4]
this.sloganIndex++
this.text.classList.remove('hide')
}, 500);
}, 5000);
}
render(): Template {
return html``
Welcome to
CompElem
A modern, reactive, fast and lightweight library
for building
Web Components
<c-element> ... </c-element>
${this.arg}
}
}
```
而后即可在HTML中直接使用,与使用一个原生元素如DIV没有任何区别
```html
```
当然,也可以直接嵌入其他UI库中只要引入编译后的js即可## APIs
- ### 视图模板
使用`render()`函数定义组件视图模板
```ts
render(): Template{
return html`Hello CompElem`
}
```
- ### 视图模板-属性表达式通过再视图模板中插入表达式可以实现动态视图,表达式通过在不同位置使用分为不同类型见(#### 指令类型)。其中属性表达式根据前缀分为
| 前缀 | 描述 | 示例 |
| ---- | ------------------------------------------------------------- | -------------------------------------- |
| @ | 事件属性,可用于任何标签 | `` |
| . | 参数属性,仅用于给组件标签传递参数 | `` |
| ? | 可选属性,用于 disabled/readonly 等 toggle 类属性 | `` |
| \* | 引用属性,表达式求值后才会设置该属性。常用于 SVG 相关属性设置 | `` |> 引用属性可通过属性参数进行格式转换,如
```ts
render(): Template{
return html`...`//
}
```支持格式包括:
- camel 驼峰式
- kebab 短横线
- snake 下划线- ### 样式
使用静态函数定义组件样式或全局样式(如弹框)
```ts
static get styles(): Array {
return [];
}
static get globalStyles(): Array {
return [];
}
```
对于需要动态控制 host 元素样式可以使用组件实例 getter
```ts
get css():string{
return `:host{
${this.border?'border: 1px solid rgb(var(--l-color-border-secondary)); ':''}
}`
}
```
- ### 属性属性是由组件外部提供参数的响应变量,可通过`@prop`注解定义
```ts
@prop({ type: Boolean }) loading = false;//显式定义属性类型
@prop round = true;//通过默认值自动推断属性类型
@prop({ type: [Boolean,String] }) round = true;//多种类型使用数组定义
@prop({ type: Array }) datalist: Array;//没有默认值必须显式指定属性类型
@prop({ type: [String, Number], sync: true }) //通过get/set设置属性
get value() {
return this.__innerValue ?? ''
}
set value(v: any) {
this.__innerValue = v
if (isNil(v)) {
this.__innerValue = '';
}
}
```属性可以在组件内修改但默认不会同步父组件,除非显式指定`sync`或自行 emit update 事件
全部注解参数见 `PropOption`> 在某些无法使用装饰器的场景中(如MixinClass),可以使用函数`makeProp(...)`定义prop
```ts
export function Loadable>(spuerClass: T) {
return class Loadable extends spuerClass {
//declare a prop
name: stringconstructor(...args: any[]) {
super()
//make the prop reactive
Decorator.call(this, prop, 'name', { type: String })
}
}
}
```- ### 状态
状态是仅由组件内部初始化的响应变量,可通过`@state`注解定义
```ts
@state hasLeft = false;//定义状态
@state({//自定义变化判断
hasChanged(nv: any[], ov: any[]) {
return isEqual(nv , ov)
}
}) private nodes: Array>;
```
状态仅能在组件内修改
全部注解参数见 `StateOption`
- ### 状态监视使用`@watch`注解可以对属性/状态进行变化监视
```ts
@prop width = "auto";@watch("width", { immediate: true })
watchWidth(nv: string, ov: string, sourceName: string) {
this.style.width = nv;
}
```对于同类属性共享处理逻辑的监视,可以批量处理
```ts
@watch(['height', 'minHeight', 'maxHeight'], { immediate: true })
watchHeight(nv: string, ov: string, sourceName: string) {
this.style[srcName] = nv;
}
```- ### 计算状态
`;
计算状态会缓存 return 结果,只有当内部使用的任意属性/状态发生变化时才会重新计算
使用`@computed`注解的 Getter,如
```ts
@computed
get hasHeader() {
return !isEmpty(this.slots.header) || !!this.header
}
```
- ### 节点引用
使用`@query/all`注解及`ref`属性
```ts
//query
@query('l-icon')
iconEl: HTMLElement
//ref
refNode: HTMLElement
```
```ts
divRef = createRef();
//视图片段
return html`
```
- ### 内置属性及函数
- `readonly` parentComponent 父组件引用,可能为空
- `readonly` renderRoot/renderRoots 渲染根元素/渲染根元素列表
- `readonly` shadowRoot 阴影DOM
- `readonly` slots 插槽元素映射
- `readonly` slotHooks 动态插槽钩子映射
- `readonly` styles 组件样式对象列表
- `readonly` attrs 组件特性
- `readonly` props 组件属性
- slotComponent 所在插槽组件
- on(evName: string, hook: (e: Event) => void) 在root元素上绑定事件
- emit(evName: string, arg: Record, options?: {event?: Event;bubbles?: boolean;composed?: boolean;}) 抛出自定义事件
- nextTick(cbk: () => void) 下一帧执行函数
- forceUpdate() 强制更新一次视图## 组件渲染流程
CompElem 组件既可以在 CompElem 环境内调用,也可以直接在原生环境调用,区别只是原生环境无法像组件传`递类型参数`。流程如下:
> 创建流程
| 功能 | | 生命周期 |
| ------------------------------------------------------------ | --- | ----------- |
| 1. 创建组件实例,完成类属性默认值设置(prop/state/...) | | |
| 2. 初始化类全局样式(仅一次)及 实例样式(产生 styles 数组) | | |
| 3. 创建 shadowRoot 并挂载组件样式 | | constructor |
| 4. 绑定 parentComponent | | connected |
| 5. 获取 attrs 及 parentProps 进行验证及初始化 props | | propsReady |
| 6. 注入 link 外部样式表 | | |
| 7. 渲染 render 及依赖绑定 | | render |
| 8. 绑定 renderRoot 及 renderRoots | | |
| 9. 执行 ref | | |
| 10. 执行 @query 注解 | | |
| 11. 执行 @watch(immediate) 注解 | | |
| 12. 执行 @event 注解 | | mounted |
| 13. 执行 slot filter / 动态 slot | | slotChange |> 更新流程【普通】
| 功能 | 生命周期 |
| ------------------------------ | ------------ |
| 1. 父组件 props 变更【或】 | propsReady |
| 1. 子组件 states 变更【或】 | |
| 2. 执行@watch 注解 | |
| 3. 合并变更内容并判断是否更新 | shouldUpdate |
| 4. 更新依赖域指令(非 render) | |
| 5. 更新动态 slot | |
| 6. 执行 ref | |
| 7. 执行@query 注解 | update |> 更新流程【强制】
| 功能 | 生命周期 |
| ------------------ | -------- |
| 1. forceUpdate | |
| 2. 渲染 render | render |
| 3. 执行@query 注解 | update |## 插槽 Slot
#### 定义插槽
使用原生 `` 标签来嵌入插槽,可以通过`node-filter`属性过滤插槽内容
```html
```
#### 插入节点(静态)
```html
默认插槽内容
命名插槽内容```
#### 插入节点(动态)
动态内容插入仅可在 CompElem 组件中编码,可以通过组件注入参数动态生成插槽内容
```ts
//仅可用于CompElem组件中
return html`
//动态内容通过slot指令定义 ${slot(
(args: Record) => html`
我是动态内容-${args.data.id}
`,
"content"
)}
`;
```在`slot`标签上注入参数
```html
```
**_注意_**,动态插槽必须关闭自动插槽,否则无效
```ts
static get autoSlot() {
return false;
}
```## 指令 Directive
指令用于分支/循环/动态插槽等结构及隐藏显示等
#### 指令类型
不同的指令类型限制了指令仅能用于对应的插入点
|指令|插入位置|描述|示例|
|-------|-------|-------|-------|
|ATTR|特性|可用于任何特性值之中,仅能插入一个|`attr="${xxx}"`|
|PROP|属性|可用于任何属性值之中,必须是组件标签,仅能插入一个|`.value="${xxx}"`|
|TEXT|文本|标签体内,可插入多个|`${xx1}${xx2}${...}`|
|CLASS|样式类|用于 class 属性值中,仅能插入一个|`class="a ${b}"`|
|STYLE|样式规则|用于 style 属性值中,仅能插入一个|`style="a:1;${b}"`|
|SLOT|插槽|与 TEXT 类似,但标签必须是组件||
|TAG|标签|直接插入在节点标签上|``|#### 内置指令
| 指令 | 类型 | 描述 | 示例 |
| ------- | --------- | ----------------------------------------------------------- | ------------------------------------------------------ |
| bind | TAG | 绑定属性/特性到标签上,根据标签类型及组件 prop 定义自动判断 | `` |
| show | TAG | 隐藏/显示标签(基于 display) | `` |
| model | TAG | 双向绑定 | `` |
| classes | CLASS | 绑定样式类属性,支持对象/数组/字符串。可以和静态字符混用 | `${forEach(ary,(item)=>html`...`)}<...` |
| ifTrue | TEXT/SLOT | 当条件为 true 时输出模板内容 | `...>${ifTrue(condition,()=>html`...`)}<...` |
| ifElse | TEXT/SLOT | 当条件为 true/false 时输出对应模板内容 | ` ...>${ifElse(condition,()=>html``,()=>html``)}<... ` |
| when | TEXT/SLOT | 多条件分支,支持 switch/ifelse 两种模式 | ` ...>${when(condition,{c1:()=>html``,c2:...})}<... ` |
| slot | SLOT | 动态插槽 | ` ...>${slot((args) => html``)}<... ` |## 装饰器 Decorator
- @state 定义组件内状态属性。可选参数{prop},可指定 propName 初始化值
- @prop 定义父组件参数,默认不可修改。可选参数{type,required,sync,getter,setter}
> 设置 getter/setter 后,该属性的`@watch` 将会失效
- @query/queryAll 定义 CssSelector 查询结果
- @tag 自定义组件的标签名
- @event 定义全局事件
- @watch 监控 state/prop 变更
- @computed 计算属性,仅在响应变量变更时更新缓存值> 装饰器还可通过函数方式进行调用,如
> ```ts
> //Decorator.call(class, decorator, fieldName, ...args)
> Decorator.call(this, prop, 'name', { type: String })
> ```
> **注意**,调用入口必须放在类的构造函数中## 事件
事件分为三类
1. 原生事件 —— `