You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
exportfunctiondetectChanges<T>(component: T): void{const hostNode =_getComponentHostLElementNode(component);ngDevMode&&assertNotNull(hostNode.data,'Component host node should be attached to an LView');constcomponentIndex=hostNode.tNode!.flags>>TNodeFlags.DirectiveStartingIndexShift;constdef=hostNode.view.tView.directives![componentIndex]asComponentDef<T>;detectChangesInternal(hostNode.dataasLView,hostNode,def,component);}
exportfunctiontick<T>(component: T): void{const rootView =getRootView(component);constrootComponent=(rootView.contextasRootContext).component;consthostNode=_getComponentHostLElementNode(rootComponent);ngDevMode&&assertNotNull(hostNode.data,'Component host node should be attached to an LView');renderComponentOrTemplate(hostNode,rootView,rootComponent);}
让我们看看Angular为我们做了什么。
Angular视图引擎的演变
虽然新的Ivy渲染器的重要性还没有完全展现出来,但许多人想知道它将如何工作以及它为我们准备的变化。
在本文中,我将展示Ivy变更检测机制,展示一些让我非常兴奋的事情,并从头开始,根据指导(类似于Angular Ivy指导)构建简单的app。
首先,介绍一下我下面将研究的app:
我创建了一个在线demo,用于了解Ivy如何在幕后运行:
https://alexzuza.github.io/ivy-cd/
Demo使用了Angular 6.0.1 aot 编译器。你可以单击任何生命周期块来跳转到对应的代码。
为了运行变更检测过程,只需在Sub-Child下面的输入框中输入一些内容即可。
视图
当然,视图是Angular中主要的低级抽象。
对于我们的例子,我们会得到下面类似的结构:
视图应该描述模板,以及它包含一些反映该模板结构的数据。
我们来看看ChildComponent视图。它有以下模板:
当前视图引擎从视图定义工厂创建nodes并将它们存储在视图定义的nodes数组中。
Ivy从instructions创建LNodes,这个instructions被写入ngComponentDef.template函数,并将它们存储在data数组中:
除了nodes之外,新视图还包含data数组中的绑定(参见上图中的data[4],data[5],data[6])。给定视图的所有绑定,从bindingStartIndex开始按照它们出现在模板中的顺序进行存储。
在这种方式下,angular 会首先创建根视图,并在data数组索引0处定位宿主元素
然后遍历所有组件并为每个视图填充data数组。
变更检测
众所周知,ChangeDetectorRef只是抽象类,具有诸如detectChanges,markForCheck等抽象方法。
当我们在组件构造函数中询问这个依赖关系时,我们实际上得到了继承 ChangeDetectorRef 类的ViewRef实例。
现在,我们来看看用于在Ivy中运行变更检测的内部方法。其中一些可用作公共API(markViewDirty和detectChanges),但我不确定其他的API。
detectChanges
detectChanges 是对组件(及其可能的子组件)同步执行变更检测。
tick
用于在整个应用程序上执行变更检测。
scheduleTick
用于安排整个应用程序的变更检测。与tick不同,scheduleTick将多个调用合并为一个变更检测运行。当视图需要重新渲染时,通常通过调用markDirty间接调用它。
markViewDirty(markForCheck)
标记当前视图和所有祖先视图为脏(译者注:脏为需要变更检测)。
在早期的Angular 5中,它只向上迭代并启用了所有父视图的检查,现在请注意,markForCheck的确触发了Ivy变更检测周期! 😮😮😮
markDirty
将组件标记为脏。
标记为脏的组件将在未来的某个时间安排对此组件进行变更检测。将一个已经为脏的组件标记为脏是一个空操作。每个组件树只能安排一次未完成的变更检测。 (使用单独的renderComponent引导的两个组件将具有单独的调度器)
checkNoChanges
没变化:)
当我调试新的变更检测机制时,我注意到我忘记了安装zone.js。而且,正如你已经猜到的一样,它没有依赖性,没有cdRef.detectChanges或tick,它依然完美运行。
为什么呢?
你可能知道Angular只会对onPush组件触发变更检测(请参阅我在stackoverflow上的回答)。
这些规则同样适用于Ivy:
其中一个输入发生变化
https://github.com/angular/angular/blob/43d62029f0e2da0150ba6f09fd8989ca6391a355/packages/core/src/render3/instructions.ts#L890
由组件或其子组件触发的绑定事件
https://github.com/angular/angular/blob/43d62029f0e2da0150ba6f09fd8989ca6391a355/packages/core/src/render3/instructions.ts#L1743
手动调用markForCheck
(现在用markViewDirty函数(见下文))
在SubChildComponent中,有(input)output绑定。第二条规则将导致调用markForCheck。既然我们已经知道这个方法实际上调用变更检测,现在应该清楚它如何在没有zonejs的情况下工作。
如果在检测后表达式变化了怎么办?
不要着急,它还在
变更检测顺序
自从发布Ivy以来,Angular团队一直在努力确保新引擎以正确的顺序正确处理所有生命周期钩子。这意味着操作顺序应该是相似的。
Max NgWizard K在他的文章中写道(强烈建议阅读它):
回到刚刚demo的子组件中来:
我打算在其他内嵌视图之前写一个sub-child作为常规组件。
现在观察它的运行:
angular首先检查嵌入视图,然后检查常规组件。所以这里和以前的引擎相比没有改变。
无论如何,我的演示中有可选的“run Angular compile”按钮,我们可以测试其他情况。
alexzuza.github.io/ivy-cd/
一次性字符串初始化
想象一下,我们写了可以接收颜色作为字符串输入值的组件。现在我们想把这个输入作为永远不会改变的常量字符串来传递:
这就是所谓的一次性字符串初始化,angular文档中的陈述如下:
对我而言,这意味着 angular 不会对此绑定进行任何额外的检查。但是我们在 angular5 中实际看到的是,它在 updateDirectives 调用期间,每一次变更检测期间就会检查一次。
现在让我们看看它在新的引擎中是怎么样的:
正如我们所看到的,angular编译器将常量存储在负责创建和更新组件的代码之外,并且只在创建模式下使用此值。
Angular不再为容器创建文本节点
即使你不知道angular ViewContainer 在引擎中如何工作,你在打开devtools时可能会注意到下面的图片:
这是Ivy的输出
我无法100%确定,但似乎一旦Ivy变得稳定,我们就会有这样的结果。
因此对于下面的代码中query,angular将返回null
全新的 Incremental DOM(IDOM)
很久以前,Google发布了所谓的Incremental DOM库。
该库专注于构建DOM树并允许动态更新。它不能直接使用,而是作为模板引擎的编译目标。而且似乎Ivy与Incremental DOM库有一些共同之处。
让我们从头开始构建一个简单的app,这将帮助我们了解IDOM渲染如何工作的。Demo
我们的app将有计数器,并会把通过input元素输入的用户名打印出来。
假设页面上已经有和元素:
我们需要做的只是渲染动态html,看起来像这样:
为了渲染这些,让我们编写elementOpen,elementClose和文本“instructions”(我这样称呼它,因为Angular使用像Ivy这样的名称可以被认为是特殊类型的虚拟CPU)。
首先,我们需要编写特殊的助手来遍历节点树:
现在让我们写instructions:
换句话说,这些函数只是遍历DOM节点并在当前位置插入节点。此外,文本命令设置data属性,以便我们可以看到浏览器的文本值。
我们希望我们的元素能够保持某种状态,所以我们来介绍NodeData:
现在,让我们改动一下renderDOM函数,以便在当前位置已经相同的情况下,我们不会向DOM添加新元素:
注意我注释的 /*, key */。如果元素有key来区分元素会更好。另请参阅http://google.github.io/incremental-dom/#demos/using-keys
之后,让我们添加将负责文本节点更新的逻辑:
我们可以为元素节点做同样的事情。
然后,让我们来编写patch函数,它将需要DOM元素,update函数以及一些数据(这些数据将由update函数使用):
最后,让我们测试一下这个instructions:
结果可以在这找到。https://jsfiddle.net/yurzui/hqhq4khc
你还可以通过使用浏览器工具,来验证代码是否仅更新其内容已更改的文本节点:
所以IDOM的主要理念就是使用真正的DOM来和新树进行对比。
全文完。谢谢阅读。
https://blog.angularindepth.com/angular-ivy-change-detection-execution-are-you-prepared-ab68d4231f2c
The text was updated successfully, but these errors were encountered: