使用imba.io框架得到比 vue 快50倍的性能基准


Posted in Javascript onJune 17, 2019

使用imba.io框架得到比 vue 快50倍的性能基准

我是标题党吗?是,但也不是。以图为证。

使用imba.io框架得到比 vue 快50倍的性能基准

上图表示了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)
}

使用imba.io框架得到比 vue 快50倍的性能基准

这个缓存的节点查询可要比 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();
  };
});

仔细观察一下这里的函数,你会看到该组件在第一次调用渲染时,将使用 createElement 创建两个子节点,并设置它们的属性并且缓存。第二次或者第一万次调用时,children-array将被缓存,不会发生任何调用。

// 在第一次调用时候 $.$不存在 $.$会等于 后面的数组
// 第二次调用 $.$ 是存在的,无运行时消耗
$.$ = $.$ || 数组

其中查看源码,我们可以看到 setChildren 函数都是对真实DOM 进行了操作。获取之前的DOM节点进行一系列操作后将当前节点返回并缓存。

tag.prototype.setChildren = function (new$,typ){

  var old = this._tree_;
  
  if (new$ === old && (!(new$) || new$.taglen == undefined)) {
    return this;
  };
  if (!old && typ != 3) {
    this.removeAllChildren();
    appendNested(this,new$);
  } else if (typ == 1) {
    var caret = null;
    for (var i = 0, items = iter$(new$), len = items.length; i < len; i++) {
      caret = reconcileNested(this,items[i],old[i],caret);
    };
  } else if (typ == 2) {
    return this;
  } else if (typ == 3) {
    var ntyp = typeof new$;
      
    if (ntyp != 'object') {
      return this.setText(new$);
    };
      
    if (new$ && new$._dom) {
      this.removeAllChildren();
      this.appendChild(new$);
    } else if (new$ instanceof Array) {
      if (new$._type == 5 && old && old._type == 5) {
        reconcileLoop(this,new$,old,null);
      } else if (old instanceof Array) {
        reconcileNested(this,new$,old,null);
      } else {
        this.removeAllChildren();
        appendNested(this,new$);
      };
    } else {
      return this.setText(new$);
    };
  } else if (typ == 4) {
    reconcileIndexedArray(this,new$,old,null);
  } else if (typ == 5) {
    reconcileLoop(this,new$,old,null);
  } else if ((new$ instanceof Array) && (old instanceof Array)) {
    reconcileNested(this,new$,old,null);
  } else {
    // what if text?
    this.removeAllChildren();
    appendNested(this,new$);
  };  
  this._tree_ = new$;
  return this;
};

如果我们使用了动态属性。代码如下

tag Component
  def render
    <self>
      <h1.title> "Welcome"
      # 有 50% 几率 拥有 red class
      <p.desc .red=(Math.random > 0.5)> "IMBA"

可以得到如下代码,详细查看可以看出,imba 提取了可变量,放入了 synced 函数中,每次渲染中只会执行 synced 里面的数据,所以依然会得到极高的渲染速度

var Component = Imba.defineTag('Component', function(tag){
  tag.prototype.render = function (){
    var $ = this.$;
    return this.setChildren($.$ = $.$ || [
      _1('h1',$,0,this).flag('title').setText("Welcome"),
      _1('p',$,1,this).flag('desc').setText("Roulette")
    ],2).synced((
      $[1].flagIf('red',Math.random() > 0.5)
    ,true));
  };
});

精确的抽取不可变量,然后无需虚拟DOM 计算,同时对于真实DOM 还进行了缓存,我们可以看出 memoized DOM 与 虚拟DOM 不同,memoized DOM 是具有实际的性能收益。

imba 框架“虚假”的性能测试

我们在上面看到了 imba 框架的理论基础,那么他是否真的比vue快50倍?当然不是,这也就是在上面说我是标题党的原因。

浏览器的运行机制

浏览器本身只能达到 60 fps( 1 秒刷新了60次 )。当然了,其实对于体验而言,60fps的体验已经差不多够用了,也就是浏览器渲染上大概需要 17ms 去渲染一次。事实上无论是每秒操作dom 5w次还是 1000次,浏览器渲染引擎也只会记录当前的脏数据。然后在需要渲染时候再进行重绘与重排。

真实世界的内存限制

面对 memoized DOM 的缓存优化以及更少 GC 带来的运行时提升,我们需要更多内存来对每一个 dom节点进行缓存。这个在初始化渲染时有大量的消耗。同时我们的浏览器执行速度和渲染速度已经足够快了,虚拟DOM已经完全够用了。

imba 框架与浏览器的畅想

Google io 大会 chorme Portals 技术

单页应用程序(Single Page Applications,SPA)提供了很好的页面交互,但代价是构建的复杂性更高,多页面应用程序(Multi-page Applications,MPA)更容易构建,但最终会在页面之间出现空白屏幕。

