如何从模板组件中分离模板(延迟加载模板)

Posted

技术标签:

【中文标题】如何从模板组件中分离模板(延迟加载模板)【英文标题】:How to separate template out of stencil components (lazy load template) 【发布时间】:2022-01-23 09:59:50 【问题描述】:

有趣的问题,所以请读到最后。我想要实现的是在另一个 js 文件中分离模板并在需要时延迟加载它。在 React 生态系统中做同样的事情,但是模板不行!分流回购https://github.com/pranav-js/triage-repo

我的 tsx 模板在另一个 .js 文件中说

template-three.js 有简单的 onClick,它只会发出警报

import  h  from '@stencil/core';
export const template_three = () => 
  return <button onClick=() => alert()>Template three here</button>;
;

当我尝试通过像这样导入 component-two.tsx 来调用此方法时

import  Component, Fragment, h, Host, State  from '@stencil/core';

@Component(
  tag: 'component-two',
  styleUrl: 'component-two.css',
  shadow: true,
)
export class ComponentTwo 
  @State() showComponentOne: any;
  method: any;
  template: string = '';

  constructor() 

  componentWillRender() 
    this.fetchComp(2);
  

// lazy load template when needed based on type passed

  fetchComp(type) 
    let this_ = this;
    switch (type) 
      case 0:
        import('./template').then(module => 
          this.showComponentOne = module.template_one;
        );
        break;
      case 1:
        import('./template-two').then(module => 
          this.showComponentOne = module.template_two;
        );
        break;
      case 2:
          import('./template-three').then(module => 
            this.showComponentOne = module.template_three;
          );
        break;
      default:
        break;
    
  

  clicked() 
    alert();
  

  methodHere() 

// check if template received then simply call that method with this attached

  render() 
    let this_ = this;
    return this.showComponentOne ? this.showComponentOne.apply(this) : <div></div>;
  


查看呈现,但事件监听器不起作用 :/,甚至没有一个简单的警报 :(。当我检查时,我没有看到任何附加到的事件按钮。 但是,如果我保留在组件类中的相同功能,它可以工作:( !!!

在组件内部和外部定义模板时检查两个不同的对象。

你能告诉我我在这里做错了什么吗?

我不能只在组件中保留模板,因为我有许多 UI 用于相同的逻辑。 到目前为止,我在互联网上没有任何方法,这个答案也无济于事 Passing custom template to stencil component

【问题讨论】:

使用functional components 作为模板可能会更幸运。 @Thomas 功能组件也有同样的问题。这很奇怪。 H 【参考方案1】:

我认为问题在于 Stencil 的可摇树 API 和动态导入的构建时分析问题。

Stencil 尝试尽可能少地提供“Stencil”代码,因此它会分析您的项目以找出您实际使用的功能,并且仅将这些功能包含在最终捆绑包中。如果您检查第 2 行的 ./build/index-hash.js(在 dev www 构建中),您会发现它检测到的功能列表。

我创建了自己的快速复制并在使用动态和静态导入时比较了这个文件。以下是区别:

动态导入

 vdomAttribute: false, vdomListener: false 

静态导入

 vdomAttribute: true, vdomListener: true 

因此,Stencil 似乎不知道您仅在动态导入的模板中使用的功能,因此不会将其包含在构建中。但是,如果您在任何组件(或静态导入到组件中的文件)中使用相同的功能,Stencil 应该包含它。

因此,一个简单的解决方法是将任何侦听器附加到项目的任何组件中的任何元素。您必须为当前仅在动态加载的模板中使用的每个 Stencil 功能执行此操作。

另一种选择是创建一个静态包含所有动态模板的 Stencil 组件。 AFAIK 这将检测所有使用的功能并为项目启用它们,即使不必在任何地方使用这个新组件。

示例:

import  Component, Host, h  from '@stencil/core';
import  Template1  from "../../templates/template1";
import  Template2  from "../../templates/template2";
import  Template3  from "../../templates/template3";

@Component(
  tag: 'template-imports',
)
export class TemplateImports 
  render() 
    return (
      <Host>
        <Template1></Template1>
        <Template2></Template2>
        <Template3></Template3>
      </Host>
    );
  

【讨论】:

很好的解释。多谢!我得出了相同的结论。我尝试了将所有模板维护在一个文件中并使用逻辑组件作为模板组件的子组件的最后一个选项。它有效,但我失去了延迟加载模板功能。我想我现在必须坚持 this.el.shadowroot.queryselector('#id')).addEventListener 。这行得通。但是模板应该考虑分析功能组件,完全相同的东西在反应中起作用:/ 问题不在于它们是功能组件,而是动态导入。如果您静态导入它们,它们将被正确分析。我并不是说将所有模板保存在一个文件中,而是要有一个额外的组件,您可以在其中静态导入每个模板。然后你仍然可以在其他组件中动态导入它们,只要你不实际使用这个附加组件,用户就不必下载更多。 能否请您添加一个关于如何静态导入的答案示例,我不太了解如何准确导入的那部分 当然,我已经添加了一个示例。 啊,明白了,但这将不再是延迟加载并增加块大小:(【参考方案2】:

我觉得应该是的

@Component(
  tag: 'component-two',
  styleUrl: 'component-two.css',
  shadow: true,
)
export class ComponentTwo 
  // omitting all code that didn't change ...

  render() 
    // probably not needed
    let this_ = this;

    /* ---------------------------------------------------- */
    /* Note the change in the following line:               */
    /* showComponentOne must be the template function here. */
    /* ---------------------------------------------------- */
    return this.showComponentOne ? <this.showComponentOne onclick=this.onClick.bind(this) /> : <div></div>;
  

【讨论】:

这和用事件监听器注册随机 div 不一样吗:(。我必须为 id、样式、占位符、事件这样做。但我认为你使用了函数组件方法,我会试试这个。对于每个事件,但我必须通过这样的道具。

以上是关于如何从模板组件中分离模板(延迟加载模板)的主要内容,如果未能解决你的问题,请参考以下文章

模板模式(部分方法延迟到子类实现)

模板方法模式

如何从 VueJS 中的 HTML 模板加载特定组件?

设计模式-模板模式

从 Backbone.View 中分离模板逻辑

如何从 NgbModal 中的延迟加载模块中打开组件?