Ember.js: [META]绣花准备

创建于 2020-08-18  ·  17评论  ·  资料来源: emberjs/ember.js

如今,兼容模式下的绣花可以在新应用程序和许多现有应用程序中使用。 在staticComponents模式下使用Embroider更加困难,这对于获得splitAtRoutes模式的好处是必要的。

Embroider存储库中的问题#501跟踪作为Ember.js版本的一部分来稳定Embroider所需的其余问题。

该问题跟踪了人们在将基于路径的代码拆分(“刺绣就绪”)实际使用Ember with Embroider作为受支持的选项之前需要采取的步骤。 尽管有许多种使用Embroider的精细方法(包括一个兼容性模式,它几乎没有提供具体的好处,但是默认情况下对于将Ember本身迁移到Embroider而言很重要),但此问题的重点是能够在普通的Ember应用程序中使用Embroider并获得好处基于路由的代码拆分。

技术要求

Embroider README中所述,为了启用基于路由的代码拆分( splitAtRoutes ),应用程序必须能够启用以下标志:

  • [] staticAddonTestSupportTrees
  • [] staticAddonTrees
  • [] staticHelpers
  • [] staticComponents

如果插件或应用程序在存在这些标志的情况下无法工作,则它们将使用“经典动态功能”。

MVP:弃用并替换(component dynamicString)

对于绣花准备就绪(“ MVP”)的第一个目标,我们需要消除在尝试在实际应用程序中启用静态标志时发现的最常见的障碍。

对于MVP里程碑,对于所有支持这些标志的生态系统插件来说,这都是非目标

相反,目标是使应用程序具有到splitAtRoutes的合理过渡路径,并且可以在该模式下构建大量的,重要的应用程序。 这意味着默认蓝图中包含的所有插件必须已经从经典的动态功能中迁移出来。 这也意味着在现实应用程序中经常使用的插件(例如Ember并发)一定不能使用经典的动态功能。

staticComponents

这是最重要的静态标志,它的要求对MVP目标构成了最大的障碍。

为了启用staticComponents ,应用程序(包括其插件)必须没有对(component dynamicString)的所有使用。

重要的是,允许应用程序及其附加组件在staticComponents模式下使用(component "static string")

在实践中,这意味着我们需要从模式迁移走这样的

