拓冰建站拓冰建站
首页 / 资讯中心 / 正文

hyperapp @hyperapp/html:用纯函数写 HTML 的视图层实践

hyperapp hyperapp/html用纯函数写 HTML 的视图层实践【免费下载链接】hyperapp1kB-ish JavaScript framework for building hypertext applications项目地址: https://gitcode.com/gh_mirrors/hy/hyperapphyperapp/html是 hyperapp 官方仓库内置的函数式视图辅助包为每一个 HTML 标签提供一个同名导出函数如h1、button、form让你在不需要 JSX 或模板字符串的情况下用“纯函数调用”的方式描述视图。本文基于 packages/html/README.md 与 packages/html/index.js 源码展开覆盖从安装、完整示例到tag()工厂函数的底层实现读完你既能直接上手写视图也能理解这些函数与核心h()之间的调用关系。设计动机为什么需要每个标签一个函数hyperapp 内置的h()函数被有意设计得十分“原始”primitive以保留任意写法的自由度。但h(div, {}, children)这种写法里标签名始终是字符串既没有静态检查也缺乏阅读节奏。如果你更喜欢函数式风格而非 JSX 或模板字符串等模板方案hyperapp/html提供的就是一组“每个 HTML 标签 对应一个函数”的集合——本质上是给h()做的、按标签名预绑定的薄封装使视图代码写得更快、读起来更像“拼装积木”。包的描述Write HTML with plain functions.与 packages/html/package.json 中的description字段一致其version为2.0.0并通过peerDependencies声明依赖hyperapp ^2.0.0——即它必须与主包 hyperapp 同版本线配合使用。快速上手计数器示例以下是 packages/html/README.md 给出的完整入门示例一个无需构建工具的浏览器单文件应用可直接保存为 HTML 运行!DOCTYPE html html langen head script typemodule import { app } from https://unpkg.com/hyperapp import { main, h1, button, text, } from https://unpkg.com/hyperapp/html?module const Subtract (state) ({ ...state, count: state.count - 1 }) const Add (state) ({ ...state, count: state.count 1 }) app({ init: (count 0) ({ count }), view: (state) main([ h1(text(state.count)), button({ onclick: Subtract }, text(-)), button({ onclick: Add }, text()), ]), node: document.getElementById(app), }) /script /head body main idapp/main /body /html几个要点main([...])等价于h(main, {}, [...])第一个参数是 props 对象可为空对象后续参数是子节点h1(text(state.count))展示了一个元素节点嵌套一个文本节点的典型组合button({ onclick: Subtract }, text(-))展示事件属性直接挂 action 函数的 hyperapp 惯用法——onclick的值就是 action无需addEventlistenerapp({ init, view, node })是 hyperapp 的最小实例配置init作为 action 提供初始状态这里count默认 0view是(state) VNode的纯函数node是挂载点。view返回的 VNode 会被核心的 diff/patch 算法渲染。关于视图的定义与组成h()/text()/memo()三大构件可进一步参考 docs/architecture/views.md。安装与导入方式使用模块打包器Rollup / Webpacknpm install hyperapp/html然后在应用中按需导入import { a, form, input } from hyperapp/html免构建直接用script typemodulescript typemodule import { a, form, input } from https://unpkg.com/hyperapp/html?module /script由于包是 ES Modulepackages/html/package.json 中type: module浏览器原生模块导入即可工作无需任何打包步骤。源码剖析tag()工厂函数如何工作packages/html/index.js 的核心不到 10 行所有导出的标签函数都由同一个工厂闭包生成import { h } from hyperapp const EMPTY_ARR [] const EMPTY_OBJ {} const tag (tag) ( props EMPTY_OBJ, children props.tag ! null || Array.isArray(props) ? props : EMPTY_ARR ) h(tag, props children ? EMPTY_OBJ : props, children) export const a tag(a) export const b tag(b) // ... 其余标签依次导出这段实现有三个值得注意的细节柯里化的闭包绑定tag(button)返回一个函数调用时第一个参数是 props默认EMPTY_OBJ。因此button(text())与h(button, {}, text())完全等价。children 参数的“智能默认值”默认表达式props.tag ! null || Array.isArray(props) ? props : EMPTY_ARR利用了一个关键特征——由h()生成的 VNode 对象上都带有tag属性见 index.js 中createVNode返回的{ tag, props, key, children, type, node }结构。所以当你写button(text())时第一个实参其实是子节点 VNode 而非 props此时props.tag ! null为真children就默认取到了这个 VNode随后props children ? EMPTY_OBJ : props又把 props 修正回空对象避免把 VNode 误当属性传给h()。数组与单节点两种写法统一children可以是单个 VNode 或 VNode 数组对应 docs/api/h.md 中children参数“VNode or array of VNodes”的说明最终h()内部会用isArray(children) ? children : [children]归一化。此外text并非在hyperapp/html中重新实现而是从主包直接再导出export { text } from hyperapp主包中text(value, node)通过createVNode(value, EMPTY_OBJ, EMPTY_ARR, TEXT_NODE, node)创建type: 3的文本 VNode见 index.js并有测试用例验证其结构见 tests/index.test.js 中text function用例断言text(hyper)生成{ children: [], key: undefined, node: undefined, props: {}, type: 3, tag: hyper }。这解释了为什么示例中可以import { text } from hyperapp/html而无需单独引主包。标签函数背后的h()语义由于所有标签函数只是h()的别名绑定h()支持的一切 props 语义在这里全部可用。结合 docs/api/h.md 与 index.d.ts 的类型定义常用的特殊属性有class支持字符串可含空格分隔的多类名、布尔开关对象、以及任意嵌套的数组组合。核心通过createClass()递归归一化index.js并优先于 props 展开处理见 index.js 中c ? { class: createClass(c) } : EMPTY_OBJ。styleCSS 属性对象连字符属性用引号包裹font-weight: boldpatch 时对setProperty与直接赋值做了分支处理index.js。key唯一字符串用于数组子节点增删时的 diff 追踪index.js 中的 keyed 算法。事件属性on前缀的属性如onclick、onchange甚至自定义合成事件名如onbuild会被patchProperty剥离前缀后走addEventListener/removeEventListener事件触发时经由dispatch(this.events[event.type], event)路由到 actionindex.js 与 index.js。TypeScript 用户可参考主包的 index.d.tsPropsS类型将class?: ClassProp、style?: StyleProp、key?与EventActionsS组合在一起CustomPayloads类型还允许事件 action 携带自定义 payload 元组。标签覆盖范围与 SVG 对照packages/html/index.js 共导出约 97 个 HTML 标签函数从常见的a、b、p、div、span、button、form、input到较长的blockquote、figcaption基本覆盖标准 HTML 元素全集且文件中的排列大致按名称长度分组这是纯粹的实现组织习惯不影响使用。如果你的视图需要 SVG 元素应改用姊妹包hyperapp/svgpackages/svg/README.md它采用完全相同的tag()工厂模式导出svg、path、circle、linearGradient、feGaussianBlur等 SVG 元素函数见 packages/svg/index.js。两个包的源码结构一致区别仅在标签名清单从源码结构看二者都依赖主包的h()而核心 patch 算法在遇到vdom.tag svg时会切换到document.createElementNS(SVG_NS, ...)创建节点index.js所以 HTML 容器内嵌 SVG 是受支持的。小结与延伸阅读hyperapp/html的定位h()的按标签预绑定封装层零额外抽象开销每个标签函数只是一次闭包换取更函数化、可读性更高的视图写法安装npm install hyperapp/htmlpeer 依赖hyperapp ^2.0.0或以script typemodule免构建导入实现核心packages/html/index.js 中的tag()工厂 从主包再导出的text底层支撑主包的 h() / text() / patch 实现、docs/api/h.md 的 props 语义与 index.d.ts 类型定义许可协议MITLICENSE.md。【免费下载链接】hyperapp1kB-ish JavaScript framework for building hypertext applications项目地址: https://gitcode.com/gh_mirrors/hy/hyperapp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门