목차
Incremental DOM
增量 DOM 元素创建
组件创建与增量 DOM 指令
结束语
웹 프론트엔드 JS 튜토리얼 각도 학습: Ivy 컴파일러의 증분 DOM에 대한 간략한 분석

각도 학습: Ivy 컴파일러의 증분 DOM에 대한 간략한 분석

Feb 23, 2022 am 11:07 AM
angular

이 기사는 Angular 프레임워크를 배우고 Ivy 컴파일러의 증분 DOM을 안내하는 내용입니다. 도움이 되기를 바랍니다.

각도 학습: Ivy 컴파일러의 증분 DOM에 대한 간략한 분석

"대규모 프런트엔드 프로젝트용"으로 설계된 프런트엔드 프레임워크로서 Angular에는 실제로 참고하고 학습할 만한 디자인이 많이 있습니다. 이 시리즈는 주로 이러한 디자인과 기능의 구현 원리를 연구하는 데 사용됩니다. 이 글에서는 Angular의 핵심 기능인 Ivy 컴파일러에 초점을 맞추고 증분 DOM 설계를 소개합니다. [관련 추천 튜토리얼: "angular Tutorial"]

저는 프론트엔드 프레임워크를 소개할 때 템플릿 엔진을 소개하는 경우가 많습니다. 템플릿 엔진의 렌더링 프로세스를 위해 Vue/React와 같은 프레임워크는 가상 DOM과 같은 디자인을 사용합니다.

Angular Ivy 컴파일러에서는 가상 DOM을 사용하지 않고 증분 DOM을 사용합니다.

Incremental DOM

아이비 컴파일러에서는 템플릿 컴파일 제품이 View Engine과 다릅니다. 이는 개별 컴파일, 증분 컴파일 등의 기능을 지원하기 위한 것입니다.

예를 들어 템플릿 코드 <span>My name is {{name}}</span>인 경우 Ivy 컴파일러에서 컴파일된 코드는 다음과 같습니다. <span>My name is {{name}}</span>这句模板代码,在 Ivy 编译器中编译后的代码大概长这个样子:

// create mode
if (rf & RenderFlags.Create) {
  elementStart(0, "span");
  text(1);
  elementEnd();
}
// update mode
if (rf & RenderFlags.Update) {
  textBinding(1, interpolation1("My name is", ctx.name));
}
로그인 후 복사

可以看到,相比于 View Engine 中的elementDef(0,null,null,1,&#39;span&#39;,...),elementStart()elementEnd()

export function ɵɵelement(
  index: number,
  name: string,
  attrsIndex?: number | null,
  localRefsIndex?: number
): void {
  ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
  ɵɵelementEnd();
}
로그인 후 복사
로그인 후 복사

View Engine의 elementDef(0,null,null,1,'span',...),, elementStart(), < code>와 비교하여 볼 수 있습니다. elementEnd()이러한 API는 더 깔끔하게 보이며 증분 DOM 디자인을 사용합니다.

증분 DOM과 가상 DOM

가상 DOM의 핵심 계산 프로세스는 다음과 같습니다.
  • JavaScript 개체를 사용하여 DOM 트리를 시뮬레이션하고 가상 DOM 트리를 얻습니다.
  • 페이지 데이터가 변경되면 새로운 가상 DOM 트리가 생성되고 이전 가상 DOM 트리와 새 가상 DOM 트리의 차이점을 비교합니다.
  • 차이점을 실제 DOM 트리에 적용합니다.

Virtual DOM은 빈번한 페이지 업데이트 및 렌더링으로 인한 성능 문제를 해결하지만 기존 Virtual DOM에는 여전히 다음과 같은 성능 병목 현상이 있습니다.
  • 단일 구성 요소에서는 구성 요소의 전체 가상 DOM 트리를 여전히 순회해야 합니다.
  • 일부 구성 요소가 전체 템플릿에서 적은 수의 동적 노드만 포함하는 경우 이러한 순회는 성능 낭비입니다.
  • 재귀 순회 및 업데이트 논리로 인해 UI 렌더링이 쉽게 차단되고 사용자 경험이 저하될 수 있습니다

In 이러한 상황에 대한 대응으로 React 및 Vue와 같은 프레임워크에는 트리 차이, 구성 요소 차이 및 요소 차이에 대한 알고리즘 최적화와 같은 더 많은 최적화가 있으며 상태 업데이트의 계산 및 렌더링을 제어하기 위해 작업 스케줄링이 도입되었습니다. Vue 3.0에서는 가상 DOM의 업데이트가 기존 전체 범위에서 트리 범위로 조정되어 알고리즘이 단순화되고 성능이 향상됩니다.

어쨌든 가상 DOM 설계에는 피할 수 없는 문제가 있습니다. 각 렌더링 작업은 최소한 변경된 노드를 수용할 수 있을 만큼 크며 일반적으로 다음과 같이 더 큰 새로운 가상 DOM 트리를 할당합니다. 더 큰 메모리 공간에서. 대규모 가상 DOM 트리에 대규모 업데이트가 필요한 경우, 특히 메모리가 제한된 모바일 장치에서 성능이 저하될 수 있습니다.

증분 DOM의 핵심 디자인 아이디어는 다음과 같습니다.
  • 새 (가상) DOM 트리를 만들 때 기존 트리를 따라 이동하면서 변경 사항을 식별합니다.
  • 변경 사항이 없으면 메모리가 할당되지 않습니다.
  • 있는 경우 기존 트리를 변경하고(꼭 필요한 경우에만 메모리 할당) 차이점을 물리적 DOM에 적용합니다.

미리 계산된 메타 정보를 기존 DOM 노드에 혼합할 때 가상 DOM 트리에 의존하는 대신 물리적 DOM 트리를 사용하는 것이 실제로 충분히 빠르기 때문에 여기에 (virtual)을 괄호 안에 넣었습니다.

증분 DOM은 가상 DOM 기반 접근 방식에 비해 두 가지 주요 장점이 있습니다.
  • 증분 기능을 사용하면 렌더링 중에 메모리 할당이 크게 줄어들어 결과적으로 더 예측 가능한 성능을 얻을 수 있습니다.
  • 템플릿 기반 접근 방식에 쉽게 매핑할 수 있습니다. 제어문 및 루프는 요소 및 속성 선언과 자유롭게 혼합될 수 있습니다.

증분형 DOM의 설계는 Google에서 제안했으며 표현 및 적용을 위한 라이브러리인 오픈 소스 라이브러리 google/incremental-dom

도 제공합니다. DOM 트리 업데이트된 라이브러리. JavaScript를 사용하면 데이터를 추출, 반복 및 HTMLElements 및 Text 노드를 생성하는 호출로 변환할 수 있습니다.

하지만 새로운 Ivy 엔진은 이를 직접적으로 사용하지 않고 자체 버전을 구현합니다.

Ivy 엔진의 Incremental DOM은 Incremental DOM 개념을 기반으로 합니다. 가상 DOM 방식과의 차이점은 diff 작업이 DOM에서 점진적으로 수행된다는 것입니다(즉, 한 번에 하나의 노드). 가상 DOM. DOM 트리에서 실행됩니다. 이 디자인을 기반으로 증분 DOM과 Angular의 더티 검사 메커니즘이 실제로 잘 작동합니다.

增量 DOM 元素创建

增量 DOM 的 API 的一个独特功能是它分离了标签的打开(elementStart)和关闭(elementEnd),因此它适合作为模板语言的编译目标,这些语言允许(暂时)模板中的 HTML 不平衡(比如在单独的模板中,打开和关闭的标签)和任意创建 HTML 属性的逻辑。

在 Ivy 中,使用elementStartelementEnd创建一个空的 Element 实现如下(在 Ivy 中,elementStartelementEnd的具体实现便是ɵɵelementStartɵɵelementEnd):

export function ɵɵelement(
  index: number,
  name: string,
  attrsIndex?: number | null,
  localRefsIndex?: number
): void {
  ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
  ɵɵelementEnd();
}
로그인 후 복사
로그인 후 복사

其中,ɵɵelementStart用于创建 DOM 元素,该指令后面必须跟有ɵɵelementEnd()调用。

export function ɵɵelementStart(
  index: number,
  name: string,
  attrsIndex?: number | null,
  localRefsIndex?: number
): void {
  const lView = getLView();
  const tView = getTView();
  const adjustedIndex = HEADER_OFFSET + index;

  const renderer = lView[RENDERER];
  // 此处创建 DOM 元素
  const native = (lView[adjustedIndex] = createElementNode(
    renderer,
    name,
    getNamespace()
  ));
  // 获取 TNode
  // 在第一次模板传递中需要收集匹配
  const tNode = tView.firstCreatePass ?
      elementStartFirstCreatePass(
          adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
      tView.data[adjustedIndex] as TElementNode;
  setCurrentTNode(tNode, true);

  const mergedAttrs = tNode.mergedAttrs;
  // 通过推断的渲染器,将所有属性值分配给提供的元素
  if (mergedAttrs !== null) {
    setUpAttributes(renderer, native, mergedAttrs);
  }
  // 将 className 写入 RElement
  const classes = tNode.classes;
  if (classes !== null) {
    writeDirectClass(renderer, native, classes);
  }
  // 将 cssText 写入 RElement
  const styles = tNode.styles;
  if (styles !== null) {
    writeDirectStyle(renderer, native, styles);
  }

  if ((tNode.flags & TNodeFlags.isDetached) !== TNodeFlags.isDetached) {
    // 添加子元素
    appendChild(tView, lView, native, tNode);
  }

  // 组件或模板容器的任何直接子级,必须预先使用组件视图数据进行猴子修补
  // 以便稍后可以使用任何元素发现实用程序方法检查元素
  if (getElementDepthCount() === 0) {
    attachPatchData(native, lView);
  }
  increaseElementDepthCount();

  // 对指令 Host 的处理
  if (isDirectiveHost(tNode)) {
    createDirectivesInstances(tView, lView, tNode);
    executeContentQueries(tView, tNode, lView);
  }
  // 获取本地名称和索引的列表,并将解析的本地变量值按加载到模板中的相同顺序推送到 LView
  if (localRefsIndex !== null) {
    saveResolvedLocalsInData(lView, tNode);
  }
}
로그인 후 복사

可以看到,在ɵɵelementStart创建 DOM 元素的过程中,主要依赖于LViewTViewTNode

在 Angular Ivy 中,使用了LViewTView.data来管理和跟踪渲染模板所需要的内部数据。对于TNode,在 Angular 中则是用于在特定类型的所有模板之间共享的特定节点的绑定数据(享元)。

ɵɵelementEnd()则用于标记元素的结尾:

export function ɵɵelementEnd(): void {}
로그인 후 복사

对于ɵɵelementEnd()的详细实现不过多介绍,基本上主要包括一些对 Class 和样式中@input等指令的处理,循环遍历提供的tNode上的指令、并将要运行的钩子排入队列,元素层次的处理等等。

组件创建与增量 DOM 指令

在增量 DOM 中,每个组件都被编译成一系列指令。这些指令创建 DOM 树并在数据更改时就地更新它们。

Ivy 在运行时编译一个组件的过程中,会创建模板解析相关指令:

export function compileComponentFromMetadata(
  meta: R3ComponentMetadata,
  constantPool: ConstantPool,
  bindingParser: BindingParser
): R3ComponentDef {
  // 其他暂时省略

  // 创建一个 TemplateDefinitionBuilder,用于创建模板相关的处理
  const templateBuilder = new TemplateDefinitionBuilder(
      constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
      directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML,
      meta.relativeContextFilePath, meta.i18nUseExternalIds);

  // 创建模板解析相关指令,包括:
  // 第一轮:创建模式,包括所有创建模式指令(例如解析侦听器中的绑定)
  // 第二轮:绑定和刷新模式,包括所有更新模式指令(例如解析属性或文本绑定)
  const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);

  // 提供这个以便动态生成的组件在实例化时,知道哪些投影内容块要传递给组件
  const ngContentSelectors = templateBuilder.getNgContentSelectors();
  if (ngContentSelectors) {
    definitionMap.set("ngContentSelectors", ngContentSelectors);
  }

  // 生成 ComponentDef 的 consts 部分
  const { constExpressions, prepareStatements } = templateBuilder.getConsts();
  if (constExpressions.length > 0) {
    let constsExpr: o.LiteralArrayExpr|o.FunctionExpr = o.literalArr(constExpressions);
    // 将 consts 转换为函数
    if (prepareStatements.length > 0) {
      constsExpr = o.fn([], [...prepareStatements, new o.ReturnStatement(constsExpr)]);
    }
    definitionMap.set("consts", constsExpr);
  }

  // 生成 ComponentDef 的 template 部分
  definitionMap.set("template", templateFunctionExpression);
}
로그인 후 복사

可见,在组件编译时,会被编译成一系列的指令,包括constvarsdirectivespipesstyleschangeDetection等等,当然也包括template模板里的相关指令。最终生成的这些指令,会体现在编译后的组件中,比如之前文章中提到的这样一个Component文件:

import { Component, Input } from "@angular/core";

@Component({
  selector: "greet",
  template: "<div> Hello, {{name}}! </div>",
})
export class GreetComponent {
  @Input() name: string;
}
로그인 후 복사

ngtsc编译后,产物包括该组件的.js文件:

const i0 = require("@angular/core");
class GreetComponent {}
GreetComponent.ɵcmp = i0.ɵɵdefineComponent({
  type: GreetComponent,
  tag: "greet",
  factory: () => new GreetComponent(),
  template: function (rf, ctx) {
    if (rf & RenderFlags.Create) {
      i0.ɵɵelementStart(0, "div");
      i0.ɵɵtext(1);
      i0.ɵɵelementEnd();
    }
    if (rf & RenderFlags.Update) {
      i0.ɵɵadvance(1);
      i0.ɵɵtextInterpolate1("Hello ", ctx.name, "!");
    }
  },
});
로그인 후 복사

其中,elementStart()text()elementEnd()advance()textInterpolate1()这些都是增量 DOM 相关的指令。在实际创建组件的时候,其template模板函数也会被执行,相关的指令也会被执行。

正因为在 Ivy 中,是由组件来引用着相关的模板指令。如果组件不引用某个指令,则我们的 Angular 中永远不会使用到它。因为组件编译的过程发生在编译过程中,因此我们可以根据引用到指令,来排除未引用的指令,从而可以在 Tree-shaking 过程中,将未使用的指令从包中移除,这便是增量 DOM 可树摇的原因。

结束语

现在,我们已经知道在 Ivy 中,是通过编译器将模板编译为template渲染函数,其中会将对模板的解析编译成增量 DOM 相关的指令。其中,在elementStart()执行时,我们可以看到会通过createElementNode()方法来创建 DOM。实际上,增量 DOM 的设计远不止只是创建 DOM,还包括变化检测等各种能力,关于具体的渲染过程,我们会在下一讲中进行介绍。

更多编程相关知识,请访问:编程教学!!

위 내용은 각도 학습: Ivy 컴파일러의 증분 DOM에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 채팅 명령 및 사용 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Angular의 메타데이터와 데코레이터에 대해 이야기해 보겠습니다. Angular의 메타데이터와 데코레이터에 대해 이야기해 보겠습니다. Feb 28, 2022 am 11:10 AM

이 글은 Angular에 대한 학습을 ​​계속하고, Angular의 메타데이터와 데코레이터를 이해하고, 그 사용법을 간략하게 이해하는 데 도움이 되기를 바랍니다.

Ubuntu 24.04에 Angular를 설치하는 방법 Ubuntu 24.04에 Angular를 설치하는 방법 Mar 23, 2024 pm 12:20 PM

Angular.js는 동적 애플리케이션을 만들기 위해 자유롭게 액세스할 수 있는 JavaScript 플랫폼입니다. HTML 구문을 템플릿 언어로 확장하여 애플리케이션의 다양한 측면을 빠르고 명확하게 표현할 수 있습니다. Angular.js는 코드를 작성, 업데이트 및 테스트하는 데 도움이 되는 다양한 도구를 제공합니다. 또한 라우팅 및 양식 관리와 같은 많은 기능을 제공합니다. 이 가이드에서는 Ubuntu24에 Angular를 설치하는 방법에 대해 설명합니다. 먼저 Node.js를 설치해야 합니다. Node.js는 서버 측에서 JavaScript 코드를 실행할 수 있게 해주는 ChromeV8 엔진 기반의 JavaScript 실행 환경입니다. Ub에 있으려면

각도 학습 상태 관리자 NgRx에 대한 자세한 설명 각도 학습 상태 관리자 NgRx에 대한 자세한 설명 May 25, 2022 am 11:01 AM

이 글은 Angular의 상태 관리자 NgRx에 대한 심층적인 이해를 제공하고 NgRx 사용 방법을 소개하는 글이 될 것입니다.

Angular의 서버 측 렌더링(SSR)을 탐색하는 기사 Angular의 서버 측 렌더링(SSR)을 탐색하는 기사 Dec 27, 2022 pm 07:24 PM

앵귤러 유니버셜(Angular Universal)을 아시나요? 웹사이트가 더 나은 SEO 지원을 제공하는 데 도움이 될 수 있습니다!

Angular + NG-ZORRO로 백엔드 시스템을 빠르게 개발 Angular + NG-ZORRO로 백엔드 시스템을 빠르게 개발 Apr 21, 2022 am 10:45 AM

이 기사는 Angular의 실제 경험을 공유하고 ng-zorro와 결합된 angualr을 사용하여 백엔드 시스템을 빠르게 개발하는 방법을 배우게 될 것입니다. 모든 사람에게 도움이 되기를 바랍니다.

프론트엔드 개발에 PHP와 Angular를 사용하는 방법 프론트엔드 개발에 PHP와 Angular를 사용하는 방법 May 11, 2023 pm 04:04 PM

인터넷의 급속한 발전과 함께 프론트엔드 개발 기술도 지속적으로 개선되고 반복되고 있습니다. PHP와 Angular는 프런트엔드 개발에 널리 사용되는 두 가지 기술입니다. PHP는 양식 처리, 동적 페이지 생성, 액세스 권한 관리와 같은 작업을 처리할 수 있는 서버측 스크립팅 언어입니다. Angular는 단일 페이지 애플리케이션을 개발하고 구성 요소화된 웹 애플리케이션을 구축하는 데 사용할 수 있는 JavaScript 프레임워크입니다. 이 기사에서는 프론트엔드 개발에 PHP와 Angular를 사용하는 방법과 이들을 결합하는 방법을 소개합니다.

각도에서 monaco-editor를 사용하는 방법에 대한 간략한 분석 각도에서 monaco-editor를 사용하는 방법에 대한 간략한 분석 Oct 17, 2022 pm 08:04 PM

각도에서 모나코 편집기를 사용하는 방법은 무엇입니까? 다음 글은 최근 비즈니스에서 사용되는 Monaco-Editor의 활용 사례를 기록한 글입니다.

Angular의 독립 구성요소에 대한 간략한 분석 및 사용 방법 알아보기 Angular의 독립 구성요소에 대한 간략한 분석 및 사용 방법 알아보기 Jun 23, 2022 pm 03:49 PM

이 기사에서는 Angular의 독립 구성 요소, Angular에서 독립 구성 요소를 만드는 방법, 기존 모듈을 독립 구성 요소로 가져오는 방법을 안내합니다.

See all articles