Maison  >  Article  >  interface Web  >  [Français] Mécanisme de mise à jour des liaisons de propriétés angulaires - Partage de technologie Laravel/Angular

[Français] Mécanisme de mise à jour des liaisons de propriétés angulaires - Partage de technologie Laravel/Angular

寻∝梦
寻∝梦original
2018-09-07 16:11:111294parcourir
Cet article parle principalement du mécanisme de liaison et de mise à jour des attributs angularjs, ainsi que de l'explication détaillée des attributs de mise à jour d'angularjs, le tout dans cet article. Lisons maintenant cet article ensemble. 🎜>
Explication du mécanisme de mise à jour des liaisons d'attributs angulaires js :

Tous les frameworks frontaux modernes utilisent des composants pour synthétiser l'interface utilisateur, ce qui se traduit naturellement par une hiérarchie de composants parent-enfant, ce qui nécessite le cadre pour fournir un mécanisme de communication entre les composants parent-enfant. De même, Angular propose également deux façons d'implémenter la communication entre composants parent-enfant :

Liaison d'entrée et de sortie

et Service partagé. Pour les composants de présentation sans état, je préfère l'approche de liaison d'entrée et de sortie, tandis que pour les composants de conteneur avec état j'utilise l'approche de services partagés. Cet article présente principalement les méthodes de liaison d'entrée et de sortie, en particulier la façon dont Angular met à jour la valeur d'entrée du composant enfant lorsque la valeur de liaison d'entrée du composant parent change. Si vous souhaitez savoir comment Angular met à jour le DOM du composant actuel, vous pouvez consulter le

Mécanisme de mise à jour du DOM angulaire traduit

Cet article vous aidera également à approfondir votre compréhension de cet article. Puisque nous allons explorer comment Angular met à jour les propriétés de liaison d'entrée des éléments et composants DOM, il est supposé que vous savez comment les composants et les directives sont représentés en interne dans Angular. Si vous n'en savez pas grand-chose et que vous êtes intéressé, vous pouvez vérifier. out Traduit Pourquoi Angular en interne Aucun composant n'a été trouvé Cet article parle principalement de la façon dont Angular utilise les instructions pour représenter les composants en interne. Cet article utilise les deux concepts de composants et d'instructions de manière interchangeable, car Angular traite en interne les composants comme des instructions. Syntaxe de liaison de modèle

Vous savez peut-être qu'Angular fournit une

syntaxe de liaison de propriété

—— , cette syntaxe est très courante et peut être utilisée sur les composants enfants, peut également être utilisé sur les éléments natifs du DOM. Si vous souhaitez transmettre des données d'un composant parent à un composant enfant ou à un élément DOM natif [], vous pouvez écrire ceci dans le modèle du composant parent : b-compspan

Vous n'avez pas faire quoi que ce soit pour l'élément natif du DOM Travail supplémentaire, mais pour le composant enfant
import { Component } from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'a-comp',
  template: `
      <b-comp [textContent]="AText"></b-comp>
      <span [textContent]="AText"></span>
  `
})
export class AComponent {
  AText = 'some';
}
vous devez déclarer l'attribut d'entrée

 : b-comptextContent

afin que lorsque l'attribut du composant parent
@Component({
    selector: 'b-comp',
    template: 'Comes from parent: {{textContent}}'
})
export class BComponent {
    @Input() textContent;
}
change , Angular mettra automatiquement à jour l'attribut

du composant enfant et les attributs de l'élément natif AComponent.AText. Dans le même temps, la fonction hook de cycle de vie BComponent.textContent du sous-composant span.textContent sera également appelée (remarque : il existe en fait BComponent, voir ci-dessous). ngOnChangesngDoCheckVous vous demandez peut-être comment Angular sait que

et

