# twin-node
**Repository Path**: zhang_xiangm/twin-node
## Basic Information
- **Project Name**: twin-node
- **Description**: TwinNode是一个轻量级html文档构建库,提供声明式构建和扁平化配置两大核心功能。无虚拟DOM,零成本抽象,专注于高性能场景。
- **Primary Language**: JavaScript
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-08-10
- **Last Updated**: 2025-08-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# twin-node.js
## 简介
TwinNode是一个轻量级html文档构建库,提供声明式构建和扁平化配置两大核心功能。无虚拟DOM,专注于高性能场景。
## 安装
```console
npm i twin-node
```
## 快速上手
```javascript
//第一步:导入
import "twin-node" //或
//第二步:用以下标准语法解构出标签函数
const {table,thead,tbody,tr,th,td} = $$.tags
//第三步:声明式构建文档
const node = table(
thead(
tr(
th("学号"),th("姓名"),th("性别")
)
),
tbody(
tr(
td(1),td("张向明"),td("男"),
),
tr(
td(2),td("蒋海艳"),td("女"),
)
)
)
//第四步:挂载
const body = $$.q("body")
node.appendTo(body)
```
## 帮助文档
### 1. 概念
#### 1.1 孪生节点
孪生节点是在Node实例(以下统一称为真节点)上构建的一层代理,其实现原理是一个Proxy。孪生节点的目的是为了使用精简指令集来操作真节点的对象属性、标签属性、样式、事件,并提供链式写法。请看如下示例:
```javascript
TwinNode("div") //创建了一个div真节点,并返回其孪生节点
.attr("id","div1") //配置attribute
.class("my-div") //配置class
.css("color","red") //配置样式
.on("click",event=>console.log(event)) //配置事件
._title("标题") //更精简的配置attribute
.backgroundColor("green") //更精简的配置样式
.Mousedown(event=>console.log(event)) //更精简的配置事件
```
#### 1.2 TwinNode函数
TwinNode是一个用来构建孪生节点的函数,其简写形式为$$,以下全部用简写形式。此外TwinNode有一个使用非常频繁的静态方法TwinNode.q(selector),其作用是在document下搜索DOM节点,并包装为孪生节点。
``` javascript
let t1 = $$("div") //创建了一个div真节点,并返回其孪生节点
let t2 = $$(document.querySelector("div")) //将已经存在的真节点包装为孪生节点
let t3 = $$.q("div") //等效于$$(document.querySelector("div"))
```
#### 1.3 标签函数
标签函数是用来声明式构建孪生节点的函数,可以使用以下两种方式来获得标签函数:
```javascript
//1: 通过对象解构语法获得,要求标签函数名与要构建的DOM节点标签名完全一致
const {div,span} = $$.tags
//2: 通过TwinNode.defineTag获取,可自定义函数名
const myDiv = $$.defineTag("div")
```
声明式构建孪生节点示例
```javascript
const {table,tr,td} = $$.tags
let twinNode = table(
tr(
td("单元格11"),
td("单元格12"),
),
tr(
td("单元格21"),
td("单元格22"),
)
)
```
#### 1.4 解孪生
解孪生是指从孪生节点上获取其真节点的过程,通过孪生节点的下标0就可以完成解孪生
```javascript
const div = $$.tags.div //创建标签函数
const twinNode = div("内容") //创建孪生节点
const realNode = twinNode[0] //解孪生
```
#### 1.5 无参组件
所谓组件就是一个能够返回孪生节点或真节点的函数,以下是创建组件和复用组件的过程
```javascript
//定义函数既是创建组件
function component() {
return tbody(
tr(
td("内容"), td("内容"), td("内容")
),
tr(
td("内容"), td("内容"), td("内容")
)
)
}
//在构建复杂节点时复用组件
const node = table(
thead(
tr(
th("字段1"), th("字段2"), th("字段3")
)
),
component //等价与component(),框架会自动调用无参的函数组件
)
//挂载节点
const body = $$.q("body")
node.appendTo(body)
```
#### 1.6 有参组件
无论是无参组件还是有参组件,其本质都是一个能返回孪生节点或真节点的函数,唯一的不同点是有参的组件在使用时必须手动传参调用。示例如下:
```javascript
//定义组件
function component(data) {
return tbody(
data.map(item =>
tr(
td(item.id),
td(item.name),
td(item.sex === 1 ? "男" : "女")
)
)
)
}
//准备数据
const myFamily = [
{ id: 1, name: "张向明", sex: 1 },
{ id: 2, name: "蒋海艳", sex: 2 },
{ id: 3, name: "张泽珩", sex: 1 }
]
//使用组件
const node = table(
thead(
tr(
th("字段1"), th("字段2"), th("字段3")
)
),
component(myFamily) //调用组件函数并传入参数
)
//挂载节点
const body = $$.q("body")
node.appendTo(body)
```
#### 1.7 文档碎片
下滑线_是一个特殊的标签函数,其用途是创建一个文档碎片孪生节点,其真节点是一个DocumentFragment实例。使用方式如下:
```javascript
//创建文档碎片组件
function component() {
const {_} = $$.tags //解构出_函数
return _(
li("项目1"),
li("项目2"),
li("项目3")
)
}
//复用组件
const node = ul(
component
)
//挂载节点
const body = $$.q("body")
node.appendTo(body)
```
### 2. 标签函数参数详解
标签函数接收多个不同类型的参数,每种类型的参数,在构建过程中都有自己特定的含义,这些参数的表义与顺序无关,但文档结构与顺序有关。标签函数可无限嵌套调用,以实现复杂的文档构建。请看如下示例:
```javascript
const node = ul(
//以下空值将被忽略,不参与构建
null,undefined,"",
//如果参数是一个孪生节点,则会将其真节点追加到ul中
li("项目1"),
li("项目2"),
//如果参数是一个数组,将被自动展开,并追加到ul中
[li("项目3"),li("项目4")],
//如参数是一个函数,将会自动运行并将结果递归追加到当前节点
()=>li("项目5"),
//如果参数是一个正则表达式,则用来为ul配置id和class和其他属性
//#开始的片段将被解析成id,多个id取最后一个
//.开始的片段将被解析成className,多个className可叠加
//[]中的片段将被解析成attribute,特别注意中括号中的中划线一定要转义成\-
//此处唯一的目的是让问题变简单,如果是复杂的attribute(如style)推荐用object语法
/#ul5.class1.class2[attribute1][attribute2=2][attribute\3=3]/,
//如果参数是一个object,则用来配置ul的attribute、样式、事件
{
title: "标题",
style: { color: "red", fontSize: "20px" },
click: event => console.log(event.target)
},
//如果参数是一个已经存在的,则会复制其内容并追到到ul中
document.getElementByid("template1"),
//无限嵌套
li(
{id:"li1",class:"list-item"},
span("内容"),
span("内容")
),
//如参数是一个真Node,这个参数将被追加到ul中
//你几乎永远不会这样做,除非你正在编写复杂的通用库
document.getElementByid("node1")
)
```
### 3. 配置后置
第2节的文档构建过程太过复杂,twin-node提供了类似于jquery的链式调用来扁平化构建过程。请看如下示例:
```javascript
const node = ul(
li("我有id颜色").attr("id", "project16").css("color", "red"),
li("class1").class("class1"),
li("点我").on("click",event=>alert(event.target))
)
```
### 4. 与SVG集成
普通的标签函数从TwinNode.tags中解构,而svg标签函数需要从TwinNode.svgTags中解构,请看如下示例:
```javascript
const { svg, path } = $$.svgTags;
const node = svg(
{ width: 100, height: 100 },
path(
{ d: "M0,0 L100,100", stroke: "red", "stroke-width": 1 }
)
)
const body = $$.q("body")
node.appendTo(body)
```
### 5. 与WebComponent集成
解构webcomponent标签函数时,函数名的下划线对应是标签的中划线,请看如下示例:
```javascript
//定义webcomponent组件
customElements.define(
"my-div",
class MyDiv extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = "这是一个webcomponent组件
";
}
}
)
//解构标签函数
const { my_div } = $$.tags;
//构建文档
const node = div(
my_div("这是组件的内容")
)
//挂载
const body = $$.q("body")
node.appendTo(body)
```
### 6. TwinNode静态函数
|函数 |功能 |
|--------------------------|-----------------------------|
|$$.q(selector) |在document下查询DOM节点,返回其孪生节点 |
|$$.qa(selector) |在document下查询DOM节点集合,返回孪生节点数组 |
|$$.defineTag(tagName) |定义一个标签函数 |
|$$.defineSvgTag(tagName) |定义一个SVG标签函数 |
### 7. TwinNode静态属性
|函数 |功能 |
|-------------------|-----------------------------|
|$$.tags |标签函数大全 |
|$$.svgTags |SVG标签函数大全 |
### 8. TwinNode实例属性
|属性名 |功能 |
|----------------------|-------------------- |
|twinNode[0] |获取真节点|
|twinNode[-1] |获取真节点的shadowRoot的孪生节点|
### 9. TwinNode实例函数
|函数 |功能 |
|-----------------------------|--------------------|
|q(selector) |用查询表达式查询子节点,返回子节点的孪生节点|
|qa(selector) |替代querySelectorAll|
|children() |获取孪生节点的直接子节点|
|attr(name,value) |获取或设置真节点的attribute|
|attr(name,false) |移除真节点的attribute|
|css(name,value) |获取或设置真节点的样式|
|var(name,value) |为真节点配置css变量|
|class(name,value) |为真节点添加类名|
|class(name,false) |移除真节点的类名|
|on(type,...args) |为真节点配置事件|
|off(type,...args) |卸载真节点的事件|
|prop(name,value) |获取或设置真节点的property|
|do(name,...ages) |调用真节点的函数,并返回本孪生节点|
|html(html) |获取或设置真节点的innerHTML|
|text(text) |获取或设置真节点的innerText|
|value(value) |获取或设置真节点的value|
|rect() |getBoundingClientRect()的结果|
|parent() |获取父节点的孪生节点|
|next() |获取下一个孪生节点|
|next(node) |在本节点之后插入一个节点|
|previous() |获取上一个孪生节点|
|up() |向上移动|
|down() |向下移动|
|previous(node) |在本节点之前插入一个节点|
|append(child) |追加子节点|
|prepend(child) |在首位插入子节点|
|appendTo(container) |追加到容器|
|prependTo(container)|插入到容器的首位|
|insert(node,where) |在指定的位置插入子节点|
|render(container) |先清空容器,再追加到容器|
|ref(name,target) |使target的name属性指向本孪生节点|
|dispatchEvent(...types) |触发新的事件|
### 10. TwinNode实例的动态函数
除以上函数外的其他函数都是遵循如下规则的动态代理函数
|规则 |功能 |示例|
|----------------|-----------------------|----|
|下划线开始 |为真节点配置attribute |_title("标题")|
|第一个字母小写 |为真节点配置样式 |backgroundColor("red")|
|第一个字母大写 |为真节点配置事件 |Click(event=>console.log(1))|
### 11. 内置的布局函数
以下用开发人员熟悉的文档注释来说明布局函数的功能、参数、返回值。
统一说明:布局函数的参数都要按顺序传入,缺省时使用null,末尾连续的null可以不传。如果实参是Number,则会自动加px,如果不想以px为单位,请使用字符串传参。
#### 11.1. hide
```javascript
/**
* 隐藏节点
* @returns {TwinNode}
*/
function hide()
```
#### 11.2. show
```javascript
/**
* 显示节点
* 如果先调用了hide,再调用show,会还原原来的display
* 如果直接调用show,则会将display设置成block
* @returns {TwinNode}
*/
function show()
```
#### 11.3. inset
```javascript
/**
* 设置节点的绝对定位位置
* @param {Number|String|null} top
* @param {Number|String|null} right
* @param {Number|String|null} bottom
* @param {Number|String|null} left
* @returns {TwinNode}
*/
function inset(top,right,bottom,left)
```
#### 11.4. size
```javascript
/**
* 设置节点的宽度和高度
* @param {Number|String} width
* @param {Number|String} height
* @returns {TwinNode}
*/
function size(width,height)
```
#### 11.5. absolute
```javascript
/**
* 将节点配置为绝对定位,并设置位置、大小和滚动条
* @param {Number|String|null} top
* @param {Number|String|null} right
* @param {Number|String|null} bottom
* @param {Number|String|null} left
* @param {Number|String|null} width
* @param {Number|String|null} height
* @param {String|null} overflow 缺省值我auto
* @returns {TwinNode}
*/
function absolute(top,right,bottom,left,width,height,overflow)
```
#### 11.6. absoluteChild
```javascript
/**
* 将节点配置为绝对定位,并设置其子元素的位置和大小
* @param {Object} option 一个键值对,键表示子节点的查询字符串,值是一个数组,数值中元素分别表示top,right,bottom,left,width,height
* @returns {TwinNode}
*/
function absoluteChild(option)
```
示例:
```javascript
//容器绝对定位,top:10px;left:10px;width:500px; height:500px;
twinNode.absolute(10,null,null,10,500,500)
.absoluteChild(
{
//id为top的子节点:top:0px;right:0px;left:0px;height:100px;
"#top":[0,0,null,0,null,100],
//id为main的子节点:top:100px;right:0px;bottom:0px;left:0px;
"#main":[100,0,0,0]
}
)
```
#### 11.7. fixed
```javascript
/**
* 将节点配置为fixed定位,并设置位置、大小和滚动条
* @param {Number|String|null} top
* @param {Number|String|null} right
* @param {Number|String|null} bottom
* @param {Number|String|null} left
* @param {Number|String|null} width
* @param {Number|String|null} height
* @param {String|null} overflow 缺省值我auto
* @returns {TwinNode}
*/
function fixed(top,right,bottom,left,width,height,overflow)
```
#### 11.8. dock
```javascript
/**
* 将节点配置为fixed定位,并设置停靠位置
* @param {String} where 停靠位置,可选值:top,right,bottom,left,center
* @param {Number|String|null} size 调和宽高
* @param {Number|String|null} offset 偏移量
* @returns {TwinNode}
*/
function dock(where,size,offset)
```
#### 11.9. flexAlign
```javascript
/**
* 在容器上启用flex布局,同时制定对齐方式
* @param {String} flexDirection 主轴方向 0,x,-,row都可表示横向,1,y,|,column都可表示纵向
* @param {String|Numer} justifyContent 主轴上的对齐方式
* @param {Number|String|null} alignItems 交叉轴上的对齐方式
* @param {String|Number} alignContent 多根轴线的对齐方式
* @returns {TwinNode}
*/
function flexAlign(flexDirection,justifyContent,alignItems,alignContent)
```
参数简写:
主轴方向:0, x, - 都是row的简写; 1, y, | 都是column的简写
对齐方式:0是flex-start的简写,1是center的简写,2是flex-end的简写
#### 11.10. flexX
```javascript
/**
* 横向flex布局
* @param {String|Numer} justifyContent 主轴上的对齐方式
* @param {Number|String|null} alignItems 交叉轴上的对齐方式
* @param {String|Number} alignContent 多根轴线的对齐方式
* @returns {TwinNode}
*/
function flexX(justifyContent,alignItems,alignContent)
```
#### 11.11. flexY
```javascript
/**
* 纵向flex布局
* @param {String|Numer} justifyContent 主轴上的对齐方式
* @param {Number|String|null} alignItems 交叉轴上的对齐方式
* @param {String|Number} alignContent 多根轴线的对齐方式
* @returns {TwinNode}
*/
function flexY(justifyContent,alignItems,alignContent)
```
#### 11.12. flexCake
```javascript
/**
* 将容器启用flex布局,并按变量切分items
* @param {String|Numer} flexDirection 主轴上的对齐方式
* @param {[...Number]} itemSize 每项分得的空间,所有项之和就是总空间
* @returns {TwinNode}
*/
function flexCake(flexDirection,...itemSize)
```
示例
```javascript
//总空间为12份,第1个子元素分得4份,第2个子元素分得8份
twinNode.flexCake("row",4,8)
```
#### 11.13. grid
```javascript
/**
* 将容器启用grid布局,并配置子元素的单位尺寸
* @param {Numer} rowCount 总行数
* @param {Number} columnCount 总列数
* @param {Object} cells 键值对,键表示子元素的查询字符串,值是一个数组,数组第0个元素表示跨行数量,第1个元素为跨列数量
* @returns {TwinNode}
*/
function grid(rowCount,columnCount,cells)
```
示例:
```javascript
twinNode.grid(
5, //共5行
5, //共5列
{
"#cell1":[2,2], //id为cell1的子节点跨2行2列
"#cell2":[3,3] //id为cell2的子节点跨3行3列
//其他子元素都是1行1列
}
)
```