{{#let (component this.componentName) as | Component |}}

相反,当前将字符串作为其公共API一部分的附加组件将需要使用组件,这意味着该附加组件将需要迁移到一种要求其用户提供组件调用的方法,而不是字符串。

这是一个特别棘手的情况,因为this.component被定义为this.componentName = <code i="29">scaffolding/${dasherize(csId!)}/${dasherize(this.args.feature)}</code> 。 正是这种情况恰恰是我们需要考虑并认真推出过渡战略的原因。

至少,为了允许插件从(component dynamicString)迁移,我们将需要创建不允许调用动态组件的component关键字的新版本。

此外,我们需要修复一个错误,该component关键字相同的工作方式。 这需要尽快完成,因为一旦人们开始尝试从(component dynamic)迁移到其他地方,人们很可能会意外地迁移到<dynamic> ,观察到它会继续工作。

行动项目:

  • []设计并发布仅支持静态字符串的(component)新版本(需要RFC)
  • []修复了允许尖括号调用行为类似于Ember中的动态组件调用的错误(错误修复程序,也许是一个亲密的API)

staticHelpers

staticHelpers标志不会降低Ember模板的表现力,但是这意味着应用程序帮助程序的整个列表在加载程序的模块集中将不可用。

我们假设这将对Ember应用程序产生破坏性影响,但问题比staticComponents少得多。 我们目前不希望staticHelpers影响MVP目标,但是当我们尝试将应用程序升级到splitAtRoutes ,情况可能会改变。 如果发生这种情况,我们将需要评估有问题的用例,并考虑使用新的API来促进迁移。

staticAddonTreesstaticAddonTestSupportTrees

当前的假设是这些标志与大多数惯用的Ember应用程序和插件兼容,因此对于MVP目标的要求不会造成任何实质性问题。

下一步

在达到MVP目标之后,该问题仍将作为跟踪问题。 在实现MVP目标之后,我们将需要促进完整的生态系统迁移并改善动态用例的人机工程学(保留对代码拆分的支持)。 模板导入很可能有助于实现这些目标。

Help Wanted

最有用的评论

@NullVoxPopuli @jherdman太棒了

通常,思考人们需要做什么而不是(component dynamicString)是他们需要import(component "staticString")某个地方

选项包括:

  • 组件的调用者将执行(component "staticString")
  • 具有动态性的代码将枚举可能性,将其放入地图中,并使用(get componentMap dynamicString)将其删除

现在您可能在想: (get components dynamicString)会比(component dynamicString)好吗? 答案是componentMap必须在某处构建,并且规则递归地应用。

假设您要编写的组件允许您编写<InputField @type="text" /><InputField @type="checkbox" /> (例如HTML中的<input> )。

input-field模板看起来像这样:

{{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
  {{component (get components @type)}}
{{/let}}

当您以这种方式编写代码时,Embroider可以看到它只需要在包中包含两个组件。 您是否这样写过:

{{component (concat "inputs/" <strong i="33">@type</strong> "-field")}}

(是的,类似的事情很常见)

则Embroider无法轻松分析代码并限制捆绑软件中将包含的组件。 如果您使用JavaScript进行工作(这很常见),则情况甚至更糟:

export default class extends Component {
  get innerComponent() {
    return `inputs/${this.args.type}-field`;
  }
}

使用此模板:

{{component this.innerComponent}}

这是一个很小的调整,但使Embroider可以确定实际使用的组件。

所有17条评论

减轻过渡成本的一种方法是允许插件静态地指示其参数中的哪个参数对应于组件调用者传递的静态字符串。

作为spitball(其中better-component是static component关键字名称的占位符)。

{{better-component <strong i="8">@arg</strong> staticString=true}}

这将允许插件指示仅当调用者提供静态字符串时,特定的“动态调用”才有效。

实际上,只有在很多动态组件案例是由直接的“传递给插件的字符串参数”引起的情况下,才应该这样做。

如果这是此问题的主题,则为Idk,但我一直在定期尝试找出关于emberclear的全部静态信息,并一直在跟踪问题及其解决方案。

https://github.com/NullVoxPopuli/emberclear/pull/784

因此,如果人们遇到问题,也许安倍公司的文件可以帮忙吗? Idk

另外,我对刺绣感到非常兴奋,一旦实现了完全的静态性,我就已经制定了宏伟的计划
https://github.com/emberjs/rfcs/issues/611

我们的应用程序是一个动态的内容交付系统。 内容创建者从“活动元素”中组合内容,每个活动元素都有一个对应的Ember Component,其名称在Ember Data模型上定义。 该系统的心脏或多或少是这样的:

// example model definition
export default class TextElement extends Model {
  _componentName = 'text-element';
}
  {{#each (sort-by "position" @activityElements) as |activityElement|}}
    {{component (get activityElement "_componentName")}}

我对以上内容的阅读表明,这是(component dynamicString)情况。 准确吗?

我对上述内容的阅读表明,这是(组件dynamicString)方案。 准确吗?

什么是@activityElements ,在哪里定义?

这将是传递到控制器组件中的Ember数据模型实例的数组。

我们还对动态组件FYI进行了大量投资。 去年我在这里问过这个问题: https

我们也是。 从字面上看,我们事先并不知道将在应用程序中使用的组件。 它们位于数据库中(这当然使它们可更改),在后端进行编译,然后按需发送到前端。 无法为我们使用动态component帮助程序就像无法在数组上进行每个操作一样。 我真的希望这样的用例能够有所发展。 如果我的应用程序无法正常工作,因为我没有所需的动力,我绝对不会在意代码拆分/摇晃树。

可以针对该场景使用所有有效组件的地图吗?

例如:

{{#let (hash
   Foo=(import 'path/to/foo')
   Etc=...
) as |validComponents|
}}
  {{component (get validComponents @someDynamicValue)}}
{{/let}}

就像,您不能真正拥有完整的动态组件,因为您只能渲染应用程序中的内容-创建列表以选择要渲染的内容也将有助于调试。 “哦,这个值不是有效成分之一”

可以针对该场景使用所有有效组件的地图吗?

这肯定会涵盖我们的用例。

@NullVoxPopuli @jherdman太棒了

通常,思考人们需要做什么而不是(component dynamicString)是他们需要import(component "staticString")某个地方

选项包括:

  • 组件的调用者将执行(component "staticString")
  • 具有动态性的代码将枚举可能性,将其放入地图中,并使用(get componentMap dynamicString)将其删除

现在您可能在想: (get components dynamicString)会比(component dynamicString)好吗? 答案是componentMap必须在某处构建,并且规则递归地应用。

假设您要编写的组件允许您编写<InputField @type="text" /><InputField @type="checkbox" /> (例如HTML中的<input> )。

input-field模板看起来像这样:

{{#let (hash text=(component "inputs/text-field") checkbox=(component "inputs/checkbox")) as |components|}}
  {{component (get components @type)}}
{{/let}}

当您以这种方式编写代码时,Embroider可以看到它只需要在包中包含两个组件。 您是否这样写过:

{{component (concat "inputs/" <strong i="33">@type</strong> "-field")}}

(是的,类似的事情很常见)

则Embroider无法轻松分析代码并限制捆绑软件中将包含的组件。 如果您使用JavaScript进行工作(这很常见),则情况甚至更糟:

export default class extends Component {
  get innerComponent() {
    return `inputs/${this.args.type}-field`;
  }
}

使用此模板:

{{component this.innerComponent}}

这是一个很小的调整,但使Embroider可以确定实际使用的组件。

我还想更仔细地阐明与其他两个机上功能的连接。

  1. 模板导入
  2. 使用组件类作为可调用项

如果我们使用这两个功能重写前面的示例,则它看起来像:

---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
  {{component (get components @type)}}
{{/let}}

这样做的好处是消除了Embroider必须理解的特殊规则,并使用户模型用于代码拆分甚至更多关于模块。

使用组件类作为可调用项

RFC#481开始,组件类已成为一个完全独立的单元,具有Glimmer VM将其作为组件调用所需的所有信息。

注意:这不仅适用于从@ember/component@glimmer/component继承的类。 从RFC 481开始,Ember对“组件”的定义是“与模板和组件管理器关联的对象”,其中包括自定义组件,如hooked-components

由于RFC#432 (上下文帮助程序和修饰符)和RFC#496 (严格模式把手),Ember模板语法的未来设计是:可以将包含帮助程序,组件或修饰符的表达式作为帮助程序,组件或修饰符进行调用。

当前已批准的RFC的含义是<SomeComponentClass /> (或<this.componentClass> ,其中this.componentClass解析为组件类)将“起作用”。 这也是实施工作的记录计划。

也就是说,为了清楚起见,我们应该创建一个新的RFC,以明确指定此行为。

在我们的某个地方指定HBS中的有效组件列表对我们来说是可行的,但是有一些警告值得我们注意:

  1. 当前,我们使用JS解决方案@wycats指出。 在HBS提供程序中执行相同的操作是可行的,但是众所周知,HBS中的逻辑有些艰巨。 与提供正确字符串的util函数相比,对提供者组件产生正确组件的单元测试要困难得多。
  2. 这在JS(无论如何都具有公共API)afaik中是不存在的,但是在这里利用目录结构来发挥我们的优势将非常好。 例如:

    {{#let (lookup-directory "components/inputs/") as |components|}}
    {{/let}}
    

    如果您已经知道按目录结构将多态类型分组在一起,那么随着时间的推移,这种事情可能会变得更易于维护。

无论如何,我觉得动态组件需要自己讨论而不是接管这一问题。

无论如何,我觉得动态组件需要自己讨论而不是接管这一问题。

我同意,并将很快在RFC存储库中打开一个,并在此处发布链接。

@mehulkar @wycats @jherdman如何允许用户仅导入其动态可调用组件列表? 似乎比包含额外级别的间接帮助器容易得多。

import Component1 from './dynamic/component-1';
import Component2 from './dynamic/component-2';
import Component3 from './dynamic/component-3';

export default class extends Component {
  get innerComponent() {
    switch(this.args.type) {
      case 'one':
        return Component1;
      case 'two':
        return Component2;
      case 'three':
        return Component3;
      default:
        // handle invalid type
    }
  }
}

然后,模板可以执行以下操作: {{#let (component this.innerComponent) as |DynamicComponent|}}或类似的东西。

我更喜欢这样的东西,它更容易搜索/读取/分析,并易于检测这些组件的使用位置。 有什么想法吗?

@Samsinite是的,我想就是这个主意。 HBS中的辅助程序主要是语法糖,对于仅模板的组件很有用。

@Samsinite基本上是我作为近期修复方案(在导入模板之前)提出的。

这将要求我们允许组件是可调用的,这就是我在此注释中所说的。

我们去做吧!

嗯,这对于仅模板组件是有意义的,对于误解:)。 我也喜欢仅模板组件的语法:

---
import TextField from "./text-field";
import Checkbox from "./checkbox";
---
{{#let (hash text=TextField checkbox=Checkbox) as |components|}}
  {{component (get components @type)}}
{{/let}}

实际上,我可能会将其仅用于模板和js支持的组件,具体取决于团队中其他人更容易阅读的内容。

此页面是否有帮助?
0 / 5 - 0 等级