框架对比
如果你正在阅读此页面,你可能已经使用过其他框架来构建应用程序,并且想知道 Mithril.js 是否能更有效地解决你的问题。
为什么不用 [你最喜欢的框架] 呢?
事实上,大多数现代框架都很快,适合构建复杂的应用程序,并且如果使用得当,它们也是可维护的。 现实中,有很多使用各种流行框架构建的高度复杂的应用程序的例子:Udemy 使用 Angular,AirBnB 使用 React,Gitlab 使用 Vue,Guild Wars 2 使用 Mithril.js(是的,在游戏内部!)。 显然,这些都是生产级别的框架。
根据经验,如果你的团队已经在另一个框架/库/技术栈上投入了大量精力,那么除非有充分的理由证明重写是值得的,否则坚持使用现有技术栈更有意义。 毕竟,重写的代价通常很高。
但是,如果你正在开始新的项目,不妨尝试一下 Mithril.js,哪怕只是为了看看其采用者从不到 10kb(gzip 压缩后)的代码中获得了多少价值。Mithril.js 被许多知名公司(例如 Vimeo、Nike、Fitbit)使用,并且它也为大型开源平台(例如 Lichess、Flarum)提供支持。
为什么使用 Mithril.js?
简而言之:因为 Mithril.js 注重实效。这个 10 分钟指南 就是一个很好的例子:学习组件、XHR 和路由只需要这么短的时间 - 这基本上就是构建实用应用所需的全部知识。
Mithril.js 的核心在于高效地完成有意义的任务。需要进行文件上传?文档会告诉你如何操作。身份验证?也有文档说明。退出动画?没问题。没有额外的库,没有黑魔法。
对比
React
React 是由 Facebook 维护的一个视图库。
React 和 Mithril.js 有很多相似之处。如果你已经学习了 React,那么你已经掌握了使用 Mithril 构建应用程序所需的几乎所有知识。
- 它们都使用虚拟 DOM、生命周期方法和基于 key 的节点协调。
- 它们都通过组件来组织视图。
- 它们都在视图中使用 JavaScript 作为流程控制机制。
React 和 Mithril.js 之间最明显的区别在于它们的范围。React 只是一个视图库,因此典型的基于 React 的应用程序依赖于第三方库来实现路由、XHR 和状态管理。使用基于库的方案允许开发人员自定义他们的技术栈,以精确地满足他们的需求。但另一方面,基于 React 的架构在项目与项目之间可能差异很大,并且这些项目更有可能超过 1MB 的大小限制。
Mithril.js 具有内置模块来处理常见需求(如路由和 XHR),并且 指南 演示了惯用用法。对于重视一致性和易于入门的团队来说,这种方法更可取。
性能
React 和 Mithril.js 都非常关心渲染性能,但采用的方法不同。过去,React 有两个 DOM 渲染实现(一个使用 DOM API,另一个使用 innerHTML
)。其即将推出的 fiber 架构引入了任务单元的调度和优先级划分。React 还有一个复杂的构建系统,可以禁用各种检查和错误消息以进行生产部署,以及各种特定于浏览器的优化。此外,还有几个面向性能的库,它们利用 React 的 shouldComponentUpdate
钩子和不可变数据结构库的快速对象相等性检查属性来减少虚拟 DOM 协调时间。React 的性能方法是设计相对复杂的解决方案。
Mithril.js 遵循少即是多的思想。它有一个更小、经过积极优化的代码库。理由是小代码库更容易审计和优化,并最终减少运行的代码量。
下面是库加载时间的比较,即解析和运行各个框架的 JavaScript 代码所需的时间。测试方法是在仅包含框架代码的脚本文件的首行添加 console.time()
,尾行添加 console.timeEnd()
。为了方便阅读,以下是在一台普通的 2010 年 PC 上,使用 Chrome 浏览器从文件系统运行捆绑脚本,并手动添加日志代码后取得的最佳 20 次测试结果:
React | Mithril.js |
---|---|
55.8 ms | 4.5 ms |
库加载时间在不会长时间保持打开状态的应用程序(例如,移动设备中的任何内容)中很重要,并且无法通过缓存或其他优化技术来改进。
鉴于这是一个微基准测试,且硬件配置会对结果产生显著影响,建议您自行进行测试。需要注意的是,Webpack 等打包工具可能会在计时器调用之前将依赖项移出,以模拟静态模块解析。因此,建议从已编译的 CDN 文件复制代码,或者从打包工具中打开输出文件,并手动将高分辨率计时器调用 console.time
和 console.timeEnd
添加到捆绑后的脚本中。避免使用 new Date
和 performance.now
,因为这些机制在统计上不够准确。
为了方便阅读,这里提供了在 Web 上使用 CDN 的基准测试版本:React 基准测试,Mithril.js 基准测试。请注意,我们正在对整个 Mithril.js 进行基准测试,而不是仅对渲染模块进行基准测试(这在范围上与 React 相当)。此外,由于需要从磁盘缓存中获取资源(每个资源约 2 毫秒),这种基于 CDN 的设置会产生一定的性能开销。因此,这里的数字并不完全准确,但足以观察到 Mithril.js 的初始化速度明显优于 React。
这是一个稍微更有意义的基准测试:测量创建 10,000 个 div(和 10,000 个文本节点)的脚本时间。同样,这里是 React 和 Mithril.js 的基准测试代码。它们的最佳结果如下所示:
React | Mithril.js |
---|---|
99.7 ms | 42.8 ms |
这些数据表明,Mithril.js 不仅初始化速度更快,而且在 React 准备就绪之前,可以处理超过 20,000 个虚拟 DOM 节点。
更新性能
更新性能可能比首次渲染性能更重要,因为更新可能在单页应用程序运行时发生多次。
DbMonster 是 Ember 团队开发的工具,可用于基准测试更新性能。它尽可能快地更新一个表,并测量每秒帧数 (FPS) 和 JavaScript 时间(最小、最大和平均值)。由于 FPS 计数受浏览器重绘时间和 setTimeout
钳制延迟的影响,评估起来可能比较困难。因此,平均渲染时间是最值得关注的指标。你可以比较 React 实现 和 Mithril.js 实现。示例结果如下所示:
React | Mithril.js |
---|---|
12.1 ms | 6.4 ms |
开发性能
需要记住的另一件事是,由于 React 在开发模式下添加了额外的检查和有用的错误消息,因此它在开发中的速度比上面基准测试中使用的生产版本慢。作为示例,这是使用 React 开发版本进行的 10,000 节点基准测试。
替代方案
一些项目(例如 Preact、react-lite、Inferno 和 Rax)声称与 React 具有 API 对等性(部分通过兼容层库实现),但它们并非完全兼容。例如,通常会移除 PropType 支持,有时不支持合成事件,并且某些 API 的语义也可能存在差异。请注意,这些库通常还包括它们自己的不属于官方 React API 的附加功能,如果有人决定切换回 React Fiber,这可能会在将来成为问题。
关于小下载大小(与 React 相比)的说法是准确的,但这些库中的大多数都比 Mithril.js 的渲染器模块略大。Preact 是唯一的例外。
需要注意的是,某些项目使用的基准测试已知已过时且存在缺陷,容易被利用,因此请谨慎对待过于激进的性能声明。Boris Kaul(一些基准测试的作者)详细撰写了关于如何进行 Web 框架基准测试的文章。需要记住的另一件事是,一些基准测试积极地使用高级优化功能,因此展示了 潜在的 性能,即在某些警告下可能的性能,但实际上不太可能,除非你积极地花时间检查整个代码库,识别优化候选者并评估优化警告带来的回归风险。
为了展示 典型的 性能特征,本比较页面中的基准测试以一种同类比较的方式实现,采用朴素且惯用的代码风格(即您通常编写代码的方式),并且不使用任何技巧或高级优化来人为地提升某个框架的性能。如果您认为此处的 DbMonster 实现可以编写得更加地道,欢迎提交 PR。
复杂性
与其他框架相比,React 和 Mithril.js 都具有相对较小的 API 表面,这有助于降低学习曲线。尽管可以使用纯 ES5 编写惯用的 Mithril.js 代码,且无需其他依赖项,但惯用的 React 代码通常依赖于复杂的工具链(例如 Babel、JSX 插件等)。这种复杂性也常见于 React 生态系统中,例如 Redux 中非标准的 object spread 语法、使用不可变数据结构的架构,以及热模块重新加载等附加功能。
尽管 Mithril.js 和其他框架也可以使用复杂的工具链,但 强烈 建议在使用 Mithril 时遵循 KISS 和 YAGNI 原则。
学习曲线
React 和 Mithril.js 的学习曲线都相对较小。React 的学习曲线主要涉及理解组件及其生命周期。Mithril.js 组件的学习曲线几乎相同。显然,Mithril.js 中有更多的 API 需要学习,因为 Mithril.js 还包括路由和 XHR,但学习曲线与学习 React、React Router 和像 superagent 或 axios 这样的 XHR 库非常相似。
惯用的 React 需要 JSX 及其警告的工作知识,因此还有与 Babel 相关的小学习曲线。
文档
React 文档清晰且编写良好,包括良好的 API 参考、入门教程以及涵盖各种高级概念的页面。不幸的是,由于 React 仅限于作为视图库,因此其文档没有探讨如何在实际应用程序的上下文中惯用地使用 React。因此,由于存在许多流行的状态管理库,使用 React 的架构在不同公司(甚至不同项目)之间可能存在显著差异。
Mithril.js 文档还包括 入门 教程、关于高级概念的页面以及广泛的 API 参考部分,其中包括输入/输出类型信息、各种常见用例的示例以及防止滥用和反模式的建议。它还包括一个速查表,供快速参考。
Mithril.js 文档还演示了在实际应用中常见用例的简单、贴近底层的解决方案,并适时地提醒开发者,Web 标准在某些方面已经可以与大型成熟的库相媲美。
Angular
Angular 是由 Google 维护的 Web 应用程序框架。
Angular 和 Mithril.js 差异很大,但它们有一些相似之处:
- 两者都支持组件化。
- 两者都有一系列用于 Web 应用程序各个方面的工具(例如,路由、XHR)。
Angular 和 Mithril.js 之间最明显的区别在于它们的复杂性。这可以最容易地在视图的实现方式中看到。Mithril.js 视图是纯 JavaScript,并且流程控制是通过 JavaScript 内置机制(例如三元运算符或 Array.prototype.map
)完成的。另一方面,Angular 实现了一个指令系统来扩展 HTML 视图,以便可以在 HTML 属性和插值中评估类似 JavaScript 的表达式。为了实现这一点,Angular 内置了一个用 JavaScript 编写的解析器和编译器。如果这还不够复杂,实际上有两种编译模式(一种默认模式,用于动态生成 JavaScript 函数以提高性能,以及 一种较慢的模式,用于处理内容安全策略限制)。
性能
多年来,Angular 在性能方面取得了很大进展。Angular 1 使用了一种称为脏检查的机制,由于需要不断地 diff 大型 $scope
结构,因此该机制往往会变慢。Angular 2 使用模板更改检测机制,该机制的性能更高。但是,即使 Angular 进行了改进,Mithril.js 通常也比 Angular 快,这是因为 Mithril.js 的小代码库大小便于审计。
由于几个原因,很难比较 Angular 和 Mithril.js 之间的加载时间。首先,Angular 1 和 2 实际上是完全不同的代码库,并且这两个版本都受到官方支持和维护(并且目前绝大多数 Angular 代码库仍然使用版本 1)。第二个原因是 Angular 和 Mithril.js 都是模块化的。在这两种情况下,都可以删除给定应用程序中未使用的框架的很大一部分。
话虽如此,已知最小的 Angular 2 捆绑包是一个 29kb 的 "hello world" 示例 (https://www.lucidchart.com/techblog/2016/09/26/improving-angular-2-load-times/),它使用 Brotli 算法压缩(使用标准 gzip 压缩为 35kb),并且移除了 Angular 的大部分实用功能。相比之下,Mithril.js hello world(包括核心功能及所有组件)的 gzip 压缩后约为 10kb。
此外,Angular 和 Mithril.js 等框架是为复杂的应用程序设计的。因此,如果应用程序需要使用 Angular 的所有 API,则需要下载数百 KB 的框架代码,而不仅仅是 29kb。
更新性能
DbMonster 是 Ember 团队开发的工具,可用于基准测试更新性能。它尽可能快地更新一个表,并测量每秒帧数 (FPS) 和 JavaScript 时间(最小、最大和平均值)。由于 FPS 计数受浏览器重绘时间和 setTimeout
钳制延迟的影响,评估起来可能比较困难。因此,平均渲染时间是最值得关注的指标。你可以比较 Angular 实现 和 Mithril.js 实现。这两种实现都是朴素的(即没有优化)。示例结果如下所示:
Angular | Mithril.js |
---|---|
11.5 ms | 6.4 ms |
复杂性
Angular 在它提供的工具数量(以各种指令和服务形式)方面优于 Mithril.js,但它也复杂得多。将 Angular 的 API 表面 与 Mithril.js 的 API 表面 进行比较。您可以自行判断哪个 API 更易于理解,并且更符合您的需求。
Angular 2 有很多概念需要理解:在语言级别,Typescript 是推荐的语言,除此之外,还有 Angular 特定的模板语法,例如绑定、管道、“安全导航运算符”。你还需要了解诸如模块、组件、服务、指令等架构概念,以及在何处使用什么。
学习曲线
如果我们进行同类比较,Angular 2 和 Mithril.js 具有相似的学习曲线:在这两种情况下,组件都是架构的核心方面,并且两者都具有合理的路由和 XHR 工具。
话虽如此,Angular 有很多概念需要学习,比 Mithril 更多。它为许多通常可以轻松实现的功能提供了特定的 API。例如,复数化本质上就是条件判断,而 "required" 验证也只是一个简单的相等性检查。Angular 模板还包含多个抽象层,用于模拟 JavaScript 在 Mithril.js 中本地执行的操作。例如,Angular 的 ng-if
/ngIf
指令使用自定义的解析器和编译器来评估表达式字符串,并模拟 lexical scoping(词法作用域)……等等。Mithril.js 往往更加透明,因此更容易推理。
文档
Angular 2 文档提供了广泛的入门教程,以及另一个实现应用程序的教程。它还有各种高级概念指南、速查表和样式指南。不幸的是,目前,API 参考还有很多不足之处。一些 API 要么没有文档,要么没有提供 API 可能用于什么的上下文。
Mithril.js 文档包括 入门 教程、关于高级概念的页面以及广泛的 API 参考部分,其中包括输入/输出类型信息、各种常见用例的示例以及防止滥用和反模式的建议。它还包括一个速查表,供快速参考。
Mithril.js 文档还演示了在实际应用中常见用例的简单、贴近底层的解决方案,并适时地提醒开发者,Web 标准在某些方面已经可以与大型成熟的库相媲美。
Vue
Vue 是一个类似于 Angular 的视图库。
Vue 和 Mithril.js 有很多不同之处,但它们也有一些相似之处:
- 它们都使用虚拟 DOM 和生命周期方法。
- 两者都通过组件来组织视图。
Vue 2 使用 Snabbdom 的一个分支作为其虚拟 DOM 系统。此外,Vue 还提供用于路由和状态管理的工具作为单独的模块。Vue 与 Angular 非常相似,同样提供了指令系统、基于 HTML 的模板和逻辑流指令。与 Angular 不同,Vue 实现了一个 monkeypatching(动态修改) 响应式系统,该系统会覆盖组件数据树中的原生方法。相比之下,Angular 1 使用脏检查和 digest/apply(消化/应用循环) 循环来实现类似的效果。与 Angular 2 类似,Vue 将 HTML 模板编译为函数,但编译后的函数看起来更像 Mithril.js 或 React 视图,而不是 Angular 的编译渲染函数。
在同等条件下,Vue 比 Angular 小得多,但不如 Mithril.js 小。Vue 核心的 gzip 压缩后约为 23kb,而 Mithril.js 中等效的渲染模块的 gzip 压缩后约为 4kb。两者都具有相似的性能特征,但基准测试通常表明 Mithril.js 略快。
性能
下面是库加载时间的比较,即解析和运行各个框架的 JavaScript 代码所需的时间。测试方法是在仅包含框架代码的脚本文件的首行添加 console.time()
,尾行添加 console.timeEnd()
。为了方便阅读,以下是在一台普通的 2010 年 PC 上,使用 Chrome 浏览器从文件系统运行捆绑脚本,并手动添加日志代码后取得的最佳 20 次测试结果:
Vue | Mithril.js |
---|---|
21.8 ms | 4.5 ms |
库加载时间在不会长时间保持打开状态的应用程序(例如,移动设备中的任何内容)中很重要,并且无法通过缓存或其他优化技术来改进。
更新性能
DbMonster 是 Ember 团队开发的工具,可用于基准测试更新性能。它尽可能快地更新一个表,并测量每秒帧数 (FPS) 和 JavaScript 时间(最小、最大和平均值)。由于 FPS 计数受浏览器重绘时间和 setTimeout
钳制延迟的影响,评估起来可能比较困难。因此,平均渲染时间是最值得关注的指标。你可以比较 Vue 实现 和 Mithril.js 实现。这两种实现都是朴素的(即没有优化)。示例结果如下所示:
Vue | Mithril.js |
---|---|
9.8 ms | 6.4 ms |
复杂性
Vue 在很大程度上受到 Angular 的启发,并且具有 Angular 所做的许多事情(例如,指令、过滤器、双向绑定、v-cloak
),但也具有受到 React 启发的事情(例如,组件)。从 Vue 2.0 开始,除了单文件组件和各种基于 webpack 的语言转译插件之外,还可以使用 hyperscript/JSX 语法编写模板。Vue 提供双向数据绑定和一个可选的 Redux 式状态管理库,但与 Angular 不同,它不提供样式指南。多种实现方式可能会导致长期项目的架构变得混乱。
Mithril.js 的概念要少得多,并且通常根据组件和数据层来组织应用程序。Mithril.js 中的所有组件创建样式都仅使用本机 JavaScript 功能输出相同的 vnode 结构。依赖于该语言的直接结果是更少的工具和更简单的项目设置。
文档
Vue 和 Mithril.js 都有很好的文档。两者都包括带有示例的良好 API 参考、入门教程以及涵盖各种高级概念的页面。
但是,由于 Vue 的多种做事方式的方法,某些事情可能没有得到充分的记录。例如,他们的 hyperscript 被大量掩盖了。
如果某个主题涉及 Mithril 范围之外的内容,Mithril.js 文档通常会提供非常详尽的说明。例如,当某个主题涉及第三方库时,Mithril.js 文档会详细说明第三方库的安装步骤。Mithril.js 文档还经常演示在实际应用中常见用例的简单、贴近底层的解决方案,并适时地提醒开发者,Web 标准在某些方面已经可以与大型成熟的库相媲美。
与 Vue 的教程相比,Mithril.js 的教程涵盖了更多内容。Vue 教程 在介绍了其核心 API 后,最终实现了一个简单的本地任务列表。Mithril.js 的 10 分钟指南 涵盖了其大部分 API,甚至介绍了实际应用程序的关键方面,例如从服务器获取数据和路由。如果这还不够,还有一个 更长、更彻底的教程。