从Ember Classic到微光组件的道路
#javascript #网络开发人员 #ember #glimmer

在2019年底,Ember.js Octane edition发布了,其中包括一种新的写作方式:闪光组件。现在,组件现在将组件类从闪光包装而不是灰烬扩展。除了导入这种较小的差异外,功能上还有很大的差异。本文将介绍差异,您想升级的原因以及在大型代码库中解决此问题的升级策略。

经典与光线

闪烁的组件可以看作是经典组件的纤细版本。大多数生命周期钩被拆除。参数是在Glimmer的自动跟踪反应性上范围内构建的。没有更多的HTML包装元素。他们使用本地类语法。并清理了许多经典的剩菜。

下面的示例实现了一个组件,该组件将文本复制为“参数”时,在单击按钮时将其复制到剪贴板上。当使用经典的灰烬组件时,可以如下实现:

// copy-to-clipboard.js
import Component from '@ember/component';
import { set } from '@ember/object';

export default Component.extend({
  isCopied: false,
  actions: {
    async copyToClipboard() {
      await navigator.clipboard.writeText(this.text);
      set(this, 'isCopied', true);
    }
  }
});
<!-- copy-to-clipboard.hbs -->
<button {{action 'copyToClipboard'}}>
  {{if isCopied 'Copied!' 'Click to copy text'}}
</button>

使用微光的相同组件看起来像这样:

// copy-to-clipboard.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class CopyToClipboard extends Component {
  @tracked isCopied = false;

  @action
  async copyToClipboard() {
    await navigator.clipboard.writeText(this.args.text);
    this.isCopied = true;
  }
}
<!-- copy-to-clipboard.hbs -->
<button {{on 'click' this.copyToClipboard}}>
  {{if this.isCopied 'Copied!' 'Click to copy text'}}
</button>

闪烁的组件可以在使用本机类语法时使用装饰器。传递到组件(这里text)和局部状态(isCopied)之间存在明显的分离。定期分配表达式可用于更新状态,该状态应得益于闪光自动跟踪,该状态应触发模板rerenders。还有这个小例子中没有说明的a lot more improvements

为什么要迁移到微光组件?

每个代码迁移都需要工程时间,无法用于构建新产品以出售给客户。因此,要使企业投资重构和迁移,必须有另一个好处。在最新的主要版本中,Ember中的经典组件仍得到支持,为什么要升级?以下利益使我们值得进行权衡。

一种做事的方式

新代码的微光组件成为自发行Ember Octane以来的标准实践。这导致我们的代码库包含两种组件类型。在包含两者的代码库中工作时,这增加了额外的心理开销。您必须意识到与哪种类型的组件合作并进行相应的更改。对于新手Ember的人来说,这可能会令人困惑。

接近本地JavaScript体验

与经典组件相比,

闪烁的组件几乎包含的灰烬特定代码实践很少。这使人们更容易开始来自不同背景的Ember。每个JavaScript开发人员都应该能够在我们的代码库中启动并相对快速起步。

渲染性能

从开发人员的角度来看,以前的观点很不错。但是,这也是对客户的好处。闪烁的组件呈现substantially faster比经典组件。

打字稿支持

打字稿已经证明,它可以留在更广泛的JavaScript生态系统中。它引起了人们的兴趣,并将其作为most popular JavaScript flavour的位置。

2022年,埃伯(Ember)以专门的核心团队认可了阿比亚奇(official TypeScript support)。闪烁的组件解锁了Ember中打字稿支持的全部潜力。使用Glint检查模板类型也在主动开发中。令人兴奋!

未来的代码库

ember.js开发不会停滞。对于当前组件模型的新改进,已经取得了进展。 first-class component templates的RFC已于2022年被接受并合并,并将为Ember用户提供新的好处。首先采用微弱的组件,我们为接下来的事情做好了准备。

迁移策略

虽然您可以直接跳入并开始一个一个组件,但我们决定采取不同的策略。对于较小的代码库,迁移组件一一可以是一种可行的方法,但是对于大型代码库来说,这可能很麻烦(想想100k+代码行)。对于一个人来说,这项工作太大了,并且副作用太多。这就是为什么我们将迁移工作分为九个里程碑的原因。

1.组件的本机JavaScript类语法

历史上,Ember使用对象语法定义组件。作为一般的JavaScript中的类语法成熟,它也成为微光组件的标准。 Ember中的经典组件为对象和类语法提供了支持。这使得切换到类语法成为迈向微光组件的重要第一步。

ember提供了一个codemod,将对象语法转换为类语法。这为我们节省了大量时间。通过这样做,我们的发展经验也得到了极大的改进。

2.没有隐含的

微光组件中的参数捆绑在args对象中。这避免了与组件自己的范围中的自定义定义属性发生冲突,并在本地定义和传递的参数之间创建了明确的区别。

