200行Angular编译器

你好 我的名字叫罗马,我不是自行车的发明者。 我喜欢Angular框架及其周围的生态系统,并以此开发我的Web应用程序。 从我的角度来看,从长远来看,Angular的主要优势是基于HTML和TypeScript之间的代码分离,这是由其开发人员之一详细描述的。why-angular-renders-components-with.html此优势有一个缺点:原则上需要编译以及在运行时动态编译组件的复杂性。 我想使用熟悉的Angular模板语法为用户的应用程序提供自定义字母模板,生成用于打印的报告和电子表格或为xml文件设置导出格式的能力! 要了解如何做到这一点,欢迎猫!

挑战赛


通常,用户使用Angular模板可能看起来像这样:我们有一定的数据集:

const data = { project: 'MySuperProject', userName: 'Roman', role: 'admin', projectLink: 'https://example.com/my-super-projectproject' } 

有必要提供自定义字母文本的机会,该字母文本将在编辑项目后发送给用户。 使用Angular模板,它可能看起来像这样:

  <body>  !  {{project}}    <a href="{{projectLink}}">3D   </a> <div *ngIf="role == 'admin'">       <a href="{{projectLink}}?mode=edit"></a> </div> </body> 

Ng模板库


使用客户端(甚至服务器端)上的Angular编译器可以解决此问题,但是这非常耗时,并且需要将大量兆字节的代码拖到客户端。 为什么Angular编译器这么大? 这是因为它支持大量用于组成组件和模块的功能,并且还包含自己的HTML解析器! 因此,我决定编写一个最小的Angular模板转换器,该转换器将使用浏览器中内置的HTML解析器。 我们在几个小时内用几行代码就只用了200条就做到了。 我决定在GitHub上与公众分享结果

使用ng-template库非常简单:

从npm安装依赖项

 npm install --save @quanterion/ng-template 

或通过纱线

 yarn add @quanterion/ng-template 

并按如下所示使用它:

 import { compileTemplate, htmlToElement } from '@quanterion/ng-template'; async test() { let data = { name: 'Roman' }; let element = htmlToElement(`<div>{{name}}</div>`); await compileTemplate(element, data); alert(element.outerHTML); } 

支持的语法


  1. 具有访问变量和调用函数功能的表达式{{expression}}
  2. Ng模板
  3. Ng容器
  4. 条件* ngIf + * ngIf为
  5. 周期* ngFor
  6. 样式[style.xxx] =“值”和[style.xxx.px] =“值”
  7. 条件类[class.xxx] =“值”
  8. 具有自动订阅值(作为异步管道)的Observables {{name $}}

有关更多详细信息,请参见ng-template.spec.ts测试。

使用评估


要评估模板中的表达式,可以将eval与首选项和妓女一起使用。 事实是,在Angular模板中,使用变量访问时没有通常的JavaScript前缀。 因此,您需要调用eval(),它具有作用域中数据对象的所有变量。 我没有成功为eval()生成这样的代码,因为 查看代码

 const data = { a: 1, b: () => 4 }; const expression = 'a+b()'; eval('a =1; b = ??;' + expression); 

不允许传递函数

通过创建一个函数来找到解决方案,该函数的参数具有带有数据的对象的字段名称:

 const data = { a: 1, b: () => 4 }; let entries = [] for (let property in data ) { entries.push([property, data[property]]) } const params = entries.map(e => e[0]); const fun = new Function('code', ...params, `return eval(code)`); const args = entries.map(e => e[1]); const expression = 'a+b()'; const result = fun.call(undefined, expression , ...args); 

PS:我希望将来,当新的Ivy编译器的API稳定下来时,将有可能为Ivy生成一组运算符并创建动态的成熟组件!

链接到源

Source: https://habr.com/ru/post/zh-CN448022/


All Articles