Angular框架详解之视图抽象定义

前言

作为“为大型前端项目”而设计的前端框架,Angular 其实有许多值得参考和学习的设计,本系列主要用于研究这些设计和功能的实现原理。本文主要围绕 Angular 中与视图有关的一些定义进行介绍。

Angular 中的视图抽象

Angular 版本可在不同的平台上运行:在浏览器中、在移动平台上或在 Web Worker 中。因此,需要特定级别的抽象来介于平台特定的 API 和框架接口之间。

Angular 中通过抽象封装了不同平台的差异,并以下列引用类型的形式出现:ElementRef,TemplateRef,ViewRef,ComponentRef和ViewContainerRef。

各抽象类视图定义

在阅读源码的时候,如果不清楚这些定义之间的区别,很容易搞混淆。所以,这里我们先来理解下它们之间的区别。

元素 ElementRef

ElementRef是最基本的抽象。如果观察它的类结构,可以看到它仅包含与其关联的本地元素:

export class ElementRef<T = any> {
 // 基础原生元素
 // 如果不支持直接访问原生元素(例如当应用程序在 Web Worker 中运行时),则为 null
 public nativeElement: T;
 constructor(nativeElement: T) {
 this.nativeElement = nativeElement;
 }
 ...
}

该 API 可用于直接访问本地 DOM 元素,可以比作document.getElementById('myId')。但 Angular 并不鼓励直接使用,尽可能使用 Angular 提供的模板和数据绑定。

模板 TemplateRef

在 Angular 中,模板用来定义要如何在 HTML 中渲染组件视图的代码。

模板通过@Component()装饰器与组件类类关联起来。模板代码可以作为template属性的值用内联的方式提供,也可以通过 templateUrl属性链接到一个独立的 HTML 文件。

用TemplateRef对象表示的其它模板用来定义一些备用视图或内嵌视图,它们可以来自多个不同的组件。TemplateRef是一组 DOM 元素(ElementRef),可在整个应用程序的视图中重复使用:

export abstract class TemplateRef<C> {
 // 此嵌入视图的父视图中的 anchor 元素
 abstract get elementRef(): ElementRef;
 // 基于此模板实例化嵌入式视图,并将其附加到视图容器
 abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
 ...
}

就其本身而言,TemplateRef类是一个简单的类,仅包括:

elementRef属性:拥有对其宿主元素的引用
createEmbeddedView方法:它允许我们创建视图并将其引用作为ViewRef返回。
模板会把纯 HTML 和 Angular 的数据绑定语法、指令和模板表达式组合起来。Angular 的元素会插入或计算那些值,以便在页面显示出来之前修改 HTML 元素。

Angular 中的视图

在 Angular 中,视图是可显示元素的最小分组单位,它们会被同时创建和销毁。Angular 哲学鼓励开发人员将 UI 视为视图的组合(而不是独立的 html 标签树)。

组件(component) 类及其关联的模板(template)定义了一个视图。具体实现上,视图由一个与该组件相关的ViewRef实例表示。

ViewRef

ViewRef表示一个 Angular 视图:

export declare abstract class ViewRef extends ChangeDetectorRef {
  // 销毁该视图以及与之关联的所有数据结构
  abstract get destroyed(): boolean;
  // 报告此视图是否已被销毁
  abstract destroy(): void;
  // 生命周期挂钩,为视图提供其他开发人员定义的清理功能
  abstract onDestroy(callback: Function): any;
}

其中,ChangeDetectorRef提供更改检测功能的基类,用于更改检测树收集所有要检查更改的视图:

export declare abstract class ChangeDetectorRef {
  // 当输入已更改或视图中触发了事件时,通常会将组件标记为脏(需要重新渲染)
  // 调用此方法以确保即使没有发生这些触发器,也要检查组件
  abstract checkNoChanges(): void;
  // 从变更检测树中分离该视图。在重新连接分离视图之前,不会对其进行检查。
  // 与 detectChanges() 结合使用可实现本地变更检测检查
  abstract detach(): void;
  // 检查此视图及其子级,与 detach() 结合使用可实现本地更改检测检查
  abstract detectChanges(): void;
  // 检查变更检测器及其子级,如果检测到任何变更,则抛出该异常
  abstract markForCheck(): void;
  // 将先前分离的视图重新附加到变更检测树
  // 默认情况下,视图将附加到树上
  abstract reattach(): void;
}

两种类型的视图

Angular 支持两种类型的视图:

(1) 链接到模板(template)的嵌入式视图(embeddedView)。

