通过单步调试的方式学习 Angular 中 TView 和 LView 的概念

举报
Jerry Wang 发表于 2022/10/31 22:53:43 2022/10/31
【摘要】 问题描述本文涉及到的代码位置:https://github.com/wangzixi-diablo/ngDynamic看这样一组 parent Component 和 child Component:@Component({ selector: 'child', template: `<span>I am a child.</span>` }) export class C...

问题描述

本文涉及到的代码位置:https://github.com/wangzixi-diablo/ngDynamic

看这样一组 parent Component 和 child Component:

@Component({
    selector: 'child',
    template: `<span>I am a child.</span>`
  })
  export class ChildViewComponent {}

  @Component({
    selector: 'parent',
    template: `
      <div>
        projected content: 
        <ng-content></ng-content>
      </div>
    `
  })
  export class ParentComponent {}

  @Component({
    selector: 'demo',
    template: `
      <parent id="p1">
      <child id="c1"></child>
      </parent>
      <child id="c2"></child>
    `
  })
  export class Demo {}

其中 parent Component 定义了一个内容投影区域,通过下列高亮代码,将子 Component 的内容投射进来:

即该区域:

最后的运行时效果:

问题分析

当上述应用被 load 但尚未完成 bootstrap 时,Ivy 将其解析为如下的 TView

const tChildComponentView = new TView(
  tData: [
    new TElementNode(‘span’),
    new TTextNode(I am a child.),
  ],,
);
const tParentComponentView = new TView(
  tData: [
    new TElementNode(‘div’),
    new TTextNode(‘projected content:),
    new TProjectionNode(),
  ],,
);
const tDemoAppView = new TView(
  tData: [
    new TElementNode(‘parent’, [‘id’, ‘p1’]),
    new TElementNode(‘child’, [‘id’, ‘c1’]),
    new TElementNode(‘child’, [‘id’, ‘c2’]),
  ],,
)

接下来的步骤就是 bootstrap,具体的逻辑就是基于 TView 创建 LView

const lParentComponentView_p1 = new LView(
  tParentComponentView,
  new ParentComponent(),
  document.createElement(‘div’),
  document.createText(‘projected content:),
);
const lChildComponentView_c1 = new LView(
  tChildComponentView,
  new ChildComponent(),
  document.createElement(‘span’),
  document.createText(I am a child.),
);
const lChildComponentView_c2 = new LView(
  tChildComponentView,
  new ChildComponent(),
  document.createElement(‘span’),
  document.createText(I am a child.),
);
const lDemoAppView = new LView(
  tDemoAppView,
  new DemoApp(),
  document.createElement(‘parent’),
  lParentComponentView_p1,
  document.createElement(‘child’),
  lChildComponentView_c1,
  document.createElement(‘child’),
  lChildComponentView_c2,
)

上述逻辑在调试器里如下图所示:

其中 JerryAppComponent 被维护为 bootstrap Component:

上述代码展示了 TViewLView 二者的区别。

再看 ChildComponent,TView 的实例只有一个,而 LView 的实例却有两个,因为 ChildComponent 被使用了两次。 另一个关键区别是 LView 只存储特定于该组件实例的数据——例如组件实例和关联的 DOM 节点。 TView 存储在组件的所有实例之间共享的信息——例如需要创建哪些 DOM 节点

在上面的示例中,LView 显示为一个类(new LView(…)。实际上,我们将它存储为一个数组([…])。将 LView 存储为一个数组是出于内存性能的原因。 每个模板都有不同数量的 DOM 节点和子组件/指令,将其存储在数组中是最有效的方式。

使用数组进行存储的含义是不清楚在数组中的哪个位置存储实例数据。 TData 用于描述 LView 中每个位置存储的内容。 所以 LView 本身不足以推理,因为它在没有上下文的情况下存储值。 TView 描述了组件需要什么,但它不存储实例信息。 通过将 LView 和 TView 放在一起,Ivy 可以访问和推理 LView 中的值。 LView 存储值,而 TView 存储 LView 中值的含义,类似元数据或者 schema 的概念。

为简单起见,LView 仅存储 DOM 节点。 在实践中,LView 还存储绑定、注入器、净化器以及与视图状态相关的任何其他内容(在 TView/TData 中具有相应的条目。)

总结

考虑 View 和 View 的一种方法是将这些概念与面向对象的编程联系起来。 TView 就像类,而 View 就像类实例。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。