{"id":15893856,"url":"https://github.com/chinanf-boy/hyperapp-explain","last_synced_at":"2025-10-26T05:04:07.370Z","repository":{"id":90548425,"uuid":"139671107","full_name":"chinanf-boy/hyperapp-explain","owner":"chinanf-boy","description":"explain: hyperapp 1kb JavaScript framework, 五脏俱全的麻雀  👷🀄️","archived":false,"fork":false,"pushed_at":"2018-07-04T05:15:06.000Z","size":25,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T18:16:14.876Z","etag":null,"topics":["explain","hyperapp","js"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chinanf-boy.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-04T05:12:00.000Z","updated_at":"2018-07-04T05:15:36.000Z","dependencies_parsed_at":"2023-07-19T04:16:24.213Z","dependency_job_id":null,"html_url":"https://github.com/chinanf-boy/hyperapp-explain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chinanf-boy/hyperapp-explain","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhyperapp-explain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhyperapp-explain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhyperapp-explain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhyperapp-explain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chinanf-boy","download_url":"https://codeload.github.com/chinanf-boy/hyperapp-explain/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chinanf-boy%2Fhyperapp-explain/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263550755,"owners_count":23478866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["explain","hyperapp","js"],"created_at":"2024-10-06T08:13:55.533Z","updated_at":"2025-10-26T05:04:02.333Z","avatar_url":"https://github.com/chinanf-boy.png","language":null,"readme":"# hyperapp [![explain](http://llever.com/explain.svg)](https://github.com/chinanf-boy/Source-Explain)\n\n1 KB JavaScript 库 - 构建 web 应用. \n\n\u003e \"version\": \"1.2.6\"\n\n[github source](https://github.com/hyperapp/hyperapp/tree/1.2.6)\n\n[中文](./readme.md) |  ~~[english explain](./README.en.md)~~\n\n欢迎 `Issue` 和 `Pull` ❤️, 最好 `Pull` 👏\n\n---\n\n😯只有1kb的应用框架\n\n- [ ] [校对🀄️: 原文 readme.md 🇨🇳翻译](./hyperapp.readme.md)\n\n---\n\n## 本目录\n\n- [x] [不先使用何谈解释](#使用-hyperapp)\n\n- [x] [认识-`package.json`](#package-json)\n\n- [x] [src/index 主文件](#src-index)\n\n- [ ] [ h - jsx语法糖](#h-jsx语法糖)\n\n- [ ] [app - 结合与挂载](#app-结合与挂载)\n\n---\n\n## 使用-hyperapp\n\n---\n\n这个例子假设你正在使用像`Babel`或`TypeScript`这样的JavaScript编译器，以及像`Parcel`，`Rollup`，`Webpack`等模块捆绑器。通常，你需要做的就是`安装JSX 转换插件`，并将编译选项添加到你的.babelrc 文件。\n\n``` json\n{\n  \"plugins\": [[\"transform-react-jsx\", { \"pragma\": \"h\" }]]\n}\n```\n\n[更多-可以看](https://github.com/hyperapp/hyperapp#getting-started)\n\n---\n\n``` js\nimport { h, app } from \"hyperapp\" // 导入\n\nconst state = { // 初始化-存储\n  count: 0\n}\n\nconst actions = { // 动作行为-定义\n  down: value =\u003e state =\u003e ({ count: state.count - value }),\n  up: value =\u003e state =\u003e ({ count: state.count + value })\n}\n\nconst view = (state, actions) =\u003e ( // 组件定义\n  \u003cdiv\u003e\n    \u003ch1\u003e{state.count}\u003c/h1\u003e\n    \u003cbutton onclick={() =\u003e actions.down(1)}\u003e-\u003c/button\u003e\n    \u003cbutton onclick={() =\u003e actions.up(1)}\u003e+\u003c/button\u003e\n  \u003c/div\u003e\n)\n\n// 使用-上述-JSX 转换插件后，我们可以-上面所写的\n\n// 不然我们需要，下面这样写\n\n// const view = (state, actions) =\u003e\n//   h(\"div\", {}, [\n//     h(\"h1\", {}, state.count),\n//     h(\"button\", { onclick: () =\u003e actions.down(1) }, \"-\"),\n//     h(\"button\", { onclick: () =\u003e actions.up(1) }, \"+\")\n//   ])\n\napp(state, actions, view, document.body) // 挂载-\u003e document.body\n```\n\n\u003e [codepen-hyperapp 一系列的示例](https://codepen.io/hyperapp/)\n\n---\n\n## package-json\n\n作为js项目的根本\n\n``` json\n  \"main\": \"dist/hyperapp.js\", // node 导入主文件\n  \"module\": \"src/index.js\", // 模块\n  \"typings\": \"hyperapp.d.ts\", // ts 类型\n```\n\n---\n\n## src-index\n\n只有一个文件[index.js](./hyperapp/src/index.js)\n\n我们先看看示例的引入\n\n``` js\nimport { h, app } from \"hyperapp\" \n```\n\n- [h \u003e jsx语法糖](#h-jsx语法糖)\n\n- [app \u003e 结合与挂载](#app-结合与挂载)\n\n---\n\n## h-jsx语法糖\n\n代码 1-26\n\n `\u003cbutton onclick={() =\u003e actions.down(1)}\u003e-\u003c/button\u003e`\n\n==\n\n`h(\"button\", { onclick: () =\u003e actions.down(1) }, \"-\"),`\n\n\u003e `h` 不仅作为-`JSX语法糖`-且-作为规范`组件结构`的作用\n\n``` js\nexport function h(name, attributes) {\n  var rest = []\n  var children = []\n  var length = arguments.length\n\n  while (length-- \u003e 2) rest.push(arguments[length])\n\n  while (rest.length) {\n    var node = rest.pop()\n    if (node \u0026\u0026 node.pop) {\n      for (length = node.length; length--; ) {\n        rest.push(node[length])\n      }\n    } else if (node != null \u0026\u0026 node !== true \u0026\u0026 node !== false) {\n      children.push(node)\n    }\n  }\n\n  return typeof name === \"function\"\n    ? name(attributes || {}, children)\n    : {\n        nodeName: name,\n        attributes: attributes || {},\n        children: children,\n        key: attributes \u0026\u0026 attributes.key\n      }\n}\n```\n\n---\n\n## app-结合与挂载\n\n使用-\u003e `app(state, actions, view, document.body)`\n\n- state `存储`\n\n- actions `动作函数`\n\n- view `组件`\n\n- document.body `挂载目标`\n\n代码 28-315\n\n``` js\nexport function h(name, attributes) {\n  var rest = []\n  var children = []\n  var length = arguments.length\n\n  while (length-- \u003e 2) rest.push(arguments[length])\n\n  while (rest.length) {\n    var node = rest.pop()\n    if (node \u0026\u0026 node.pop) {\n      for (length = node.length; length--; ) {\n        rest.push(node[length])\n      }\n    } else if (node != null \u0026\u0026 node !== true \u0026\u0026 node !== false) {\n      children.push(node)\n    }\n  }\n\n  return typeof name === \"function\"\n    ? name(attributes || {}, children)\n    : {\n        nodeName: name,\n        attributes: attributes || {},\n        children: children,\n        key: attributes \u0026\u0026 attributes.key\n      }\n}\n\nexport function app(state, actions, view, container) {\n  var map = [].map\n  var rootElement = (container \u0026\u0026 container.children[0]) || null\n  var oldNode = rootElement \u0026\u0026 recycleElement(rootElement)\n  var lifecycle = []\n  var skipRender\n  var isRecycling = true\n  var globalState = clone(state)\n  var wiredActions = wireStateToActions([], globalState, clone(actions))\n\n  scheduleRender()\n\n  return wiredActions\n\n  function recycleElement(element) {\n    return {\n      nodeName: element.nodeName.toLowerCase(),\n      attributes: {},\n      children: map.call(element.childNodes, function(element) {\n        return element.nodeType === 3 // Node.TEXT_NODE\n          ? element.nodeValue\n          : recycleElement(element)\n      })\n    }\n  }\n\n  function resolveNode(node) {\n    return typeof node === \"function\"\n      ? resolveNode(node(globalState, wiredActions))\n      : node != null\n        ? node\n        : \"\"\n  }\n\n  function render() {\n    skipRender = !skipRender\n\n    var node = resolveNode(view)\n\n    if (container \u0026\u0026 !skipRender) {\n      rootElement = patch(container, rootElement, oldNode, (oldNode = node))\n    }\n\n    isRecycling = false\n\n    while (lifecycle.length) lifecycle.pop()()\n  }\n\n  function scheduleRender() {\n    if (!skipRender) {\n      skipRender = true\n      setTimeout(render)\n    }\n  }\n\n  function clone(target, source) {\n    var out = {}\n\n    for (var i in target) out[i] = target[i]\n    for (var i in source) out[i] = source[i]\n\n    return out\n  }\n\n  function setPartialState(path, value, source) {\n    var target = {}\n    if (path.length) {\n      target[path[0]] =\n        path.length \u003e 1\n          ? setPartialState(path.slice(1), value, source[path[0]])\n          : value\n      return clone(source, target)\n    }\n    return value\n  }\n\n  function getPartialState(path, source) {\n    var i = 0\n    while (i \u003c path.length) {\n      source = source[path[i++]]\n    }\n    return source\n  }\n\n  function wireStateToActions(path, state, actions) {\n    for (var key in actions) {\n      typeof actions[key] === \"function\"\n        ? (function(key, action) {\n            actions[key] = function(data) {\n              var result = action(data)\n\n              if (typeof result === \"function\") {\n                result = result(getPartialState(path, globalState), actions)\n              }\n\n              if (\n                result \u0026\u0026\n                result !== (state = getPartialState(path, globalState)) \u0026\u0026\n                !result.then // !isPromise\n              ) {\n                scheduleRender(\n                  (globalState = setPartialState(\n                    path,\n                    clone(state, result),\n                    globalState\n                  ))\n                )\n              }\n\n              return result\n            }\n          })(key, actions[key])\n        : wireStateToActions(\n            path.concat(key),\n            (state[key] = clone(state[key])),\n            (actions[key] = clone(actions[key]))\n          )\n    }\n\n    return actions\n  }\n\n  function getKey(node) {\n    return node ? node.key : null\n  }\n\n  function eventListener(event) {\n    return event.currentTarget.events[event.type](event)\n  }\n\n  function updateAttribute(element, name, value, oldValue, isSvg) {\n    if (name === \"key\") {\n    } else if (name === \"style\") {\n      for (var i in clone(oldValue, value)) {\n        var style = value == null || value[i] == null ? \"\" : value[i]\n        if (i[0] === \"-\") {\n          element[name].setProperty(i, style)\n        } else {\n          element[name][i] = style\n        }\n      }\n    } else {\n      if (name[0] === \"o\" \u0026\u0026 name[1] === \"n\") {\n        name = name.slice(2)\n\n        if (element.events) {\n          if (!oldValue) oldValue = element.events[name]\n        } else {\n          element.events = {}\n        }\n\n        element.events[name] = value\n\n        if (value) {\n          if (!oldValue) {\n            element.addEventListener(name, eventListener)\n          }\n        } else {\n          element.removeEventListener(name, eventListener)\n        }\n      } else if (name in element \u0026\u0026 name !== \"list\" \u0026\u0026 !isSvg) {\n        element[name] = value == null ? \"\" : value\n      } else if (value != null \u0026\u0026 value !== false) {\n        element.setAttribute(name, value)\n      }\n\n      if (value == null || value === false) {\n        element.removeAttribute(name)\n      }\n    }\n  }\n\n  function createElement(node, isSvg) {\n    var element =\n      typeof node === \"string\" || typeof node === \"number\"\n        ? document.createTextNode(node)\n        : (isSvg = isSvg || node.nodeName === \"svg\")\n          ? document.createElementNS(\n              \"http://www.w3.org/2000/svg\",\n              node.nodeName\n            )\n          : document.createElement(node.nodeName)\n\n    var attributes = node.attributes\n    if (attributes) {\n      if (attributes.oncreate) {\n        lifecycle.push(function() {\n          attributes.oncreate(element)\n        })\n      }\n\n      for (var i = 0; i \u003c node.children.length; i++) {\n        element.appendChild(\n          createElement(\n            (node.children[i] = resolveNode(node.children[i])),\n            isSvg\n          )\n        )\n      }\n\n      for (var name in attributes) {\n        updateAttribute(element, name, attributes[name], null, isSvg)\n      }\n    }\n\n    return element\n  }\n\n  function updateElement(element, oldAttributes, attributes, isSvg) {\n    for (var name in clone(oldAttributes, attributes)) {\n      if (\n        attributes[name] !==\n        (name === \"value\" || name === \"checked\"\n          ? element[name]\n          : oldAttributes[name])\n      ) {\n        updateAttribute(\n          element,\n          name,\n          attributes[name],\n          oldAttributes[name],\n          isSvg\n        )\n      }\n    }\n\n    var cb = isRecycling ? attributes.oncreate : attributes.onupdate\n    if (cb) {\n      lifecycle.push(function() {\n        cb(element, oldAttributes)\n      })\n    }\n  }\n\n  function removeChildren(element, node) {\n    var attributes = node.attributes\n    if (attributes) {\n      for (var i = 0; i \u003c node.children.length; i++) {\n        removeChildren(element.childNodes[i], node.children[i])\n      }\n\n      if (attributes.ondestroy) {\n        attributes.ondestroy(element)\n      }\n    }\n    return element\n  }\n\n  function removeElement(parent, element, node) {\n    function done() {\n      parent.removeChild(removeChildren(element, node))\n    }\n\n    var cb = node.attributes \u0026\u0026 node.attributes.onremove\n    if (cb) {\n      cb(element, done)\n    } else {\n      done()\n    }\n  }\n\n  function patch(parent, element, oldNode, node, isSvg) {\n    if (node === oldNode) {\n    } else if (oldNode == null || oldNode.nodeName !== node.nodeName) {\n      var newElement = createElement(node, isSvg)\n      parent.insertBefore(newElement, element)\n\n      if (oldNode != null) {\n        removeElement(parent, element, oldNode)\n      }\n\n      element = newElement\n    } else if (oldNode.nodeName == null) {\n      element.nodeValue = node\n    } else {\n      updateElement(\n        element,\n        oldNode.attributes,\n        node.attributes,\n        (isSvg = isSvg || node.nodeName === \"svg\")\n      )\n\n      var oldKeyed = {}\n      var newKeyed = {}\n      var oldElements = []\n      var oldChildren = oldNode.children\n      var children = node.children\n\n      for (var i = 0; i \u003c oldChildren.length; i++) {\n        oldElements[i] = element.childNodes[i]\n\n        var oldKey = getKey(oldChildren[i])\n        if (oldKey != null) {\n          oldKeyed[oldKey] = [oldElements[i], oldChildren[i]]\n        }\n      }\n\n      var i = 0\n      var k = 0\n\n      while (k \u003c children.length) {\n        var oldKey = getKey(oldChildren[i])\n        var newKey = getKey((children[k] = resolveNode(children[k])))\n\n        if (newKeyed[oldKey]) {\n          i++\n          continue\n        }\n\n        if (newKey != null \u0026\u0026 newKey === getKey(oldChildren[i + 1])) {\n          if (oldKey == null) {\n            removeElement(element, oldElements[i], oldChildren[i])\n          }\n          i++\n          continue\n        }\n\n        if (newKey == null || isRecycling) {\n          if (oldKey == null) {\n            patch(element, oldElements[i], oldChildren[i], children[k], isSvg)\n            k++\n          }\n          i++\n        } else {\n          var keyedNode = oldKeyed[newKey] || []\n\n          if (oldKey === newKey) {\n            patch(element, keyedNode[0], keyedNode[1], children[k], isSvg)\n            i++\n          } else if (keyedNode[0]) {\n            patch(\n              element,\n              element.insertBefore(keyedNode[0], oldElements[i]),\n              keyedNode[1],\n              children[k],\n              isSvg\n            )\n          } else {\n            patch(element, oldElements[i], null, children[k], isSvg)\n          }\n\n          newKeyed[newKey] = children[k]\n          k++\n        }\n      }\n\n      while (i \u003c oldChildren.length) {\n        if (getKey(oldChildren[i]) == null) {\n          removeElement(element, oldElements[i], oldChildren[i])\n        }\n        i++\n      }\n\n      for (var i in oldKeyed) {\n        if (!newKeyed[i]) {\n          removeElement(element, oldKeyed[i][0], oldKeyed[i][1])\n        }\n      }\n    }\n    return element\n  }\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fhyperapp-explain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchinanf-boy%2Fhyperapp-explain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchinanf-boy%2Fhyperapp-explain/lists"}