嵌入式视图表示视图容器中的 Angular 视图。模板只是保存视图的蓝图,可以使用上述的createEmbeddedView方法从模板实例化视图。

(2) 链接到组件(component)的宿主视图(hostView)。

直属于某个组件的视图叫做宿主视图。

宿主视图是在动态实例化组件时创建的,可以使用ComponentFactoryResolver动态创建实例化一个组件。在 Angular 中,每个组件都绑定到特定的注入器实例,因此在创建组件时我们将传递当前的注入器实例。

视图中各个元素的属性可以动态修改以响应用户的操作,而这些元素的结构(数量或顺序)则不能。你可以通过在它们的视图容器(ViewContainer)中插入、移动或移除内嵌视图来修改这些元素的结构。

ViewContainerRef

ViewContainerRef是可以将一个或多个视图附着到组件中的容器:

export declare abstract class ViewContainerRef {
  // 锚元素,用于指定此容器在包含视图中的位置
  // 每个视图容器只能有一个锚元素,每个锚元素只能有一个视图容器
  abstract get element(): ElementRef;
  // 此视图容器的 DI
  abstract get injector(): Injector;
  // 此容器当前附加了多少视图
  abstract get length(): number;
  // 销毁此容器中的所有视图
  abstract clear(): void;
  // 实例化单个组件,并将其宿主视图插入此容器
  abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;
  // 实例化一个嵌入式视图并将其插入
  abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>;
  // 从此容器分离视图而不销毁它
  abstract detach(index?: number): ViewRef | null;
  // 从此容器检索视图
  abstract get(index: number): ViewRef | null;
  // 返回当前容器内视图的索引
  abstract indexOf(viewRef: ViewRef): number;
  // 将视图移动到此容器中的新位置
  abstract insert(viewRef: ViewRef, index?: number): ViewRef;
  abstract move(viewRef: ViewRef, currentIndex: number): ViewRef;
  // 销毁附加到此容器的视图
  abstract remove(index?: number): void;
}

任何 DOM 元素都可以用作视图容器,Angular 不会在元素内插入视图,而是将它们附加到绑定到ViewContainer的元素之后。

通常,标记ng-container元素是标记应创建ViewContainer的位置的最佳选择。它作为注释呈现,因此不会在 DOM 中引入多余的 HTML 元素。

通过ViewContainerRef,可以用createComponent()方法实例化组件时创建宿主视图,也可以用 createEmbeddedView()方法实例化TemplateRef时创建内嵌视图。

视图容器的实例还可以包含其它视图容器,以创建层次化视图(视图树)。

视图树(View hierarchy)

在 Angular 中,通常会把视图组织成一些视图树(view hierarchies)。视图树是一棵相关视图的树,它们可以作为一个整体行动,是 Angular 变更检测的关键部件之一。

视图树的根视图就是组件的宿主视图。宿主视图可以是内嵌视图树的根,它被收集到了宿主组件上的一个视图容器(ViewContainerRef)中。当用户在应用中导航时(比如使用路由器),视图树可以动态加载或卸载。

视图树和组件树并不是一一对应的:

  • 嵌入到指定视图树上下文中的视图,也可能是其它组件的宿主视图
  • 组件可能和宿主组件位于同一个NgModule中,也可能属于其它NgModule

组件、模板、视图与模块

在 Angular 中,可以通过组件的配套模板来定义其视图。模板就是一种 HTML,它会告诉 Angular 如何渲染该组件。

视图通常会分层次进行组织,让你能以 UI 分区或页面为单位进行修改、显示或隐藏。与组件直接关联的模板会定义该组件的宿主视图。该组件还可以定义一个带层次结构的视图,它包含一些内嵌的视图作为其它组件的宿主。

带层次结构的视图可以包含同一模块(NgModule)中组件的视图,也可以(而且经常会)包含其它模块中定义的组件的视图。

总结

本文简单介绍了 Angular 中元素、视图、模板、组件中与视图相关的一些定义,包括ElementRef,TemplateRef,ViewRef,ComponentRef和ViewContainerRef。

其中,视图是 Angular 中应用程序 UI 的基本构建块,它是一起创建和销毁的最小元素组。

ViewContainerRef主要用于创建和管理内嵌视图或组件视图。组件可以通过配置模板来定义视图,与组件直接关联的模板会定义该组件的宿主视图,同时组件还可以包括内嵌视图。

到此这篇关于Angular框架详解之视图抽象定义的文章就介绍到这了,更多相关Angular视图抽象定义内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

参考

(0)