prennent en charge la liaison BComponent. En effet, lorsque le compilateur Angular analyse le modèle, s'il rencontre un simple élément DOM tel que span, il découvrira si l'élément est défini dans textContentdom_element_schema_registryspan, sachant ainsi qu'il s'agit d'une sous-classe HTMLElement. , et est l'un des attributs (Remarque : vous pouvez essayer si est lié à un textContent et une erreur sera signalée, et l'attribut span ne peut pas être reconnu si vous rencontrez un composant) ; ou directive, vérifiez l'élément de son décorateur [abc]=AText L'attribut data abc a-t-il cet élément d'attribut de liaison ? Sinon, le compilateur générera également une erreur : @Component/@Directiveinput

Cette connaissance est facile à comprenons. Examinons maintenant de plus près ce qui se passe en interne.
Can’t bind to ‘textContent’ since it isn’t a known property of …

Component Factory

Bien que les attributs d'entrée soient liés aux éléments du composant enfant

et

, toutes les informations requises pour la mise à jour de la liaison d'entrée se trouvent dans le composant du composant parent BComponent En usine. Jetons un coup d'œil au code de l'usine de composants de span : AComponentAComponent

Si vous lisez
function View_AComponent_0(_l) {
  return jit_viewDef1(0, [
     jit_elementDef_2(..., 'b-comp', ...),
     jit_directiveDef_5(..., jit_BComponent6, [], {
         textContent: [0, 'textContent']
     }, ...),
     jit_elementDef_2(..., 'span', [], [[8, 'textContent', 0]], ...)
  ], function (_ck, _v) {
     var _co = _v.component;
     var currVal_0 = _co.AText;
     var currVal_1 = 'd';
     _ck(_v, 1, 0, currVal_0, currVal_1);
  }, function (_ck, _v) {
     var _co = _v.component;
     var currVal_2 = _co.AText;
     _ck(_v, 2, 0, currVal_2);
  });
}
Mécanisme de mise à jour du DOM angulaire traduit

ou Traduit pourquoi les composants ne sont pas trouvés en interne dans Angular , vous serez plus familier avec chaque nœud de vue dans le code ci-dessus. Parmi les deux premiers nœuds, est un nœud élément et est un nœud instruction. Ces deux constituent le sous-composant jit_elementDef_2 ; le troisième nœud jit_directiveDef_5 est également un nœud élément et constitue le BComponent. élément. (Si vous souhaitez en savoir plus, rendez-vous sur le site Web PHP chinois jit_elementDef_2Manuel de développement AngularJSspan pour en savoir) Liaison de nœuds

Les nœuds du même type utilisent la même définition de nœud fonction, mais les différences Les paramètres reçus sont différents. Par exemple, les paramètres de la fonction de définition de nœud

sont les suivants :

jit_directiveDef_5

Parmi eux, le paramètre
jit_directiveDef_5(..., jit_BComponent6, [], {
    textContent: [0, 'textContent']
}, ...),
est appelé

props<.>. Vous pouvez voir ce point dans {textContent: [0, 'textContent']}directiveDef Liste des paramètres de fonction :

props
directiveDef(..., props?: {[name: string]: [number, string]}, ...)
Le paramètre est un objet, chaque clé est le nom de l'attribut de liaison, et la valeur correspondante est composée de l'index de liaison et du nom de l'attribut de liaison, par exemple, dans cet exemple il n'y a qu'une seule liaison, la valeur correspondante de

textContent est : Si l'instruction a plusieurs liaisons, par exemple :

<b-comp [textContent]="AText" [otherProp]="AProp">

props 参数值也包含两个属性:

jit_directiveDef5(49152, null, 0, jit_BComponent6, [], {
    textContent: [0, 'textContent'],
    otherProp: [1, 'otherProp']
}, null),

Angular 会使用这些值来生成当前指令节点的 binding,从而生成当前视图的指令节点。在变更检测时,每一个 binding 决定 Angular 使用哪种操作来更新节点和提供上下文信息,绑定类型是通过 BindingFlags 设置的(注:每一个绑定定义是 BindingDef,它的属性 flags: BindingFlags 决定 Angular 该采取什么操作,比如 Class 型绑定和 Style 型绑定都会调用对应的操作函数,见下文)。比如,如果是属性绑定,编译器会设置绑定标志位为:

export const enum BindingFlags {
    TypeProperty = 1 << 3,
注:上文说完了指令定义函数的参数,下面说说元素定义函数的参数。

本例中,因为 span 元素有属性绑定,编译器会设置绑定参数为 [[8, 'textContent', 0]]

jit_elementDef2(..., 'span', [], [[8, 'textContent', 0]], ...)

不同于指令节点,对元素节点来说,绑定参数结构是个二维数组,因为 span 元素只有一个绑定,所以它仅仅只有一个子数组。数组 [8, 'textContent', 0] 中第一个参数也同样是绑定标志位 BindingFlags,决定 Angular 应该采取什么类型操作(注:[8, 'textContent', 0] 中的 8 表示为 property 型绑定):

export const enum BindingFlags {
    TypeProperty = 1 << 3, // 8

其他类型标志位已经在文章 译 Angular DOM 更新机制 有所解释:

TypeElementAttribute = 1 << 0,
TypeElementClass = 1 << 1,
TypeElementStyle = 1 << 2,

编译器不会为指令定义提供绑定标志位,因为指令的绑定类型也只能是 BindingFlags.TypeProperty

注:节点绑定 这一节主要讲的是对于元素节点来说,每一个节点的 binding 类型是由 BindingFlags 决定的;对于指令节点来说,每一个节点的 binding 类型只能是 BindingFlags.TypeProperty

updateRenderer 和 updateDirectives

组件工厂代码里,编译器还为我们生成了两个函数:

function (_ck, _v) {
    var _co = _v.component;
    var currVal_0 = _co.AText;
    var currVal_1 = _co.AProp;
    _ck(_v, 1, 0, currVal_0, currVal_1);
},
function (_ck, _v) {
    var _co = _v.component;
    var currVal_2 = _co.AText;
    _ck(_v, 2, 0, currVal_2);
}

如果你读了 译 Angular DOM 更新机制,应该对第二个函数即 updateRenderer 有所熟悉。第一个函数叫做 updateDirectives。这两个函数都是 ViewUpdateFn 类型接口,两者都是视图定义的属性:

interface ViewDefinition {
  flags: ViewFlags;
  updateDirectives: ViewUpdateFn;
  updateRenderer: ViewUpdateFn;

有趣的是这两个函数的函数体基本相同,参数都是 _ck_v,并且两个函数的对应参数都指向同一个对象,所以为何需要两个函数?

因为在变更检测期间,这是不同阶段的两个不同行为:

  • 更新子组件的输入绑定属性

  • 更新当前组件的 DOM 元素

这两个操作是在变更检测的不同阶段执行,所以 Angular 需要两个独立的函数分别在对应的阶段调用:

  • updateDirectives——变更检测的开始阶段被调用,来更新子组件的输入绑定属性

  • updateRenderer——变更检测的中间阶段被调用,来更新当前组件的 DOM 元素

这两个函数都会在 Angular 每次的变更检测时 被调用,并且函数参数也是在这时被传入的。让我们看看函数内部做了哪些工作。

_ck 就是 check 的缩写,其实就是函数 prodCheckAndUpdateNode,另一个参数就是 组件视图数据。函数的主要功能就是从组件对象里拿到绑定属性的当前值,然后和视图数据对象、视图节点索引等一起传入 prodCheckAndUpdateNode 函数。其中,因为 Angular 会更新每一个视图的 DOM,所以需要传入当前视图的索引。如果我们有两个 span 和两个组件:

<b-comp [textContent]="AText"></b-comp>
<b-comp [textContent]="AText"></b-comp>
<span [textContent]="AText"></span>
<span [textContent]="AText"></span>

编译器生成的 updateRenderer 函数和 updateDirectives 函数如下:

function(_ck, _v) {
    var _co = _v.component;
    var currVal_0 = _co.AText;
    
    // update first component
    _ck(_v, 1, 0, currVal_0);
    var currVal_1 = _co.AText;
    
    // update second component
    _ck(_v, 3, 0, currVal_1);
}, 
function(_ck, _v) {
    var _co = _v.component;
    var currVal_2 = _co.AText;
    
    // update first span
    _ck(_v, 4, 0, currVal_2);
    var currVal_3 = _co.AText;

    // update second span
    _ck(_v, 5, 0, currVal_3);
}

没有什么更复杂的东西,这两个函数还不是重点,重点是 _ck 函数,接着往下看。

更新元素的属性

从上文我们知道,编译器生成的 updateRenderer 函数会在每一次变更检测被调用,用来更新 DOM 元素的属性,并且其参数 _ck 就是函数 prodCheckAndUpdateNode。对于 DOM 元素的更新,该函数经过一系列的函数调用后,最终会调用函数 checkAndUpdateElementValue,这个函数会检查绑定标志位是 [attr.name, class.name, style.some] 其中的哪一个,又或者是属性绑定:

case BindingFlags.TypeElementAttribute -> setElementAttribute
case BindingFlags.TypeElementClass     -> setElementClass
case BindingFlags.TypeElementStyle     -> setElementStyle
case BindingFlags.TypeProperty         -> setElementProperty;

上面代码就是刚刚说的几个绑定类型,当绑定标志位是 BindingFlags.TypeProperty,会调用函数 setElementProperty,该函数内部也是通过调用 DOM Renderer 的 setProperty 方法来更新 DOM。

注:setElementProperty 函数里这行代码 view.renderer.setProperty(renderNode,name, renderValue);,renderer 就是 Renderer2 interface,它仅仅是一个接口,在浏览器平台下,它的实现就是 DefaultDomRenderer2

更新指令的属性

上文中已经描述了 updateRenderer 函数是用来更新元素的属性,而 updateDirective 是用来更新子组件的输入绑定属性,并且变更检测期间传入的参数 _ck 就是函数 prodCheckAndUpdateNode。只是进过一系列函数调用后,最终调用的函数却是checkAndUpdateDirectiveInline,这是因为这次节点的标志位是 NodeFlags.TypeDirective

checkAndUpdateDirectiveInline 函数主要功能如下:

  1. 从当前视图节点里获取组件/指令对象

  2. 检查组件/指令对象的绑定属性值是否发生改变

  3. 如果属性发生改变:

    a. 如果变更策略设置为 OnPush,设置视图状态为 checksEnabled

    b. 更新子组件的绑定属性值

    c. 准备 SimpleChange 数据和更新视图的 oldValues 属性,新值替换旧值

    d. 调用生命周期钩子 ngOnChanges

  4. 如果该视图是首次执行变更检测,则调用生命周期钩子 ngOnInit

  5. 调用生命周期钩子 ngDoCheck

当然,只有在生命周期钩子在组件内定义了才被调用,Angular 使用 NodeDef 节点标志位来判断是否有生命周期钩子,如果查看源码你会发现类似如下代码:

if (... && (def.flags & NodeFlags.OnInit)) {
  directive.ngOnInit();
}
if (def.flags & NodeFlags.DoCheck) {
  directive.ngDoCheck();
}

和更新元素节点一样,更新指令时也同样把上一次的值存储在视图数据的属性 oldValues 里(注:即上面的 3.c 步骤)。

好了,本篇文章到这就结束了(想看更多就到PHP中文网AngularJS使用手册中学习),有问题的可以在下方留言提问。


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn