# Heng.js **Repository Path**: zhang_xiangm/heng.js ## Basic Information - **Project Name**: Heng.js - **Description**: 声明式构建HTML文档,不需要CLI,无虚拟DOM - **Primary Language**: JavaScript - **License**: MIT-0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-25 - **Last Updated**: 2025-05-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Heng.js Heng.js声明式构建HTML文档,不需要CLI,无虚拟DOM,支持链式调用。 ## 框架介绍 Heng.js献给我家小生命珩珩。Heng.js可实现无CLI,无虚拟DOM,声明式构建HTML。构建完的HengNode可使用类似于jquery的链式方式配置属性、样式、事件。Heng.js巧妙的函数式编程特性天生支持组件化,一个能返回HengNode的函数就是一个可复用的组件。 ## 链式调用 ### $$函数 该函数献给伟大的jquery的落幕。$$函数将一个真实DOM元素包装成一个HengNode实例,以便链式调用。链式调用遵循如下规则: 1. 以下划线开始的函数为节点配置attribute; 2. 小驼峰命名的函数是为节点配置样式,如果值是数值则会自动加上px; 3. 大驼峰命名函数为节点配置事件。 ``` //$$函数的第一参数是一个选择器 $$("#elementId") ._title("标题") ._class("class1") .width(100) .height(50) .zIndex("1") .Click(event=>console.log(event)); //$$函数的第一参数也可以是一个真实DOM节点 let element = document.querySelector("#elementId"); $$(element) ._title("标题") ._class("class1") .width(100) .height(50) .zIndex("1") .Click(event=>console.log(event)); ``` ### $$代理 Element.prototype上配置了一个叫$$的代理,该代理会将真实DOM节点包装成HengNode,以便链式调用。 ``` document.querySelector("#elementId") .$$ ._title("标题")._class("class1") .width(100).height(50).zIndex("1") .Click(event=>console.log(event)); ``` ### $$函数与WebComponent ``` //第一个参数是要查找的节点的选择器 //第二个参数是目标节点所在的WebComponent组件(或选择器) //第三个参数表示是否在WebComponent组件的shadowRoot中查找,默认为false $$("#elementId","#webcomponentId",true) ._title("标题")._class("class1") .width(100).height(50).zIndex("1") .Click(event=>console.log(event)); ``` ## 语义化构建 Heng.js的语义化构建严格遵循javascript语法规则,不需要借助任何第三方翻译或编译插件。这一理念是作者对前端复杂化趋势的呐喊和抗争。希望Heng.js的后续插件开发者秉承这一理念。 ### 静态构建 ``` //从Heng.tags中解构任何合法标签函数,之后的章节将省略这一步 const {ul,li} = Heng.tags; //使用标签函数声明式构建html文档 let hengNode = ul( li(1), li(2) ); //将HengNode挂载到DOM树,之后的章节将省略这一步 Heng.render(hengNode,document.getElementById("container"),true); ``` ### 动态构建 ``` //准备好一份数据,通常是用ajax从服务端获取 let myFamily = [ {id:1,name:"张向明"}, {id:2,name:"将海艳"}, {id:3,name:"张泽珩"} ]; let hengNode = table( thead( tr(th("编号"),th("姓名")) ), tbody( //此处使用数据 myFamily.map(row=> tr(td(row.id),td(row.name)) ) ) ); ``` ## 组件化 ### 无参组件 在Heng.js中,所谓组件就是一个返回值为HengNode实例的函数。 ``` //声明组件,组件就是一个能返回HengNode的函数 function component(){ let hengNode = ul( li(1), li(2) ); return hengNode; } //使用组件时,在合适的位置插入组件名即可,不需要调用 let hengNode = div( component ); ``` ### 有参组件 ``` //准备一份要用的数据 let myFamily = [ {id:1,name:"张向明"}, {id:2,name:"将海艳"}, {id:3,name:"张泽珩"} ]; //准备一个有参组件 function MY_FAMILY(member){ let hengNode = table( thead( tr(th("编号"),th("姓名")) ), tbody( member.map(row=> tr(td(row.id),td(row.name))._id(row.id) ) ) ); return hengNode; } //使用组件时,在合适的位置调用组件函数并传入参数 let hengNode = div( MY_FAMILY(myFamily) ); ``` ## 属性、样式和事件配置 ### 通用方式 ``` ul( {id:"ul1",class:"class1 class2"}, {style:{color:"red"}}, {click:event=>console.log(event)}, li(1), li(2), li(3) ) ``` ### 用正则表达式+选择器配置id和class ``` ul( /#ul2.class3.class4/g, li(1), li(2), li(3) ) ``` ### 链式配置 ``` ul( li("样式配置函数用小驼峰命名").color("red").backgroundColor("lightgray"), li("属性配置函数用下划线开始")._id("aaa")._class("bbb"), li("事件配置函数首字母大写") .Click(event=>console.log("单击")) .DblClick(event=>console.log("双击")) ) ``` ## 导出 在构建过程中将节点导出到一个变量中,构建完后再配置属性、样式、事件 ``` //构建 ul( li(1)._id("myLi",window), li(2), li(3) ); //配置(如果此时已经在DOM节点上完成了渲染,这项配置的改变会自动更新到真实节点) myLi._class("className") .color("red").backgroundColor("lightgray") .Click(event=>console.log("单击")) .DblClick(event=>console.log("双击")); ``` ## 文档碎片 某些情况下,一组节点并不需要容器,此时可以使用Heng.tags中解构出一个叫下划线的标签函数,该标签函数可以构建一个文档碎片(DocumentFragment) ``` const { _ , li } = Heng.tags; let nodes = _( li(1), li(2), li(3) ); Heng.render(nodes,document.querySelector("ul")); ``` ## 怀旧风格 ``` let myFamily = [ {id:1,name:"张向明"}, {id:2,name:"将海艳"}, {id:3,name:"张泽珩"} ]; let data = ul( echo=>{ for(let item of myFamily){ echo(li(item.name)) } } ) ``` ## SVG支持 ``` //注意普通标签函数从Heng.tags解构,而svg标签从svgTags解构 const {svg,rect,line} = Heng.svgTags; let image = svg( {width:100,height:100,stroke:"gray","stroke-width":1}, rect( {x:1,y1,width:98,height:60} ), line( {x1:1,y1:70,x2:98,y2:98} ) ); ``` ## webcomponent支持 ``` //定义的自定义组件的类 class MyComponent extends HTMLElement{ constructor(){ super(); this.innerHTML = "组件的内容"; } } //注册自定义组件的标签名 customElements.define("my-component",MyComponent); //解构标签函数,注意标签函数一定要用下划线分割单词 const {div,my_component} = Heng.tags; //使用标签函数 let hengNode = div( my_component ) ``` ## 常用技巧 ### 巧用map ``` //周的下拉框 select( [1,2,3,4,5,6,"日"].map(item=> option(`周${item}`)._value(item) ) ) ``` ### 逻辑短路 ``` //以下代码不显示周日 ul( ["周一","周二","周三","周四","周五","周六","周日"].map(item => //若item等于周日,整个逻辑表达式将返回false,而false,null,undefine会被框架忽略掉 //若item不等于周日,整个逻辑表达式将返回&&右边的表达式 item !== "周日" && li(item) ) ) ``` ## 常见问题 ### 配置像素样式可省略px,但有特殊情况 ``` div("内容") .width("100%") //单位不是px,必须用字符串 .height(50) //单位是px,可以用数字,框架会自动加上px .zIndex(10) //zIndex=10px是一个无效的配置 .zIndex("10") //这才是正确的配置zIndex ``` ### Heng.render ``` //第三个参数表示渲染到容器时是否清空原来的内容,默认是不清空(即追加),通常情况下要用true Heng.render(hengNode, container, clearContainer = false) ```