相关推荐

  • Angular框架详解之视图抽象定义

    前言 作为"为大型前端项目"而设计的前端框架,Angular 其实有许多值得参考和学习的设计,本系列主要用于研究这些设计和功能的实现原理.本文主要围绕 Angular 中与视图有关的一些定义进行介绍. Angular 中的视图抽象 Angular 版本可在不同的平台上运行:在浏览器中.在移动平台上或在 Web Worker 中.因此,需要特定级别的抽象来介于平台特定的 API 和框架接口之间. Angular 中通过抽象封装了不同平台的差异,并以下列引用类型的形式出现:ElementR

  • Angular ng-class详解及实例代码

    在前面Angularjs开发一些经验总结中我们说到在angular开发中angular controller never 包含DOM元素(html/css),在controller需要一个简单的POJO(plain object javascript object),与view完全的隔离(交互angularjs框架的职责.但在某些项目中看见controller涉及DOM的元素最多的是在controller scope上定义某变量,其值为class name,形如: function ctr($s

  • python flask框架详解

    Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务.本文参考自Flask官方文档, 英文不好的同学也可以参考中文文档 1.安装flask pip install flask 2.简单上手 一个最小的 Flask 应用如下: from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World' if __na

  • 低门槛开发iOS、Android、小程序应用的前端框架详解

    现如今跨平台开发技术已不是什么新鲜话题了,在市面上也有一些开源的框架可供选择,然而技术成熟.产品服务健全的平台并不多,其中也不乏推陈出新的框架值得关注. 比如最近使用的AVM,由APICloud迭代推出的多端开发框架,基于JavaScript,兼容多语法,如果是Vue.React的用户,可直接上手,没什么学习成本,具备虚拟DOM,可一次编写多端渲染:主要是APICloud上线已有7年,相对已经成熟,所以我把自己的一些认知和实践结合AVM官方文档的内容做了一下整理,希望能对需要使用跨平台开发技术的

  • jQuery Validate验证框架详解(推荐)

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求. 一.导入js库 <script type="text/javascript" src="<%=path %>/validate/jquery-1.6.2.min.js"></script> <script type="text/javascript" src=&qu

  • SpringMVC 整合SSM框架详解

    整合SSM 环境要求 环境: IDEA MySQL5.7.19 Tomcat9 Maven3.6 要求: 需要熟练掌握MySQL数据库,Spring,JavaWeb及MyBatis知识,简单的前端知识: 数据库环境 创建一个存放书籍数据的数据库表 CREATE DATABASE `ssmbuild`; USE `ssmbuild`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ( `bookID` INT(10) NOT NULL AUT

  • Python卷积神经网络图片分类框架详解分析

    [人工智能项目]卷积神经网络图片分类框架 本次硬核分享当时做图片分类的工作,主要是整理了一个图片分类的框架,如果想换模型,引入新模型,在config中修改即可.那么走起来瓷!!! 整体结构 config 在config文件夹下的config.py中主要定义数据集的位置,训练轮数,batch_size以及本次选用的模型. # 定义训练集和测试集的路径 train_data_path = "./data/train/" train_anno_path = "./data/trai

  • 浅试仿 mapstruct实现微服务编排框架详解

    目录 微服务编排框架 开发背景 接口的方式 通过注解的方式 书写代码方式的选择 方案选择 feign MapStruct 方案总结 Feign @FeignClient MapStruct 微服务编排框架 起始原因 是 我们公司 分布式事务 使用的是 seate 分布式事务框架,现在只在一些小部分使用,因为考虑到seate 对性能 TCP的影响,对事务这块没有更多的选择.我就在想 是不是做一个 微服务 编排框架 来解决这个问题.这里就 开发背景 因为我们是saas 可能A企业要这个功能,B企业不

  • 基于NIO的Netty网络框架(详解)

    Netty是一个高性能.异步事件驱动的NIO框架,它提供了对TCP.UDP和文件传输的支持,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果. Netty的优点有: a.功能丰富,内置了多种数据编解码功能.支持多种网络协议. b.高性能,通过与其它主流NIO网络框架对比,它的综合性能最佳. c.可扩展性好,可通过它提供的ChannelHandler组件对网络通信方面进行灵活扩展. d.易用性,API使用简单.

  • 对Python 语音识别框架详解

    如下所示: from win32com.client import constants import os import win32com.client import pythoncom speaker = win32com.client.Dispatch("SAPI.SPVOICE") class SpeechRecognition: def __init__(self, wordsToAdd): self.speaker = win32com.client.Dispatch(&qu

随机推荐