我是标题党吗?是,但也不是。以图为证。
上图表示了vue, react 以及 imba 在 todo 这个项目中拥有60个 todoItem 不同进行 crud 操作的表现。可以看到 imba 达到了每秒操作5w次以上。如果你也想试一试该测试,可以访问Todos Bench 。测试使用的是 Benchmark.js 。
imba 简单介绍
imba 是一种新的编程语言,可以编译为高性能的 JavaScript。可以直接用于 Web 编程(服务端与客户端)开发。
下面是语法:
// 自定义标签 tag App // 属性 prop items // 方法定义 def addItem if @input.value items.push(title: @input.value) @input.value = "" def toggleItem item item:completed = !item:completed // 挂载 Imba.mount(element, into) // 如果没有第二个参数,默认挂载到 document.body 上面 Imba.mount <App.vbox items=[] -> <form.bar :submit.prevent.addItem> <input@input> <button> 'add' <ul> for item in items <li .done=item:completed :tap.toggleItem(item)> item:title
可以看出作者喜欢 ruby 以及 pug与,偏向于缩进类风格(个人并不是很喜欢这种语法风格)。具体语法可以参考 imba 文档 。当然了,因为可以编译成js,所以服务端编译成 js 进行node开发也是可以实现的。
imba 框架极速的性能基础
任何一个实现的性能优化都有其理论基础,那么 imba 性能那么快的基础究竟是什么呢?答案也就是 memoized DOM(记忆DOM)。
理论基础
浏览器的 DOM 操作可以说是浏览器最终要的功能,无论框架是基于虚拟 DOM 或者是真实 DOM,最终离不开操作 DOM 对象。
HTML DOM 是浏览器定义了访问和操作 HTML 文档的标准方法。但是操作 DOM 的接口是 JavaScript。但是浏览器通常会把 js 引擎和渲染引擎分开实现。也就是页面实际渲染部分是和解析js部分分开的。
借着《高性能的 JavaScript》话说,如果把 DOM 和 js 各自想象为岛屿。他们需要一座桥进行沟通。所以每一次执行 DOM 操作就过桥一次。
那我们先谈谈虚拟DOM,虚拟DOM 的性能提升在于是将 DOM 的对比放在了js层。进而通过对比不同之处来进行实际的 DOM 渲染。也就是说,其实虚拟DOM 并没有“实际”的性能收益,桥仍旧还在那边。仅仅在 js引擎需要过桥的那边找到了一位聪明睿智的大叔,对过桥的人和过桥的货物进行优化和限制(虚拟DOM 高性能的diff算法,状态批量更新)。
那么 memoized DOM 又是怎么做的呢?把 DOM 节点的控制直接放入内存之中。类似于此类优化.
function getEls(sel) { // 设置缓存 if (!getEls.cache) getEls.cache = {}; // 如果缓存中存在 el,直接返回 if (getEls.cache[sel]) { return getEls.cache[sel]; } // 没有去通过 DOM 查询 const r = document.querySelectorAll(sel || '☺'), length = r.length; // 缓存并返回元素节点 return getEls.cache[sel] = (length == 1) ? r[0] : r; }
我们可以测试一下。这里我写一个 getElsByDocument 以及 simplePerTest。
// 直接通过 querySelectorAll 获取节点 function getElsByDocument(sel) { const r = document.querySelectorAll(sel || '☺'), length = r.length; return length == 1 ? r[0] : r; } // 简单性能测试 function simplePerTest(fn, el) { const fnName = fn.name console.time(fnName) // 2000 次操作 for(let i = 0,len = 2000; i < len; i++) { fn(el) } console.timeEnd(fnName) }
这个缓存的节点查询可要比 querySelectorAll 快了 140倍以上啊,随着 img 节点越多,得到的性能提升也越高啊。如果imba 框架中所有的节点都在内存中呢?同时,我们还会得到一个 js 运行时优化( GC 的大量减少),因为虚拟DOM 要维护一个树,在进行多次 crud 之后就会产生大量无用对象从而导致浏览器进行 GC,而 memoized DOM 在多次 crud 不会进行多次 GC。(可能会在渲染引擎中 GC?但我感觉渲染引擎中GC 要比JS 中影响要小很多。挖个坑,研究完渲染引擎再来探讨一下)
框架实践
实例如下所示:
tag Component def render <self> <h1.title> "Welcome" <p.desc> "I am a component"
上面的自定义组件会编译成下面的js
var Component = Imba.defineTag('Component', function(tag){ tag.prototype.render = function (){ var $ = this.$; // 返回dom return this.setChildren($.$ = $.$ || [ createElement('h1',$,0,this).flag('title').setText("Welcome"), createElement('p',$,1,this).flag('desc').setText("I am a component") ]).synced(); }; });