Angular의 다중 레벨 종속성 주입 설계에 대한 간략한 분석
이 기사는 Angular 소스 코드 학습을 안내하고 다단계 종속성 주입 설계를 소개합니다.
"대규모 프런트엔드 프로젝트용"으로 설계된 프런트엔드 프레임워크로서 Angular에는 실제로 참고하고 학습할 만한 디자인이 많이 있습니다. 이 시리즈는 주로 이러한 디자인과 기능의 구현 원리를 연구하는 데 사용됩니다. 이 글에서는 Angular의 가장 큰 특징인 의존성 주입에 초점을 맞추고 Angular의 다단계 의존성 주입 설계를 소개합니다. [추천 관련 튜토리얼: "angular Tutorial"]
이전 글에서는 Angular의 Injectot
인젝터, Provider
프로바이더, 인젝터 메커니즘을 소개했습니다. 그렇다면 Angular 애플리케이션에서 구성 요소와 모듈은 어떻게 종속성을 공유합니까? 동일한 서비스를 여러 번 인스턴스화할 수 있습니까? Injectot
注入器、Provider
提供者,以及注入器机制。那么,在 Angular 应用中,各个组件和模块间又是怎样共享依赖的,同样的服务是否可以多次实例化呢?
组件和模块的依赖注入过程,离不开 Angular 多级依赖注入的设计,我们来看看。
多级依赖注入
前面我们说过,Angular 中的注入器是可继承、且分层的。
在 Angular 中,有两个注入器层次结构:
-
ModuleInjector
模块注入器:使用@NgModule()
或@Injectable()
注解在此层次结构中配置ModuleInjector
-
ElementInjector
元素注入器:在每个 DOM 元素上隐式创建
模块注入器和元素注入器都是树状结构的,但它们的分层结构并不完全一致。
模块注入器
模块注入器的分层结构,除了与应用中模块设计有关系,还有平台模块(PlatformModule)注入器与应用程序模块(AppModule)注入器的分层结构。
平台模块(PlatformModule)注入器
在 Angular 术语中,平台是供 Angular 应用程序在其中运行的上下文。Angular 应用程序最常见的平台是 Web 浏览器,但它也可以是移动设备的操作系统或 Web 服务器。
Angular 应用在启动时,会创建一个平台层:
- 平台是 Angular 在网页上的入口点,每个页面只有一个平台
- 页面上运行的每个 Angular 应用程序,所共有的服务都在平台内绑定
一个 Angular 平台,主要包括创建模块实例、销毁等功能:
@Injectable() export class PlatformRef { // 传入注入器,作为平台注入器 constructor(private _injector: Injector) {} // 为给定的平台创建一个 @NgModule 的实例,以进行离线编译 bootstrapModuleFactory<M>(moduleFactory: NgModuleFactory<M>, options?: BootstrapOptions): Promise<NgModuleRef<M>> {} // 使用给定的运行时编译器,为给定的平台创建一个 @NgModule 的实例 bootstrapModule<M>( moduleType: Type<M>, compilerOptions: (CompilerOptions&BootstrapOptions)| Array<CompilerOptions&BootstrapOptions> = []): Promise<NgModuleRef<M>> {} // 注册销毁平台时要调用的侦听器 onDestroy(callback: () => void): void {} // 获取平台注入器 // 该平台注入器是页面上每个 Angular 应用程序的父注入器,并提供单例提供程序 get injector(): Injector {} // 销毁页面上的当前 Angular 平台和所有 Angular 应用程序,包括销毁在平台上注册的所有模块和侦听器 destroy() {} }
实际上,平台在启动的时候(bootstrapModuleFactory
方法中),在ngZone.run
中创建ngZoneInjector
,以便在 Angular 区域中创建所有实例化的服务,而ApplicationRef
(页面上运行的 Angular 应用程序)将在 Angular 区域之外创建。
在浏览器中启动时,会创建浏览器平台:
export const platformBrowser: (extraProviders?: StaticProvider[]) => PlatformRef = createPlatformFactory(platformCore, 'browser', INTERNAL_BROWSER_PLATFORM_PROVIDERS); // 其中,platformCore 平台必须包含在任何其他平台中 export const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);
使用平台工厂(例如上面的createPlatformFactory
)创建平台时,将隐式初始化页面的平台:
export function createPlatformFactory( parentPlatformFactory: ((extraProviders?: StaticProvider[]) => PlatformRef)|null, name: string, providers: StaticProvider[] = []): (extraProviders?: StaticProvider[]) => PlatformRef { const desc = `Platform: ${name}`; const marker = new InjectionToken(desc); // DI 令牌 return (extraProviders: StaticProvider[] = []) => { let platform = getPlatform(); // 若平台已创建,则不做处理 if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) { if (parentPlatformFactory) { // 若有父级平台,则直接使用父级平台,并更新相应的提供者 parentPlatformFactory( providers.concat(extraProviders).concat({provide: marker, useValue: true})); } else { const injectedProviders: StaticProvider[] = providers.concat(extraProviders).concat({provide: marker, useValue: true}, { provide: INJECTOR_SCOPE, useValue: 'platform' }); // 若无父级平台,则新建注入器,并创建平台 createPlatform(Injector.create({providers: injectedProviders, name: desc})); } } return assertPlatform(marker); }; }
通过以上过程,我们知道 Angular 应用在创建平台的时候,创建平台的模块注入器ModuleInjector
。我们从上一节Injector
定义中也能看到,NullInjector
是所有注入器的顶部:
export abstract class Injector { static NULL: Injector = new NullInjector(); }
因此,在平台模块注入器之上,还有NullInjector()
。而在平台模块注入器之下,则还有应用程序模块注入器。
应用程序根模块(AppModule)注入器
每个应用程序有至少一个 Angular 模块,根模块就是用来启动此应用的模块:
@NgModule({ providers: APPLICATION_MODULE_PROVIDERS }) export class ApplicationModule { // ApplicationRef 需要引导程序提供组件 constructor(appRef: ApplicationRef) {} }
AppModule
根应用模块由BrowserModule
重新导出,当我们使用 CLI 的new
命令创建新应用时,它会自动包含在根AppModule
中。应用程序根模块中,提供者关联着内置的 DI 令牌,用于为引导程序配置根注入器。
Angular 还将ComponentFactoryResolver
添加到根模块注入器中。此解析器存储了entryComponents
系列工厂,因此它负责动态创建组件。
模块注入器层级
到这里,我们可以简单地梳理出模块注入器的层级关系:
模块注入器树的最上层则是应用程序根模块(AppModule)注入器,称作 root。
在 root 之上还有两个注入器,一个是平台模块(PlatformModule)注入器,一个是
구성 요소 및 모듈의 종속성 주입 프로세스는 Angular의 다단계 종속성 주입 설계와 불가분의 관계에 있습니다.NullInjector()
다단계 종속성 주입
ModuleInjector
모듈 인젝터:@NgModule()
또는@ The Injectable() 사용
주석은 이 계층 구조의ModuleInjector
ElementInjector
요소 인젝터를 구성합니다. 모든 DOM 요소에 암시적으로 생성됩니다 모듈 인젝터와 요소 인젝터는 모두 트리입니다. - 구조화되어 있지만 계층 구조가 정확히 동일하지는 않습니다. 🎜모듈 인젝터
🎜모듈 인젝터의 계층 구조는 애플리케이션의 모듈 설계뿐만 아니라 플랫폼과도 관련이 있습니다. 모듈( PlatformModule) 인젝터 및 애플리케이션 모듈(AppModule) 인젝터 계층 구조입니다. 🎜플랫폼 모듈(PlatformModule) 인젝터
🎜Angular 용어에서 플랫폼은 Angular 애플리케이션이 실행되는 컨텍스트입니다. Angular 애플리케이션의 가장 일반적인 플랫폼은 웹 브라우저이지만 모바일 장치의 운영 체제 또는 웹 서버일 수도 있습니다. 🎜🎜Angular 애플리케이션이 시작되면 플랫폼 레이어가 생성됩니다. 🎜- 플랫폼은 웹 페이지에서 Angular의 진입점이며 각 페이지에는 단 하나의 플랫폼만 있습니다. 🎜
- 실행 중인 모든 Angular 애플리케이션 페이지에서 모든 일반적인 서비스는 플랫폼 내에 바인딩됩니다. 🎜🎜🎜주로 모듈 인스턴스 생성, 파괴 및 기타 기능을 포함하는 Angular 플랫폼: 🎜🎜실제로 플랫폼이 시작될 때(
class ComponentFactory_ extends ComponentFactory<any>{ create( injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any, ngModule?: NgModuleRef<any>): ComponentRef<any> { if (!ngModule) { throw new Error('ngModule should be provided'); } const viewDef = resolveDefinition(this.viewDefFactory); const componentNodeIndex = viewDef.nodes[0].element!.componentProvider!.nodeIndex; // 使用根数据创建根视图 const view = Services.createRootView( injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT); // view.nodes 的访问器 const component = asProviderData(view, componentNodeIndex).instance; if (rootSelectorOrNode) { view.renderer.setAttribute(asElementData(view, 0).renderElement, 'ng-version', VERSION.full); } // 创建组件 return new ComponentRef_(view, new ViewRef_(view), component); } }
로그인 후 복사로그인 후 복사bootstrapModuleFactory에서)
메서드),ngZone.run
에ngZoneInjector
를 생성하여 Angular 영역에 모든 인스턴스화된 서비스를 생성하고ApplicationRef
(다음에서 실행됨) 페이지 Angular 애플리케이션)은 Angular 영역 외부에 생성됩니다. 🎜🎜브라우저에서 시작하면 브라우저 플랫폼이 생성됩니다. 🎜🎜 플랫폼 팩토리(예: 위의function createRootData( elInjector: Injector, ngModule: NgModuleRef<any>, rendererFactory: RendererFactory2, projectableNodes: any[][], rootSelectorOrNode: any): RootData { const sanitizer = ngModule.injector.get(Sanitizer); const errorHandler = ngModule.injector.get(ErrorHandler); const renderer = rendererFactory.createRenderer(null, null); return { ngModule, injector: elInjector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler, }; }
로그인 후 복사로그인 후 복사createPlatformFactory
)를 사용하여 플랫폼이 생성되면 페이지의 플랫폼이 암시적으로 초기화됩니다. 🎜🎜By 위의 과정에서 우리는 Angular 애플리케이션이 플랫폼을 생성할 때 플랫폼의 모듈 인젝터export interface ElementDef { ... // 在该视图中可见的 DI 的公共提供者 publicProviders: {[tokenKey: string]: NodeDef}|null; // 与 visiblePublicProviders 相同,但还包括位于此元素上的私有提供者 allProviders: {[tokenKey: string]: NodeDef}|null; }
로그인 후 복사로그인 후 복사ModuleInjector
를 생성한다는 것을 알고 있습니다. 또한 이전 섹션의Injector
정의에서NullInjector
가 모든 인젝터의 최상위임을 알 수 있습니다. 🎜🎜 따라서 플랫폼 모듈 인젝터 위에는const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
로그인 후 복사로그인 후 복사NullInjector()
입니다. 플랫폼 모듈 인젝터 아래에는 애플리케이션 모듈 인젝터도 있습니다. 🎜애플리케이션 루트 모듈(AppModule) 인젝터
🎜각 애플리케이션에는 하나 이상의 Angular 모듈이 있으며 루트 모듈은 모듈을 시작하는 데 사용됩니다. 🎜🎜class Injector_ implements Injector { constructor(private view: ViewData, private elDef: NodeDef|null) {} get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any { const allowPrivateServices = this.elDef ? (this.elDef.flags & NodeFlags.ComponentView) !== 0 : false; return Services.resolveDep( this.view, this.elDef, allowPrivateServices, {flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue); } }
로그인 후 복사로그인 후 복사AppModule
CLI의new
명령을 사용하여 새로운 애플리케이션은 루트AppModule
에 자동으로 포함됩니다. 애플리케이션 루트 모듈에서 공급자는 부트스트랩에 대한 루트 인젝터를 구성하는 데 사용되는 내장 DI 토큰과 연결됩니다. 🎜🎜Angular는 루트 모듈 인젝터에ComponentFactoryResolver
도 추가합니다. 이 파서는 팩토리의entryComponents
계열을 저장하므로 구성 요소를 동적으로 생성하는 일을 담당합니다. 🎜모듈 인젝터 계층 구조
🎜이 시점에서는 모듈 인젝터의 계층 관계를 간단하게 정리할 수 있습니다. 🎜- 🎜모듈 인젝터 트리의 최상위 수준은 root라고 불리는 애플리케이션 루트 모듈(AppModule) 인젝터입니다. 🎜🎜
- 🎜루트 위에 두 개의 인젝터가 있는데, 하나는 플랫폼 모듈(PlatformModule) 인젝터이고 다른 하나는
NullInjector()
입니다. 🎜🎜🎜🎜따라서 모듈 인젝터의 계층 구조는 다음과 같습니다. 🎜🎜🎜🎜🎜실제 애플리케이션에서는 다음과 같을 가능성이 높습니다. 🎜Angular DI 具有分层注入体系,这意味着下级注入器也可以创建它们自己的服务实例。
元素注入器
前面说过,在 Angular 中有两个注入器层次结构,分别是模块注入器和元素注入器。
元素注入器的引入
当 Angular 中懒加载的模块开始广泛使用时,出现了一个 issue:依赖注入系统导致懒加载模块的实例化加倍。
在这一次修复中,引入了新的设计:注入器使用两棵并行的树,一棵用于元素,另一棵用于模块。
Angular 会为所有
entryComponents
创建宿主工厂,它们是所有其他组件的根视图。这意味着每次我们创建动态 Angular 组件时,都会使用根数据(
RootData
)创建根视图(RootView
):class ComponentFactory_ extends ComponentFactory<any>{ create( injector: Injector, projectableNodes?: any[][], rootSelectorOrNode?: string|any, ngModule?: NgModuleRef<any>): ComponentRef<any> { if (!ngModule) { throw new Error('ngModule should be provided'); } const viewDef = resolveDefinition(this.viewDefFactory); const componentNodeIndex = viewDef.nodes[0].element!.componentProvider!.nodeIndex; // 使用根数据创建根视图 const view = Services.createRootView( injector, projectableNodes || [], rootSelectorOrNode, viewDef, ngModule, EMPTY_CONTEXT); // view.nodes 的访问器 const component = asProviderData(view, componentNodeIndex).instance; if (rootSelectorOrNode) { view.renderer.setAttribute(asElementData(view, 0).renderElement, 'ng-version', VERSION.full); } // 创建组件 return new ComponentRef_(view, new ViewRef_(view), component); } }
로그인 후 복사로그인 후 복사该根数据(
RootData
)包含对elInjector
和ngModule
注入器的引用:function createRootData( elInjector: Injector, ngModule: NgModuleRef<any>, rendererFactory: RendererFactory2, projectableNodes: any[][], rootSelectorOrNode: any): RootData { const sanitizer = ngModule.injector.get(Sanitizer); const errorHandler = ngModule.injector.get(ErrorHandler); const renderer = rendererFactory.createRenderer(null, null); return { ngModule, injector: elInjector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, rendererFactory, renderer, errorHandler, }; }
로그인 후 복사로그인 후 복사引入元素注入器树,原因是这样的设计比较简单。通过更改注入器层次结构,避免交错插入模块和组件注入器,从而导致延迟加载模块的双倍实例化。因为每个注入器都只有一个父对象,并且每次解析都必须精确地寻找一个注入器来检索依赖项。
元素注入器(Element Injector)
在 Angular 中,视图是模板的表示形式,它包含不同类型的节点,其中便有元素节点,元素注入器位于此节点上:
export interface ElementDef { ... // 在该视图中可见的 DI 的公共提供者 publicProviders: {[tokenKey: string]: NodeDef}|null; // 与 visiblePublicProviders 相同,但还包括位于此元素上的私有提供者 allProviders: {[tokenKey: string]: NodeDef}|null; }
로그인 후 복사로그인 후 복사默认情况下
ElementInjector
为空,除非在@Directive()
或@Component()
的providers
属性中进行配置。当 Angular 为嵌套的 HTML 元素创建元素注入器时,要么从父元素注入器继承它,要么直接将父元素注入器分配给子节点定义。
如果子 HTML 元素上的元素注入器具有提供者,则应该继承该注入器。否则,无需为子组件创建单独的注入器,并且如果需要,可以直接从父级的注入器中解决依赖项。
元素注入器与模块注入器的设计
那么,元素注入器与模块注入器是从哪个地方开始成为平行树的呢?
我们已经知道,应用程序根模块(
AppModule
)会在使用 CLI 的new
命令创建新应用时,自动包含在根AppModule
中。当应用程序(
ApplicationRef
)启动(bootstrap
)时,会创建entryComponent
:const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
로그인 후 복사로그인 후 복사该过程会使用根数据(
RootData
)创建根视图(RootView
),同时会创建根元素注入器,在这里elInjector
为Injector.NULL
。在这里,Angular 的注入器树被分成元素注入器树和模块注入器树,这两个平行的树了。
Angular 会有规律的创建下级注入器,每当 Angular 创建一个在
@Component()
中指定了providers
的组件实例时,它也会为该实例创建一个新的子注入器。类似的,当在运行期间加载一个新的NgModule
时,Angular 也可以为它创建一个拥有自己的提供者的注入器。子模块和组件注入器彼此独立,并且会为所提供的服务分别创建自己的实例。当 Angular 销毁
NgModule
或组件实例时,也会销毁这些注入器以及注入器中的那些服务实例。Angular 解析依赖过程
上面我们介绍了 Angular 中的两种注入器树:模块注入器树和元素注入器树。那么,Angular 在提供依赖时,又会以怎样的方式去进行解析呢。
在 Angular 种,当为组件/指令解析 token 获取依赖时,Angular 分为两个阶段来解析它:
- 针对
ElementInjector
层次结构(其父级) - 针对
ModuleInjector
层次结构(其父级)
其过程如下(参考多级注入器-解析规则):
当组件声明依赖项时,Angular 会尝试使用它自己的
ElementInjector
来满足该依赖。如果组件的注入器缺少提供者,它将把请求传给其父组件的
ElementInjector
。这些请求将继续转发,直到 Angular 找到可以处理该请求的注入器或用完祖先
ElementInjector
。如果 Angular 在任何
ElementInjector
中都找不到提供者,它将返回到发起请求的元素,并在ModuleInjector
层次结构中进行查找。如果 Angular 仍然找不到提供者,它将引发错误。
为此,Angular 引入一种特殊的合并注入器。
合并注入器(Merge Injector)
合并注入器本身没有任何值,它只是视图和元素定义的组合。
class Injector_ implements Injector { constructor(private view: ViewData, private elDef: NodeDef|null) {} get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any { const allowPrivateServices = this.elDef ? (this.elDef.flags & NodeFlags.ComponentView) !== 0 : false; return Services.resolveDep( this.view, this.elDef, allowPrivateServices, {flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue); } }
로그인 후 복사로그인 후 복사当 Angular 解析依赖项时,合并注入器则是元素注入器树和模块注入器树之间的桥梁。当 Angular 尝试解析组件或指令中的某些依赖关系时,会使用合并注入器来遍历元素注入器树,然后,如果找不到依赖关系,则切换到模块注入器树以解决依赖关系。
class ViewContainerRef_ implements ViewContainerData { ... // 父级试图元素注入器的查询 get parentInjector(): Injector { let view = this._view; let elDef = this._elDef.parent; while (!elDef && view) { elDef = viewParentEl(view); view = view.parent!; } return view ? new Injector_(view, elDef) : new Injector_(this._view, null); } }
로그인 후 복사解析过程
注入器是可继承的,这意味着如果指定的注入器无法解析某个依赖,它就会请求父注入器来解析它。具体的解析算法在
resolveDep()
方法中实现:export function resolveDep( view: ViewData, elDef: NodeDef, allowPrivateServices: boolean, depDef: DepDef, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any { // // mod1 // / // el1 mod2 // \ / // el2 // // 请求 el2.injector.get(token)时,按以下顺序检查并返回找到的第一个值: // - el2.injector.get(token, default) // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module // - mod2.injector.get(token, default) }
로그인 후 복사如果是
<child></child>
这样模板的根AppComponent
组件,那么在 Angular 中将具有三个视图:<!-- HostView_AppComponent --> <my-app></my-app> <!-- View_AppComponent --> <child></child> <!-- View_ChildComponent --> some content
로그인 후 복사依赖解析过程,解析算法会基于视图层次结构,如图所示进行:
如果在子组件中解析某些令牌,Angular 将:
首先查看子元素注入器,进行检查
elRef.element.allProviders|publicProviders
。然后遍历所有父视图元素(1),并检查元素注入器中的提供者。
如果下一个父视图元素等于
null
(2),则返回到startView
(3),检查startView.rootData.elnjector
(4)。只有在找不到令牌的情况下,才检查
startView.rootData module.injector
( 5 )。
由此可见,Angular 在遍历组件以解析某些依赖性时,将搜索特定视图的父元素而不是特定元素的父元素。视图的父元素可以通过以下方法获得:
// 对于组件视图,这是宿主元素 // 对于嵌入式视图,这是包含视图容器的父节点的索引 export function viewParentEl(view: ViewData): NodeDef|null { const parentView = view.parent; if (parentView) { return view.parentNodeDef !.parent; } else { return null; } }
로그인 후 복사总结
本文主要介绍了 Angular 中注入器的层级结构,在 Angular 中有两棵平行的注入器树:模块注入器树和元素注入器树。
元素注入器树的引入,主要是为了解决依赖注入解析懒加载模块时,导致模块的双倍实例化问题。在元素注入器树引入后,Angular 解析依赖的过程也有调整,优先寻找元素注入器以及父视图元素注入器等注入器的依赖,只有元素注入器中无法找到令牌时,才会查询模块注入器中的依赖。
更多编程相关知识,请访问:编程入门!!
위 내용은 Angular의 다중 레벨 종속성 주입 설계에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!
- 针对

핫 AI 도구

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

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

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

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

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

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

뜨거운 주제











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

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

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

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

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

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

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

각도-날짜/시간 선택기 형식을 사용자 정의하는 방법은 무엇입니까? 다음 기사에서는 형식을 사용자 정의하는 방법에 대해 설명합니다. 모든 사람에게 도움이 되기를 바랍니다.
