通过单步调试的方式学习 Angular 中 TView 和 LView 的概念
问题描述
本文涉及到的代码位置: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:
上述代码展示了 TView
和 LView
二者的区别。
再看 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 就像类实例。
- 点赞
- 收藏
- 关注作者
评论(0)