hyperapp 是什么鬼?

hyperapp 是一个前端的应用构建库。初见写法,很有一种写react的亲切的感觉(其实就是一个套路),不过这肯定不能成为吸引广发gay友从而在短短两个月拿到 8K star的理由。更重要的一个原因是 官方宣称的1kb。是的, hyperapp 的核心代码只有1kb,这对早已习惯react全家桶,同时对当今web应用一个页面动辄3、4M毒害的gay友来说,的确是一个福音。基于此,官方给自己的定位是:

  • 更小:只要1kb,做到其他框架应该做的;
  • 更实用:主流的前端应用思想,不会对学习带来额外负担;
  • 开箱即用:完善的虚拟Dom、key更新、应用生命周期。
  • 以上个人翻译,有吹嘘成分

既然听起来这么厉害,今天就来一探究竟了……

简单的使用

最简单的使用方法就是看官网给的 计数器 示例,可以在 这里 查看最终效果:

<body>
<script src="https://unpkg.com/hyperapp"></script>
<script>// ******划重点const { h, app } = hyperappconst state = {count: 0
}const actions = {down: value => state => ({ count: state.count - value }),up: value => state => ({ count: state.count + value })
}const view = (state, actions) =>h("div", {}, [h("h1", {}, state.count),h("button", { onclick: () => actions.down(1) }, "–"),h("button", { onclick: () => actions.up(1) }, "+")])window.main = app(state, actions, view, document.body)// *****划重点
</script>
</body>

显而易见,state 定义了应用的状态, view 定义了应用的视图,通过 h 方法生成一个虚拟Dom,也就是可以被浏览器解释的结点树,action 则定义了应用的一些行为逻辑,最后在通过 app 方法挂载到真实的Dom元素结点上。

当然这只是很简单的使用。对于已经习惯了react写法的我们来说,我们可能在 view 的部分更习惯写纯函数,或者说一些牵扯到生命周期的操作,当然这些在 hyperapp 中也是可以的。

具体的操作可以参考 官方文档

看源码吧还是

当然学习使用不是我们的目的,这些操作其他库中都有实现,真正感兴趣的是他说的1kb,所以还是来看源码吧(讲真,源码写的有点绕)。

核心的方法只有两个,h 函数 和 app 函数,h函数很简单,只是用来构建 dom 结点的。源码如下:

/*** 先来看h的用法,作用是生成一个虚拟dom节点* name 可以是 一个标签名字符串,如‘div’, 也可以是一个已经被渲染的component,如‘h(div,'',)’* props 标签的属性定义,如‘class’,事件等* 不定参数,都会当做当前节点的子节点计算*/export function h(name, props) {var nodevar stack = []var children = []for (var i = arguments.length; i-- > 2; ) {stack.push(arguments[i])}while (stack.length) {if (Array.isArray((node = stack.pop()))) {for (i = node.length; i--; ) {stack.push(node[i])}} else if (null == node || true === node || false === node) {} else {children.push(typeof node === "number" ? node + "" : node)}}return typeof name === "string"? {name: name,props: props || {},children: children}: name(props || {}, children)
}

app 方法则是项目的入口,整个构建的操作其实在这里执行。在app函数里又定义了许多常用的工具方法,比如 createElement(创建元素),getKey(获取元素结点的key),removeElement(移除元素)等等。又很多,这里不在一一分析,重点方法只有两个 init 方法和 patch方法。

init()

init的方法的调用还是挺有意思的,如下:

repaint(init([], (state = copy(state)), (actions = copy(actions))))

可理解成:

function a() { console.log('a'); setTimeout(b); }
function b() { console.log('b') }
function c() { console.log('c') };a(c());

其实就是确保在入口的 repaint 方法每次被调用的时候先执行 init 方法。

我们来看 init 方法的主体部分:

// actions 有两种情况,一种是参数只存在state的情况,一种是参数存在state和action的情况,又是讨厌的递归function init(path, slice, actions) {for (var key in actions) {typeof actions[key] === "function"? (function(key, action) {actions[key] = function(data) {// 第一次初始化的时候,path为[],所以得到的还是初始传入的stateslice = get(path, state)  // actions参数中存在action的情况,同时执行重新渲染一次if (typeof (data = action(data)) === "function") {data = data(slice, actions)}if (data && data !== slice && !data.then) {repaint((state = set(path, copy(slice, data), state, {})))}return data}})(key, actions[key]): init(path.concat(key),(slice[key] = slice[key] || {}),(actions[key] = copy(actions[key])))}}

其实 init 方法的目的就是确保了两种执行 repaint 方法的不同情况(有个看源码的小技巧就是去看官方提供的单元测试,来反推某个方法的用法)。init 方法的目的是执行 repaint 方法(真实渲染的方法入口,最终会执行 patch 方法)。

patch()

function patch(parent, element, oldNode, node, isSVG, nextSibling) {if (node === oldNode) {} else if (null == oldNode) {element = parent.insertBefore(createElement(node, isSVG), element)} else if (node.name && node.name === oldNode.name) {updateElement(element, oldNode.props, node.props)var oldElements = []var oldKeyed = {}var newKeyed = {}for (var i = 0; i < oldNode.children.length; i++) {oldElements[i] = element.childNodes[i]var oldChild = oldNode.children[i]var oldKey = getKey(oldChild)if (null != oldKey) {oldKeyed[oldKey] = [oldElements[i], oldChild]}}var i = 0var j = 0while (j < node.children.length) {var oldChild = oldNode.children[i]var newChild = node.children[j]var oldKey = getKey(oldChild)var newKey = getKey(newChild)if (newKeyed[oldKey]) {i++continue}if (null == newKey) {if (null == oldKey) {patch(element, oldElements[i], oldChild, newChild, isSVG)j++}i++} else {var recyledNode = oldKeyed[newKey] || []if (oldKey === newKey) {patch(element, recyledNode[0], recyledNode[1], newChild, isSVG)i++} else if (recyledNode[0]) {patch(element,element.insertBefore(recyledNode[0], oldElements[i]),recyledNode[1],newChild,isSVG)} else {patch(element, oldElements[i], null, newChild, isSVG)}j++newKeyed[newKey] = newChild}}while (i < oldNode.children.length) {var oldChild = oldNode.children[i]if (null == getKey(oldChild)) {removeElement(element, oldElements[i], oldChild)}i++}for (var i in oldKeyed) {if (!newKeyed[oldKeyed[i][1].props.key]) {removeElement(element, oldKeyed[i][0], oldKeyed[i][1])}}} else if (node.name === oldNode.name) {element.nodeValue = node} else {element = parent.insertBefore(createElement(node, isSVG),(nextSibling = element))removeElement(parent, nextSibling, oldNode)}return element}

具体的方法什么意思就不一一解释了,有一点要注意的是,这个库用了很多小套路,如果想要理解的话,最好先去好好理解下 JS 中的()是什么意思?

源码

太长就不放了,放个链接吧。戳。

其他类似的

其实类似的实现还有 preact ,不过 preact 大了一丢丢,但是在知名度和可靠性上肯定是 preact
遥遥领先的,本文只是用来学习,真正项目使用的话还是要慎重考虑的,优先考虑 react 和 preact 这些。

总结

写到这里感觉自己也是似懂非懂的了,一定是源码看的太少了……

以后继续加油,拜拜

hyperapp.js 一个轻量级的 react 实现相关推荐

  1. Day.js 一个轻量级的 JavaScript 时间日期处理库

    在项目中难免要去处理时间和日期,所以就一定会用到 Moment.js ,Moment.js 是一个大而全的 JS 时间库,使得我们处理时间和日期变得简便,但是 Moment.js 太重了(大约 200 ...

  2. Next.js---一个轻量级的 React SSR应用框架

    Next.js---一个轻量级的 React SSR应用框架 为什么要用Next.js Next.js介绍 优点 创建 项目目录结构 Next中的路由 如何实现跳转 带参跳转 路由钩子 使用 数据请求 ...

  3. 手把手带你用next搭建一个完善的react服务端渲染项目(集成antd、redux、样式解决方案)

    前言 本文参考了慕课网jokcy老师的React16.8+Next.js+Koa2开发Github全栈项目,也算是做个笔记吧. 源码地址 github.com/sl1673495/n- 介绍 Next ...

  4. 【React进阶-1】从0搭建一个完整的React项目(入门篇)

    这篇文章带领大家从零开始手动撸一个React项目的基础框架,集成React全家桶.万字长文,请各位有足够的时间时再来阅读和学习. 概述 平时工作中一直在用React提供的脚手架工具搭建React项目, ...

  5. React.js 小书 Lesson5 - React.js 基本环境安装

    React.js 小书 Lesson5 - React.js 基本环境安装 本文作者:胡子大哈 本文原文:http://huziketang.com/books/react/lesson5 转载请注明 ...

  6. 这是可用于下一个项目的React Native工具列表

    by Rajput Mehul 通过拉杰普特·梅胡尔(Rajput Mehul) 这是可用于下一个项目的React Native工具列表 (Here's a list of React Native ...

  7. 我决定切换到 Vue.js,不再使用 React!

    点击上方的终端研发部,右上角选择"设为星标" 作者:Gwenael P,前端工程师,Vue.js的狂热者 译者:弯月 现在,Vue.js 在 Github 上得到的星星数已经超过了 ...

  8. 帮你解剖Python的一个轻量级桌面GUI开发第三方库:Eel,让它体无完肤

    Python的一个轻量级桌面GUI开发第三方库:Eel 一.Eel介绍 二.资源库eel的安装 三.文件结构和简单的hello介绍 3.1 文件结构 3.2 代码:hello1.py 3.3 main ...

  9. 利用脚手架工具搭建一个新的react项目

    利用脚手架工具搭建一个新的react项目 一,工程架构 1.使用的是create-react-app脚手架工具搭建的工程架构 npm install create-react-app -g全局安装 c ...

最新文章

  1. 脸书 AI 识别误将黑人标记为「灵长类动物」
  2. java p7 数字签名,p7结构的数字信封 | 学步园
  3. golang 结构体和数据库表字段 反射自动映射 sqlmapper库 简介
  4. sscanf,sscanf_s及其相关用法
  5. linux里grep和egrep,fgrep的区别
  6. java 使用gzip压缩和解压 传输文件必备
  7. Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析(7)
  8. 测试两个主机之间的连通性_如何使用知行EDI系统模拟连通性测试?
  9. fdfs-文件上传信息返回详情
  10. 如何看懂luac -l -l 命令
  11. 小学计算机专业说课稿模板,小学信息技术说课稿模板汇总八篇
  12. Ubuntu桌面显示或隐藏回收站等图标
  13. 计算机的硬盘配额如何更改,磁盘配额怎么设置
  14. python 100天 pdf 最新版_GitHub - Nolan2018/Python-100-Days: Python - 100天从新手到大师
  15. 如何挖到人生当中第一本CNVD
  16. Netlogon 特权提升漏洞(CVE-2020-1472)原理分析与验证
  17. 单元测试的必要性?一文聊聊单元测试
  18. 物联网服务器协议命令,物联网使用HTTP协议传输数据
  19. 计算机专业的硕士犯罪,计算机犯罪研究
  20. appium自带的appium insepect

热门文章

  1. Sieve of Eratosthenes质数
  2. 字幕通-字幕翻译工具
  3. C# WinForm开发系列之c# 通过.net自带的chart控件绘制饼图,柱形图和折线图的基础使用和扩展
  4. 工业智能网关BL110应用之二十一: 如何添加LAN口采集的设备
  5. github资源收藏地
  6. 怎样unity调用大华摄像头
  7. bat脚本中获取上级目录_批处理bat命令--获取当前盘符和当前目录和上级目录
  8. ACW各种dp模板题 day29
  9. GitHub+Hexo搭建自己的Blog之(3)-主题配置(Next)
  10. CSS3实现div滑入滑出效果(从下往上)