Portals 结合了这两者的优势,主要用于改进网页交互体验,目标是无缝导航。它类似于 iframe ,内嵌在网页上,但可以导航到页面内容上。用户在一个页面跳转另一个内容时,虽然 URL 相应地发生变化,但是不需要打开另一个窗口,此时该内容标记的 Portals 会变成原来页面的顶级页面,同时原来页面在其后保持主进程地位。现场演示了这对于购物体验的极大便利,此外还有对漫画这类单页面应用的演示。

使用imba.io框架得到比 vue 快50倍的性能基准

 js引擎 与 渲染引擎的关联

在之前,浏览器 js引擎和渲染引擎是没有任何关联的,我们去写动画只能通过 setTimeout 或者 setInterval,更加没有办法知道浏览器什么时候处于空闲状态,但是随着时间的发展,我们可以通过 requestAnimationFrame 和 requestIdleCallback。requestAnimationFrame 要求浏览器在下次重绘之前调用指定的回调函数更新动画。requestIdleCallback方法将在浏览器的空闲时段期间对要调用的队列函数进行执行。

那么内置DOM 操作是否能够在js引擎中,是否能够减少过桥的性能消耗或者完全把桥打通。让我们拭目以待。

Javascript 相关文章推荐
JavaScript 构造函数 面相对象学习必备知识
Jun 09 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
Mar 02 Javascript
jQuery unbind()方法实例详解
Jan 19 Javascript
详解MVC如何使用开源分页插件(shenniu.pager.js)
Dec 16 Javascript
Angularjs中的验证input输入框只能输入数字和小数点的写法(推荐)
Aug 16 Javascript
Vue的移动端多图上传插件vue-easy-uploader的示例代码
Nov 27 Javascript
vue采用EventBus实现跨组件通信及注意事项小结
Jun 14 Javascript
详解基于electron制作一个node压缩图片的桌面应用
Jan 29 Javascript
详解VUE调用本地json的使用方法
May 15 Javascript
Node.js控制台彩色输出的方法与原理实例详解
Dec 01 Javascript
小程序富文本提取图片可放大缩小
May 26 Javascript
vuex实现购物车功能
Jun 28 Javascript
通过js给网页加上水印背景实例
Jun 17 #Javascript
JavaScript 扩展运算符用法实例小结【基于ES6】
Jun 17 #Javascript
通过实例解析js简易模块加载器
Jun 17 #Javascript
如何从头实现一个node.js的koa框架
Jun 17 #Javascript
javascript定时器的简单应用示例【控制方块移动】
Jun 17 #Javascript
深入解析koa之异步回调处理
Jun 17 #Javascript
JS中数据结构与算法---排序算法(Sort Algorithm)实例详解
Jun 17 #Javascript
You might like
自动把纯文本转换成Web页面的php代码
2009/08/27 PHP
有关php运算符的知识大全
2011/11/03 PHP
php利用imagemagick实现复古老照片效果实例
2017/02/16 PHP
Input 特殊事件onpopertychange和oninput
2009/06/17 Javascript
Js实现动态添加删除Table行示例
2014/04/14 Javascript
jQuery图片切换插件jquery.cycle.js使用示例
2014/06/16 Javascript
利用a标签自动解析URL分析网址实例
2014/10/20 Javascript
JS实现表单中checkbox对勾选中增加边框显示效果
2015/08/21 Javascript
BootStrap日期控件在模态框中选择时间下拉菜单无效的原因及解决办法(火狐下不能点击)
2016/08/18 Javascript
JavaScript仿网易选项卡制作代码
2016/10/06 Javascript
谈谈因Vue.js引发关于getter和setter的思考
2016/12/02 Javascript
js eval函数使用,js对象和字符串互转实例
2017/03/06 Javascript
浅谈AngularJS中$http服务的简单用法
2018/05/15 Javascript
vuejs 动态添加input框的实例讲解
2018/08/24 Javascript
解决百度Echarts图表坐标轴越界的方法
2018/10/17 Javascript
vue中监听路由参数的变化及方法
2019/12/06 Javascript
vue+koa2搭建mock数据环境的详细教程
2020/05/18 Javascript
Vue切换组件实现返回后不重置数据,保留历史设置操作
2020/07/21 Javascript
[47:04]EG vs RNG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
Python中的默认参数详解
2015/06/24 Python
Python绘制频率分布直方图的示例
2019/07/08 Python
Python Selenium 之数据驱动测试的实现
2019/08/01 Python
python批量修改交换机密码的示例
2020/09/22 Python
Python3.7安装PyQt5 运行配置Pycharm的详细教程
2020/10/15 Python
一款利用html5和css3实现的3D滚动特效的教程
2015/01/04 HTML / CSS
德国旅游网站:weg.de
2018/06/03 全球购物
幼儿园家长会欢迎词
2014/01/09 职场文书
1亿有多大教学反思
2014/05/01 职场文书
单方离婚协议书范本(2014版)
2014/09/30 职场文书
开展党的群众路线教育实践活动个人对照检查材料
2014/11/05 职场文书
2014年药房工作总结
2014/11/22 职场文书
2014年设计师工作总结
2014/11/25 职场文书
小学生差生评语
2014/12/29 职场文书
校长师德表现自我评价
2015/03/05 职场文书
质量整改通知单
2015/04/21 职场文书
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS