温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

移动端JS的引擎有哪些

发布时间:2021-10-18 15:11:59 来源:亿速云 阅读:214 作者:iii 栏目:web开发

这篇文章主要介绍“移动端JS的引擎有哪些”,在日常操作中,相信很多人在移动端JS的引擎有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”移动端JS的引擎有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

JS 引擎选型要点

JavaScript 作为世界上最热门的脚本语言,有着非常多的引擎实现:有 Apple 御用的  JavaScriptCore,有性能最强劲的 V8,还有最近热度很高的 QuickJS......如何从这些 JS  引擎里选出最适合的?我个人认为要有几个考量:

  • 性能:这个没话说,肯定是越快越好

  • 体积:JS 引擎会增加一定的包体积

  • 内存占用:内存占用越少越好

  • JavaScript 语法支持程度:支持的新语法越多越好

  • 调试的便捷性:是否直接支持 debug?还是需要自己编译实现调试工具链

  • 应用市场平台规范:主要是 iOS 平台,平台禁止应用集成带 JIT 功能的虚拟机

比较麻烦的是,上面的几个点都不是互相独立的:

比如说开启 JIT 的 V8 引擎,性能肯定是最好的,但它引擎体积就很大,内存占用也很高;在包体积上很占优势的 QuickJS,由于没有 JIT  加持,和有 JIT 的引擎比起来平均会有 5-10 倍的性能差距。

下面我会综合刚刚提到的几个点,并选择了 JavaScriptCore,V8,Hermes 和 QuickJS 这 4 个  JSVM,说说它们的优点和特点,再谈谈他们的不足。

JS 引擎功能大比拼

1.JavaScriptCore

移动端JS的引擎有哪些

              mobile_JSVM_JSC

JavaScriptCore 是 WebKit 默认的内嵌 JS 引擎,wikipedia 上都没有独立的词条,只在 WebKit  词条的三级目录[1]里介绍了一下,个人感觉还是有些不像话,毕竟也是老牌 JS 引擎了。

由于 WebKit 是 Apple 率先开源的,所以 WebKit 引擎运用在 Apple 自家的 Safari 浏览器和 WebView 上,尤其是  iOS 系统上,因为 Apple 的限制,所有的网页只能用 WebKit 加载,所以 WebKit 在 iOS 上达到了事实垄断,作为 WebKit  模块一部分的 JSC,顺着政策春风,也「基本」垄断了 iOS 平台的 JS 引擎份额。

垄断归垄断,其实 JSC 的性能还是可以的。

很多人不知道 JSC 的 JIT 功能其实比 V8 还要早,放在十几年前是最好的 JS 引擎,只不过后来被 V8 追了上来。而且 JSC 有个重大利好,在  iOS7 之后,JSC 作为一个系统级的 Framework 开放给开发者使用,也就是说,如果你的 APP 使用 JSC,只需要在项目里 import  一下,包体积是 0 开销的!这点在今天讨论的 JS 引擎中,JSC 是最能打的。

虽然开启 JIT 的 JSC 性能很好,但是只限于苹果御用的 Safari 浏览器和 WKWebView,只有这两个地方 JIT  功能才是默认开启的,如果在项目里直接引入 JSC,JIT 功能是关闭的。为什么这么做呢?RednaxelaFX 大佬[2] 给出过非常专业的解释[3]:

JIT  编译需要底层系统支持动态代码生成,对操作系统来说这意味着要支持动态分配带有“可写可执行”权限的内存页。当一个应用程序拥有请求分配可写可执行内存页的权限时,它会比较容易受到攻击从而允许任意代码动态生成并执行,这样就让恶意代码更容易有机可乘。

Apple 出于安全上的考虑,禁止了第三方 APP 使用 JSC 时开启 JIT,这些特点在 React Native 的 JS Runtime  页面[4]也有过相关的解释。不过在实际应用中,不做重 CPU 的运算只当胶水语言使用,JSC 还是绰绰有余了。

上面的讨论都是针对 iOS 系统的,在 Android 系统上,JSC 的表现就不尽人意了。

JSC 并没有对 Android 机型做很好的适配,虽然可以开启 JIT,但是性能表现并不好,这也是 Facebook 决心制作 Hermes  的一个原因,具体的性能对比分析可见本文的 Hermes 小节。

最后再说说 JSC 的调试支持情况。如果是 iOS 平台,我们可以直接用 Safari 的 debbuger 功能调试,如果是 Android  平台,目前我还没有找到一个很好的真机调试方法。

综合来看,JavaScriptCore 在 iOS 平台上有非常明显的主场优势,各个指标都是很优秀的,但在 Android  上因为缺乏优化,表现并不是很好。

2.V8

移动端JS的引擎有哪些

mobile_JSVM_V8

V8,我想我不用过多解释了,JavaScript 能有如今的地位,V8 功不可没。性能没得说,开启 JIT 后就是业内最强(不止是 JS),有很多介绍  V8 的文章,我这里就不多描述了,我们这里说说 V8 在移动端的表现。

同样作为 Google 家的产品,每一台 Android 手机上都安装了基于 Chromium 的 WebView,V8 也一并捆绑了。但是 V8 和  Chromium 捆绑的太紧密了,不像 iOS 上的 JavaScriptCore 封装为系统库可以被所有 App 调用。这就导致你想在 Android 上用  V8 还得自己封装,社区比较出名的项目是 J2V8[5],提供了 V8 的 Java bindings 案例。

V8 性能没得说,Android 上可以开启 JIT,但这些优势都是有代价的:开启 JIT 后内存占用高,并且 V8 的包体积也不小(大概 7 MB  左右),如果作为只是画 UI 的 Hybrid 系统,还是有些奢侈了。

我们再说说 V8 在 iOS 上的集成。

V8 在 2019 年推出了 JIT-less V8[6],也就是关闭 JIT 只使用 Ignition interpreter 解释执行 JS  文件,那么我们在 iOS 上集成 V8 就成了可能,因为 Apple 还是支持接入只有解释器功能的虚拟机引擎的。但是个人认为关闭了 JIT 的 V8 接入  iOS 价值不大,因为只开启解释器的话,这时候的 V8 和 JSC 的性能其实是差不多的,引入反而会增加一定的体积开销。

V8 还有一个有意思的特性很少人提及,那就是——堆快照(Heap snapshots),这个是 V8 在 2015[7]  年就支持的功能,但是社区里很少有人讨论它。

堆快照是什么原理呢?一般来说 JSVM 启动后,第一步往往是解析 JS 文件,这个还是比较耗时的,V8 支持预先生成 Heap  snapshots,然后直接加载到堆内存中,快速的获得 JS 的初始化上下文。跨平台框架 NativeScript[8] 就利用了这样的技术,可以让 JS  的加载速度提升 3 倍,技术细节可以看他们的博文[9]。

移动端JS的引擎有哪些

V8_heap_snapshots

V8 真机调试也需要引入第三方库,Android 端社区上有人对 J2V8 做了 Chrome 调试协议的扩展,即 J2V8-Debugger[10]  项目,iOS 我没有找到相关的项目,可能需要自己实现一套扩展。

综合来看 V8 的确是 JSVM 中的性能王者,Android 端使用时可以完全发挥它的威力,但是 iOS 平台因为主场劣势,并不是很推荐。

3.Hermes

移动端JS的引擎有哪些

mobile_JSVM_hermes

Hermes 是 FaceBook 2019 年中旬开源的一款 JS 引擎,从 release[11] 记录可以看出,这个是专为 React Native  打造的 JS 引擎,可以说从设计之初就是为 Hybrid UI 系统打造。

Hermes 一开始推出就是要替代原来 RN Android 端的 JS 引擎,即 JavaScriptCore(因为 JSC 在 Android  端表现太拉垮了)。我们可以理一下时间线,FaceBook 自从 2019-07-12 宣布 Hermes 开源[12]后,jsc-android[13]  的维护信息就永远的停在了 2019-06-25[14],这个信号暗示得非常的明显:JavaScriptCore Android  我们不再维护啦,大家都去用我们做的 Hermes 啊。

最近 Hermes 已经计划伴随 React Native 0.64 版本登录 iOS 平台了,但是 RN 版本更新 blog  还没有出,大家可以看看我之前对 Apple 开发者协议的解读:Apple Agreement 3.3.2 规范解读,在这里我就不多说了。

Hermes 的特点主要是两个,一个是不支持 JIT,一个是支持直接生成/加载字节码,我们在下面分开讲一下。

Hermes 不支持 JIT 的主要原因有两个:加入 JIT 后,JS 引擎启动的预热时间会变长,一定程度上会加长首屏  TTI[15](页面首次加载可交互时间),现在的前端页面都讲究一个秒开,TTI 还是个挺重要的测量指标。另一个问题上 JIT  会增加包体积和内存占用,Chrome 内存占用高 V8 还是要承担一定责任的。

因为不支持 JIT,Hermes 在一些 CPU 密集计算的领域就不占优势了,所以在 Hybrid 系统里,最优的解决方案就是充分发挥  JavaScript 胶水语言的作用,CPU 密集的计算(例如矩阵变换,参数加密等)放在 Native 里做,算好了再传递给 JS 表现在 UI  上,这样可以兼顾性能和开发效率。

Hermes 最引人瞩目的就是支持生成字节码了,我在之前的博文《? 跨端框架的核心技术到底是什么?》也提到过,Hermes 加入 AOT  后,Babel、Minify、Parse 和 Compile 这些流程全部都在开发者电脑上完成,直接下发字节码让 Hermes 运行就行,我们直接用个 demo  演示一下。

移动端JS的引擎有哪些

Hermes

先写个 test.js 的文件,里面随便写点啥都行;然后编译一下 Hermes 的源码,编译过程直接按文档[16]来就行,我这里就略过了。

首先 Hermes 支持直接解释运行 JS 代码,就是正常的 JS 加载编译运行流程。

hermes test.js

我们可以加入 -emit-binary 参数尝试一下生成 Bytecode 的功能:

hermes -emit-binary -out test.hbc test.js

然后就会生成一份 test.hbc 字节码文件:

移动端JS的引擎有哪些

hermes_bytecode

最后我们可以让 Hermes 直接加载运行 test.hbc 文件:

hermes test.hbc

客观评价一下 Hermes 的字节码,首先省去了在 JS 引擎里解析编译的流程,JS 代码的加载速度将会大大加快,体现在 UI 上就是 TTI  时间会明显缩短;另一个优势 Hermes 的字节码在设计时就考虑了移动端的性能限制,支持增量加载而不是全量加载,对内存受限的中低端 Android  机更友好;不过字节码的体积会比原来的 JS 文件会大一些,但是考虑到 Hermes 引擎本身体积就不大,综合考虑下来这些体积增量还是可以接受的。

关于详细的 Hermes 性能测试情况,网上有两篇文章写的比较好:一篇是 React Native Memory profiling: JSC vs V8  vs Hermes[17],可以看到在 Android 设备上 Hermes 的表现还是很优异的,而 JSC 的表现非常拉垮:

移动端JS的引擎有哪些

JSCvsV8vsHermes

另一篇是携程的文章:携程对 RN 新一代 JS 引擎 Hermes 的调研,可以看出 Hermes 综合成绩最高(JSC 还是一样的拉垮):

移动端JS的引擎有哪些

JSVM_CPU_Performance

说完性能我们再说说 Hermes 的 JS 语法支持情况。

Hermes 主要支持的是 ES6 语法,刚开源时不支持 Proxy,不过 v0.7.0[18] 已经支持了。他们的团队也比较有想法,不支持 with  eval() 等这种属于设计糟粕的 API,这种设计的权衡我个人还是比较认同的。

最后我们谈谈 Hermes 的调试功能。

目前 Hermes 已经支持了 Chrome 的调试协议,我们可以直接用 Chrome 的 debugging 工具直接调试 Hermes  引擎,具体的操作可见文档:Debugging JS on Hermes using Google Chrome's DevTools[19]

综合来看,Hermes 是一款专为移动端 Hybrid UI System 打造的 JS 引擎,如果要自建一套 Hybrid 系统,Hermes  是一个非常好的选择。

4.QuickJS

移动端JS的引擎有哪些

mobile_JSVM_quickjs

正式介绍 QuickJS 前我们先说说它的作者:Fabrice Bellard。

软件界一直有个说法,一个高级程序员创造的价值可以超过 20 个平庸的程序员,但 Fabrice Bellard  不是高级程序员,他是天才,在我看来他的创造力可以超过 20 个高级程序员,我们可以顺着时间轴[20]理一下他创造过些什么:

  • 1997年,发布了最快速的计算圆周率的算法,此算法是 Bailey-Borwein-Plouffe  公式的变体,前者的时间复杂度是O(n^3),他给优化成了O(n^2),使得计算速度提高了43%,这是他在数学上的成就

  • 2000 年,发布了 FFmpeg,这是他在音视频领域的一个成就

  • 2000,2001,2018 三年三度获得国际混淆 C 代码大赛

  • 2002 年,发布了TinyGL,这是他在图形学领域的成就

  • 2005 年,发布了 QEMU,这是他在虚拟化领域的成就

  • 2011 年,他用 JavaScript 写了一个 PC 虚拟机 Jslinux,一个跑在浏览器上的 Linux 操作系统

  • 2019 年,发布了 QuickJS,一个支持 ES2020 规范的 JS 虚拟机

当人和人之间的差距差了几个数量级后,羡慕嫉妒之类的情绪就会转变为崇拜了,Bellard 就是一个这样的人。

收复一下心情,我们来看一下 QuickJS 这个项目。QuickJS 继承了 Fabrice Bellard 作品的一贯特色——小巧而又强大。

QuickJS 体积非常小,只有几个 C 文件,没有乱七八糟的第三方依赖。但是他的功能又非常完善,JS 语法支持到  ES2020[21],Test262[22] 的测试显示,QuickJS 的语法支持度比 V8 还要高。

移动端JS的引擎有哪些

test262

那么 QuickJS 的性能如何呢?QuickJS 官上有个基准测试[23],综合比较了多款 JS 引擎对同一测试用例的跑分情况。下面是测试结果:

移动端JS的引擎有哪些

JSVM_Benchmark

结合上面的表格和个人的一些测试,可以简单的得出一些结论:

  • 开启 JIT 的 V8 综合评分差不多是 QuickJS 的 35 倍,但是在同等主打轻量的 JS 引擎中,QuickJS 的性能还是很耀眼的

  • 在内存占用上,QuickJS 远低于 V8,毕竟 JIT 是是吃内存的大户,而且 QuickJS 的设计对嵌入式系统很友好(Bellard 成就奖杯 ?  再 +1)

  • QuickJS 和 Hermes 的跑分情况是差不多的,我私下做了一些性能测试,这两个引擎的表现也很相近

因为 QuickJS 的设计,我不经好奇他和 Lua 的性能对比如何。

Lua 是一门非常小巧精悍的语言,在游戏领域和 C/C++ 开发中一直充当胶水语言的作用。

我个人写了一些测试用例,发现 QuickJS 和 Lua 的执行效率也是差不多的,后来在网上找到一篇博文 Lua vs  QuickJS[24],这个老哥也做了一些测试,结论也是它俩的性能差不多,在部分场景 Lua 会比 QuickJS 快一些。

官方文档里有提到,QuickJS 支持生成字节码[25],这样可以免去 JS 文件编译解析的过程。

我一开始以为 QuickJS 和 Hermes 一样,可以直接生成字节码,然后交给 QuickJS 解释执行。后来自己编译了一下才发现,QuickJS  的作用机制和 Hermes 还不太一样:qjsc 生成字节码的 -e 和 -c 选项,都是先把 js 文件生成一份字节码,然后拼到一个 .c  文件里,大概长下面的这个样子:

#include <quickjs/quickjs-libc.h>  const uint32_t qjsc_hello_size = 87;  // JS 文件编译生成的字节码都在这个数组里 const uint8_t qjsc_hello[87] = {  0x02, 0x04, 0x0e, 0x63, 0x6f, 0x6e, 0x73, 0x6f,  0x6c, 0x65, 0x06, 0x6c, 0x6f, 0x67, 0x16, 0x48,  0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,  0x6c, 0x64, 0x22, 0x65, 0x78, 0x61, 0x6d, 0x70,  0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c, 0x6c,  0x6f, 0x2e, 0x6a, 0x73, 0x0e, 0x00, 0x06, 0x00,  0x9e, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00,  0x14, 0x01, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x39,  0xf1, 0x00, 0x00, 0x00, 0x43, 0xf2, 0x00, 0x00,  0x00, 0x04, 0xf3, 0x00, 0x00, 0x00, 0x24, 0x01,  0x00, 0xd1, 0x28, 0xe8, 0x03, 0x01, 0x00, };  int main(int argc, char **argv) {   JSRuntime *rt;   JSContext *ctx;   rt = JS_NewRuntime();   ctx = JS_NewContextRaw(rt);   JS_AddIntrinsicBaseObjects(ctx);   js_std_add_helpers(ctx, argc, argv);   js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);   js_std_loop(ctx);   JS_FreeContext(ctx);   JS_FreeRuntime(rt);   return 0; }

因为这是个 .c 文件,想跑起来还得再编译一次生成二进制文件。

从字节码这个设计点来看,QuickJS 和 Hermes 的定位还是不太一样的。

虽然直接生成字节码可以大大减少 JS 文本文件的解析时间,但是 QuickJS 还是更偏嵌入式一些,生成的字节码放在一个 C  文件中,还需要进行编译才能运行;Hermes 为 React Native  而生,生成的字节码一开始就考虑到分发功能(热更新就是一个应用场景),支持字节码的直接加载运行,不需要再编译一次。

上面主要还是对性能的考量,下面我们看看开发体验。

首先是 QuickJS 的调试功能支持。到目前为止(2021-02-22),QuickJS 还没有官方的调试器,也就是说 debugger  语句会被忽略,社区有人实现了一套基于 VSCode 的调试器支持vscode-quickjs-debug[26],但是会对 QuickJS  做一些定制,个人还是蛮期待官方支持某个调试器协议的。

从 集成 的角度上看,社区上已经有了 iOS[27] 和 Android[28] 的示例项目,可以拿来用来参考接入到自己的工程中。

综合来看,QuickJS 是一款潜力非常大的 JS 引擎,在 JS 语法高度支持的前提下,还把性能和体积都优化到了极致。在移动端的 Hybrid UI  架构和游戏脚本系统都可以考虑接入。

选型思路

1.单引擎

单引擎的意思就是 iOS 端和 Android 端统一采用一个引擎,这样做的话在 JS 层差异可以抹平,不容易出现同一份 JS 代码在 iOS  上运行是好的,Android 上就出错的奇异 BUG。结合市面上的跨端方案,大概有下面三种选型:

  • 统一采用 JSC:这个是 React Native 0.60 之前的方案

  • 统一使用 Hermes:这个是 React Native 0.64 之后的设计方案

  • 统一采用 QuickJS:QuickJS 体积很小,可以用来制作非常轻量的 Hybrid 系统

上面看出没有统一采用 V8,这个就是我前面说的,V8 在 iOS 平台没有主场优势,关闭 JIT 后性能和 JSC  差不多,还会增大包体积,并不是很划算。

2.双引擎

双引擎也很好理解,就是 iOS 端和 Android  端各用各的,优点是可以发挥各自的主场优势,缺点是可能会因为平台不一致导致双端运行结果不统一,现在的方案有这么几种:

  • iOS 用 JSC,Android 用 V8:Weex,NativeScript 都是这样的,可以在包体积和性能上有较好的均衡

  • iOS 用 JSC,Android 用 Hermes:React Natvie 现如今的方案

  • iOS 用 JSC,Android 用 QuickJS:滴滴的跨端框架 hummer[29] 就是这样的设计

从选型上看,iOS 上都选择了 JSC,Android 各有各的选择,倒是充分发挥了两个平台的特色 : )

3.调试

无论是单引擎还是双引擎,集成后的业务开发体验也很重要。对于自带 debugger 功能的引擎来说一切都不在话下,但是对于没有实现调试协议的引擎来说,缺少  debugger 还是会影响体验的。

但不是也没有办法,一般来说我们可以曲线救国,类似于 React Native 的 Remote JS Debugging 的思路:

我们可以加个开关,把 JS 代码通过 websocket 传送到 Chrome 的 Web Worker,然后用 Chrome 的 V8  进行调试。这样做的优势是可以调整一些业务上的 BUG,劣势就是又会引入一个 JS 引擎,万一遇到一些引擎实现的 BUG,就很难 debug  了。不过好在这种情况非常非常少见,我们也不能因噎废食对吧。

到此,关于“移动端JS的引擎有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

js
AI