闪烁的组件模板在使用参数和this.前缀时,在访问备份类的属性时,通过使用@前缀来反映这一点。这种工作方式在经典组件中也得到了支持,即使参数与本地属性相同。这意味着迁移是无障碍的,幸运的是,也有一个codemod。但是,CodeMod可以区分参数和本地属性,并且将在以后的阶段进行清理。

3.将简单组件弄清楚

通过查看所有组件并检查哪些不使用或有限的经典组件功能,我们能够识别一组易于迁移到微光的组件。示例是没有任何JavaScript逻辑的组件,因为Glimmer介绍了仅使用模板的组件的概念,这些概念在没有明确备份类的情况下工作。这是低悬挂的水果,通过直接使它们摆脱困境,我们避免了其他阶段的不必要的开销。

4.删除外部HTML语义

经典组件具有wrapping HTML element,它在微光组件中不存在。准备此次拆卸的第一步是摆脱对该包装元素产生影响的所有属性。在大多数情况下,此用法是classNames属性,该属性将CSS类添加到包装元素中。

转换是通过直接在组件的模板中添加这些属性来完成的。

5.使组件无标记

可以通过将tagName设置为空字符串来删除经典组件的包装元素,因此可以将其名称为“无标记”组件。 ember-decorators包装中的@tagName装饰器可用于执行此操作。这使得在以后的阶段很容易发现和清理。

在此阶段使组件无标记仍然引入破坏变化,我们将其与添加装饰器一起修复。

我们注意到的一个常见的陷阱是,灰烬组件上的属性无处可设置并且被丢弃。在一线示中,您需要明确地分辨出必须放置传递的属性的位置。这可以通过使用...attributes语法来完成。通常,这会导致样式错误作为类或iD的设置。我们的视觉测试对于检测这些问题很有用。如果您对我们如何设置视觉测试感兴趣,请查看our talk at EmberFest 2022

第二个问题是,依赖于此包装元件的生命周期不再被调用。这些生命周期事件包含元素参考,例如didInsertElement。为了迁移这些,我们使用了render-modifiers软件包。自Glimmer和Octane以来,有新的方法可以像使用构造函数和驱动器,编写自定义修饰符或使用资源一样封装这种逻辑。为了限制范围,我们选择了这一努力。

6.去除混合物

混合蛋白是在不同组件上共享常见代码的一种方式。在一片微光中,他们不再支持。我们审查了我们的Mixins,并列出了一种重组方式,因为在大多数情况下,Mixins可以用更具体的共享代码替换。

常见情况是模板格式化逻辑,可以将其制成助手,共享常数,可以移动到单独的文件,并且可以在不需要Ember上下文的情况下分离的实用程序函数。对于不太适合共享代码的任何标准方式的用法,我们选择了Chris Garrett在“Do You Need EmberObject?”中描述的定制类装饰器。

7.删除弃用的生命周期事件

在第5阶段中,deprecated lifecycle hooks的子集已被删除。还有其他其他与包装元素没有绑定的,例如didRenderwillUpdate等。可以使用与第5阶段使用的类似策略进行删除这些生命周期事件。一般来说,它们也可以用本机捕获器代替。

8.删除观察者

在Ember中劝阻观察者的使用时间很长一段时间。当有更好的替代方案,可能导致性能问题并且很难调试时,它们通常被过度使用。有了微光的组件,@observes装饰器也不再支持。

重构观察者可能是不平凡的,因为它要求您以不同的方式考虑状态更新。 Glimmer没有对更改做出反应,而是引入了自动折磨,这允许标记应触发UI更新的输入。可以通过使用Getters和AutoTracking来代替一些用法。在其他情况下,render-modifiers软件包的did-update修饰符可以用作替换。在这里编写自定义修饰符也是一个选项。

9.最后———从Glimmer延伸!

现在已经删除了所有经典特定功能,现在是时候扩展闪烁的组件基类,而不是Ember Classic One。

通过进行此更改,参数将移至args对象,而不是组件的本地范围。必须调整支持类中的用法以在两者之间使用此步骤。要考虑的一个边缘情况是,通过更改此范围,它们不再覆盖组件中的默认值。可以通过编写本机getter来解决这一问题,该getter从args对象返回参数并归还默认值,以防参数未传递。

同样,模板中的参数用法也必须更新以指示范围的差异。必须为参数设置@前缀,因为codemod没有像第2阶段所述的那样处理这一点。

最后,可以删除5阶段的tagName装饰器,因为微光组件总是无标记的。

结论

本文提供了一种将大型灰烬代码库从经典迁移到微光组件的策略。遵循此组件迁移可确保代码库在过去不会陷入困境。更好的是,他们解锁了现代功能Ember提供的,并且在此刻进行了新的功能!