龙空技术网

Ember.js:最快的 DOM 渲染引擎 Glimmer.js 火了!

高级前端进阶 5968

前言:

眼前姐妹们对“js监听dom渲染”大体比较讲究,看官们都需要分析一些“js监听dom渲染”的相关知识。那么小编在网络上汇集了一些有关“js监听dom渲染””的相关内容,希望各位老铁们能喜欢,小伙伴们一起来了解一下吧!

家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

高级前端‬进阶

今天给大家带来的主题是来自 Ember.js 团队的快速轻量级 UI 组件库,即 Glimmer.js。话不多说,直接进入主题!

1.Glimmer.js 是什么

Glimmer.js 是一个构建在 Ember.js 渲染引擎 Glimmer VM (Virtual Machine)之上的轻量级组件库。 Glimmer.js 针对小应用程序包大小和最大运行时性能进行了大量优化,因此非常适合不需要像 Ember.js 这样全功能框架且体积敏感的场景。

Glimmer.js 提供定义、组合和渲染组件的功能,并使 DOM 与组件树的内部状态保持同步。 Glimmer.js 使用来自 Ember 项目的久经考验的命令行界面工具 (CLI) Ember CLI 来帮助开发者创建和管理应用程序。

同时编码语言上,Glimmer.js 是用 TypeScript 编写的,用它构建的应用程序也是如此。Glimmer.js 模板使用类似 Handlebars 的语法,比如下面的例子:

{{#each measurementLists.first key="@index"}}  <MeasurementRow    @value={{measurement.value}}    @parameter={{measurement.parameter}}    @unit={{measurement.unit}}  />{{/each}}

这些模板最终会被编译为 Glimmer VM(是一个在浏览器内的 JavaScript VM 中运行的成熟 VM)处理的字节码,并转换为 DOM 操作。

Glimmer.js 具有以下优秀特征,这些优势特性不仅助力 Ember.js 成为优秀的前端框架,同时也成为单独使用 Glimmer.js 时开发者的利器 :

虚拟机模式:Glimmer 是最快的 DOM 渲染引擎之一,为初始化渲染和更新提供卓越的性能。 Glimmer 的架构类似于虚拟机 (VM),可将模板编译为底层代码,以便尽可能快地运行同时不牺牲易用性。用途广泛:Glimmer 组件可以与目前所有主流技术堆栈共存,因此可以在 Glimmer 组件中开发新功能而无需重写现有代码。久经沙场: Glimmer VM 为 Ember 的组件提供支持,同时提供了一个充分测试的全栈框架

Glimmer.js 项目的相关包由 Glimmer 开发人员直接导入和使用,主要包括以下两个组成部分:

@glimmer/component : 定义 Glimmer 组件基类和组件生命周期@glimmer/tracking : 定义 Glimmer 属性变化跟踪系统2.Glimmer的底层原理

Glimmer 的底层逻辑是:模板代表了一种用于构建和更新 DOM 的声明式的编程语言。 通过围绕模板构建 Web UI 作为中心抽象,开发者可以使用来自编程语言和编译器的先进技术来显著提高 Web 应用程序在实践中的性能。

图片来自:Sandip Roy

正因为如此,与传统的 JavaScript 库相比,Glimmer 的架构与 clang/LLVM 或 javac/JVM 等编译器工具链有更多共同点。在高层次上,Glimmer 由两部分组成:

编译器 Compiler:将模板转换为优化的二进制字节码。运行时 Runtime:运行时负责执行字节码并将其指令翻译成诸如创建 DOM 元素或实例化 JavaScript 组件类之类的命令编译器

编译器负责将程序的模板转换为 Glimmer 二进制字节码。因为 Glimmer 是一个优化编译器,它必须了解程序中的所有模板,以便了解它们如何协同工作。 这与像 Babel 这样的转译器形成鲜明对比,比如 Babel 可以单独转换每个文件。

当编译器遍历应用程序并发现模板时,它会解析每个模板并创建一个中间表示 (IR)。 IR 类似于最终的字节码程序,但包含对可能尚未发现的外部对象(其他模板等)的符号引用。

中间表示 IR

一旦所有模板都被解析为 IR,编译器执行最后一次解析符号地址并将最终操作码写入共享二进制缓冲区。 在本机编译器术语中,开发者可以将此视为生成最终可执行文件的链接步骤。

此二进制可执行文件作为 .gbx 文件保存到磁盘,可以提供给浏览器并使用运行时执行。

到这一步还没有结束,字节码程序将在需要与 JavaScript 互操作的浏览器中进行执行。 例如,用户将模板工具函数实现为 JavaScript 函数。 在编译的程序中,如果用户键入 {{formatDate user.createdAt}},那么如何知道调用什么函数?

在编译期间,Glimmer 将为每个引用的外部对象分配唯一的数字标识符,这些标识符又被称为句柄,它们是如何引用实时JavaScript 对象,例如二进制字节码中的函数。

图片来自:

在浏览器中执行 Glimmer 字节码时,运行时会请求句柄为 4 的对象,而不是请求formatDate 函数本身。为了满足这个要求,编译器还产生了一个称为外部模块表的数据结构,它将每个句柄映射到其关联的 JavaScript 对象。

例如,假设编译一个调用两个帮助函数的模板,formatDate 和 pluralize。 这些帮助函数分别获得分配的句柄 0 和 1。 为了允许运行时将这些句柄转换为正确的函数对象,编译器可能会生成如下映射:

// module-table.tsimport formatDate from 'app/helpers/format-date';import pluralize from 'app/helpers/pluralize';export default [formatDate, pluralize];

使用此数据结构,可以轻松实现将句柄转换为适当的活动对象的功能:

import moduleTable from './module-table';function resolveHandle<T>(handle: number): T {  return moduleTable[handle];}
运行时

Glimmer 运行时读取已编译字节码并将其渲染在浏览器中。

在高层次上,运行时从渲染组件树开始。渲染流程从一个根组件开始,然后向下遍历其模板。 在执行此操作时,它会保存一组优化的字节码,稍后在重新渲染应用程序时可以使用这些字节码。 这些更新代码忽略纯静态的内容,例如 :DOM 元素和具有静态输入的帮助函数或组件,并添加动态内容的更新指令。

Glimmer运行时

Glimmer 同时为组件、帮助函数和值设置一个引用树和验证器。 这些允许 Glimmer 有效地检查组件层次结构的给定子树的状态是否已更改,如果是则重新渲染。

在更高层次上,运行时以 3 个主要概念为中心:

组件(Component)引用(References)验证器(Validators)

Glimmer 中的组件表示一个可重用的 UI 单元,它封装了行为和外观。 因为 Glimmer VM 是一个高度可配置的运行时,组件的确切含义由宿主环境和一个或多个组件管理器决定。

引用是代表纯(无副作用)计算结果的稳定对象,其中计算结果可能会随时间变化。 Glimmer 创建引用来表示模板中使用的值,以便可以在多个组件之间有效地共享,并在基础值发生变化时有效地更新。

验证器是稳定的对象,可以为计算结果的新鲜度提供一定的保证。 验证器可以组合,这样,如果任何子验证器的值已更改,父验证器也将被标记为已更改。

Glimmer 应用程序由一棵组件树组成,从一个根组件开始,渲染对值、帮助函数和模板中可能包含的其他组件的任何引用。 可以把它想象成入口组件,类似于许多编程语言中的 main 函数。 然而,与那些语言不同的是,Glimmer VM 的级别相当低,并且不提供定义此组件的约定。 这允许宿主环境定义它们认为合适的约定和入口组件,并使用 Glimmer 的 renderMain 函数渲染。

在渲染时,Glimmer 为它创建的每个组件和引用添加验证器。 这些验证器组合在一起,使得给定组件的验证器代表其包含的任何引用或组件的验证器的并集,从而产生与组件树匹配的验证器树。

宿主环境可以提供不同的方法来使状态无效(通过验证器),例如 Ember 的 set() 函数或来自 Glimmer.js 的 @tracked。

import Component from '@glimmer/component';import { tracked } from '@glimmer/tracking';import { action } from '@ember/object';export default class CounterComponent extends Component {  @tracked count = 0;  // @tracked  get timesTen() {    return this.count * 10;  }  @action  plusOne() {    this.count += 1;  }}

重新渲染时,Glimmer 遍历组件树,检查每个组件的验证器以查看是否需要重新渲染。 如果是,它遍历组件的子树,但如果不是,则完全跳过该组件。 这导致非常有效的重新渲染,因为默认情况下只会重新渲染已更改的部分。

3.使用 Glimmer.js

Glimmer.js 支持与 Ember.js 集成使用或者单独使用。如果是在 Ember.js 项目中使用,需要首先安装相关依赖:

npm install --dev @glimmer/component @glimmer/tracking

然后使用下面的命令来创建一个 Ember.js 组件:

ember generate my-component -gc

如果是单独使用,需要首先安装 Ember.js 相应的构建工具:

npm install -g ember-cli

接着使用下面的命令创建一个Glimmer项目:

ember new my-app -b @glimmerx/blueprint

最后通过下面的命令启动开发服务器进入正常的开发流程:

cd my-app/ && npm run start

下面代码是一个 Glimmer.js 的组件示例:

4.本文总结

本文主要和大家介绍下来自 Ember.js 团队的快速轻量级 UI 组件库,即 Glimmer.js。相信通过本文的阅读,大家对 Ember.js 、Glimmer.js都会有一个初步的了解。

因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!

参考资料

标签: #js监听dom渲染