찾다
웹 프론트엔드JS 튜토리얼Angular10 컴포넌트 관련 개념을 한 글에서 살펴보겠습니다.

이 글에서는 angular10의 컴포넌트에 대해 소개하고, 컴포넌트 요소, 컴포넌트 수명주기, 컴포넌트 간 통신, 동적 컴포넌트의 기본 사용법을 함께 살펴보겠습니다.

Angular10 컴포넌트 관련 개념을 한 글에서 살펴보겠습니다.

【관련 튜토리얼 추천: "angular tutorial"】

ComponentComponents

  • html template
  • typescript,behavior 정의
  • css 그룹, 여러 CSS 파일을 도입할 수 있으므로 여러 정의 가능 하나의 구성 요소에 CSS 파일
//从angular主模块中引入Component(组件装饰器或组件注解)
import { Component } from '@angular/core';

//装饰器中以json的形式声明元数据
@Component({
  //它指定了一个叫 <app-root> 的元素。 该元素是 index.html 文件里的一个占位符
  //为什么这个组件跟入口index建立了联系呢?因为入口main.ts中绑定了主模块为appModule
  selector: 'app-root',   //在模板中找对应的标签,找到后创建并插入该组件实例
  templateUrl: './app.component.html',  // html模板
  styleUrls: ['./app.component.css'],   // css样式,可以引入多个css文件
  // 这个属性(内联模板)和templateUrl(外联模板)二选一,template后面可以直接跟html字符串
  // 注意在模板语法(反引号)中是使用插值表达式,不能使用${}插入值
  template: `<h1 id="title">{{title}}</h1>`   
})
//组件控制器,写逻辑代码的地方
export class AppComponent {
  title = 'myAngular';
  //构造函数可以用来进行属性的声明和初始化语句
  //在angular里面有个特别重要的点要记住:只能通过构造函数注入依赖
  constructor() {}
}</app-root>

전제 조건

  • Angular CLI 설치(npm install -g @angular/cli)
  • Angular 프로젝트 만들기(ng new)

이 두 가지 조건이 충족되지 않으면 빌드 환경을 참조하세요.

컴포넌트 생성 방법

Angular CLI를 사용하여 컴포넌트 생성

ng generate component <project-name> // 创建一个组件

ng g c <project-name> // 缩写

컴포넌트 생성에 일반적으로 사용되는 기타 옵션

ng g c <project-name>  --skip-tests // 创建一个组件,且不用安装测试文件

ng g c <project-name> --inline-style // 缩写-s,内联样式

ng g c <project-name> --inline-template // 缩写-t,内联模板

ng g c <project-name> --module= <module-name> // 指定创建的组件在哪个模块引用,在多个模块的项目中会用到

Angular CLI를 통해 자동으로 컴포넌트를 생성하는 것 외에도 수동으로 컴포넌트를 생성할 수도 있습니다(권장하지 않음) ), 여기에는 소개가 없습니다.

컴포넌트 라이프사이클(high-level content, master ngOnInit, ngOnDestroy, ngOnChanges, ngAfterViewInit())

작업에서 자주 사용하는 라이프사이클은 ngOninit, ngOnDestroy 2개 뿐이며, 나머지 라이프사이클은 다음과 같습니다. 매우 드물게 사용되지만 구성요소의 수명주기를 마스터할 수 있다면 각도에 ​​대한 더 깊은 이해를 갖게 될 것입니다.

라이프 사이클 의미

컴포넌트 인스턴스의 라이프 사이클은 Angular가 컴포넌트 클래스를 인스턴스화하고 컴포넌트 뷰와 해당 하위 뷰를 렌더링할 때 시작됩니다. 수명 주기에는 항상 변경 감지가 수반되며, 데이터 바인딩된 속성이 변경되면 Angular가 확인하고 필요에 따라 뷰 및 구성 요소 인스턴스를 업데이트합니다. Angular가 구성 요소 인스턴스를 삭제하고 DOM에서 렌더링된 템플릿을 제거하면 수명 주기가 종료됩니다. 지시문은 Angular가 실행 중에 인스턴스를 생성, 업데이트 및 삭제하는 것과 유사한 수명 주기를 갖습니다.

애플리케이션:

앱은 수명 주기 후크 메서드를 사용하여 구성 요소 또는 지시어 수명 주기의 주요 이벤트를 트리거하여 새 인스턴스를 초기화하고, 필요할 때 변경 감지를 시작하고, 변경 감지 중 업데이트에 응답하고, 필요할 때 인스턴스를 삭제할 수 있습니다. .

라이프 사이클 이벤트 구현 방법

모든 구성 요소 또는 지시문은 적절한 시간에 구성 요소 또는 지시문 인스턴스에서 작동할 수 있는 하나 이상의 수명 주기 후크를 구현할 수 있습니다.

각 인터페이스에는 고유한 후크 방법이 있으며 해당 이름은 인터페이스 이름과 ng 접두사로 구성됩니다. 예를 들어 OnInit 인터페이스의 후크 메서드는 ngOnInit()이라고 합니다. 이 메서드를 구성 요소 또는 지시문 클래스에 구현하면 Angular는 먼저 구성 요소 또는 지시문의 입력 속성을 확인한 후 이를 호출합니다.

import { Component } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-root&#39;,
  styleUrls: [&#39;./app.component.css&#39;],  
  template: `<h1 id="title">{{title}}</h1>`   
})
// 实现OnInit生命周期,可以实现多个生命周期
export class AppComponent implements OnInit{ 
  title = &#39;myAngular&#39;;
  constructor() {}
  ngOnInit(){
      console.log("Angular在首次检查完组件的输入属性后,然后调用它,只调用一次")
  }
}

Lifecycle 개요

Hook Timing Purpose Note
ngOnChanges() 바인딩된 입력 속성의 값이 변경될 때 호출되며, 첫 번째 호출은 ngOnInit()보다 먼저 발생해야 합니다. Response 각도일 때 데이터 바인딩된 입력 속성을 설정하거나 재설정합니다. 이 메서드는 현재 및 이전 속성 값의 SimpleChanges 개체를 허용합니다. ​​ 이 일은 매우 자주 발생하므로 여기에서 수행하는 모든 작업은 성능에 큰 영향을 미칩니다.
ngOnInit() ngOnChanges()의 첫 번째 라운드가 완료된 후 호출되며 한 번만 호출됩니다. Angular가 먼저 데이터 바인딩을 표시하고 지시문/구성 요소의 입력 속성을 설정한 후 지시문/구성 요소를 초기화합니다. 컴포넌트가 초기 데이터를 얻기에 좋은 곳입니다 변경 감지가 수행될 때마다 ngOnChanges() 직후에, 변경 감지가 수행될 때 ngOnInit() 직후에 한 번만 호출하는 것이 중요합니다
ngDoCheck() 처음으로. Angular가 자체적으로 감지할 수 없거나 감지할 의사가 없는 변경 사항이 발생하면 감지하고 대응합니다. ngOnChanges만큼 자주 발생합니다
ngAfterContentInit() 처음 ngDoCheck()가 호출될 때 한 번만. Angular가 외부 콘텐츠를 구성 요소 뷰 또는 지시어가 있는 뷰에 투영한 후에 호출됩니다. 한 번만 호출됩니다
ngAfterContentChecked() ngAfterContentInit() 및 각 ngDoCheck() 후에 Angular가 구성 요소 또는 지시문에 투영된 콘텐츠를 확인할 때마다 호출됩니다.
ngAfterViewInit() ngAfterContentChecked()가 처음 호출될 때 한 번만 호출됩니다. 구성요소 뷰와 해당 하위 뷰를 초기화한 후 호출됩니다. 한 번만 호출
ngAfterViewChecked() ngAfterViewInit()하고 ngAfterContentChecked() 이후 매번 호출하세요. 컴포넌트 뷰와 하위 뷰의 변경 감지를 완료한 후 매번 호출됩니다.
ngOnDestroy() Angular가 지시문/구성 요소를 삭제하기 전에 호출됩니다. 각 지시문/구성 요소가 Angular에 의해 삭제되기 전에 호출되고 청소됩니다. 메모리 누수를 방지하려면 관찰 가능 항목의 구독을 취소하고 여기에서 이벤트 핸들러를 분리하세요.
관찰 가능한 개체 구독 취소,
타이머 지우기,
이 명령으로 전역적으로 또는 애플리케이션 서비스에 등록된 모든 콜백 등록 취소.
중요해요

重点生命周期详解

初始化组件和指令 ngOnInit

  • 在构造函数外部执行复杂的初始化。组件的构造应该既便宜又安全。比如,你不应该在组件构造函数中获取数据。当在测试中创建组件时或者决定显示它之前,你不应该担心新组件会尝试联系远程服务器。ngOnInit() 是组件获取初始数据的好地方
  • 在 Angular 设置好输入属性之后设置组件。构造函数应该只把初始局部变量设置为简单的值。请记住,只有在构造完成之后才会设置指令的数据绑定输入属性。如果要根据这些属性对指令进行初始化,请在运行 ngOnInit() 时设置它们

在实例销毁时进行清理 ngOnDestroy

这里是释放资源的地方,这些资源不会自动被垃圾回收。如果你不这样做,就存在内存泄漏的风险。

  • 取消订阅可观察对象和 DOM 事件。
  • 停止 interval 计时器。
  • 反注册该指令在全局或应用服务中注册过的所有回调。
  • ngOnDestroy() 方法也可以用来通知应用程序的其它部分,该组件即将消失

页面埋点

可以在组件中通过ngOnInit和ngOnDestroy方法统计页面的时长,

更好的做法可以通过指令实现ngOnInit和ngOnDestroy生命周期,用来统计页面时长

也可以通过路由守卫来记录页面出入栈所需要的时间

@Directive({selector: &#39;[appSpy]&#39;})
export class SpyDirective implements OnInit, OnDestroy {

  constructor(private logger: LoggerService) { }

  ngOnInit()    { this.logIt(`onInit`); }

  ngOnDestroy() { this.logIt(`onDestroy`); }

  private logIt(msg: string) {
    this.logger.log(`Spy #${nextId++} ${msg}`);
  }
}

使用变更检测钩子 ngOnchanges

一旦检测到该组件或指令的输入属性发生了变化,Angular 就会调用它的 ngOnChanges() 方法。

因为这个方法会频繁执行,所以要注意判断的计算量,会影响性能。

// 可以监听输入属性的变化
ngOnChanges(changes: SimpleChanges) {
  for (const propName in changes) {
    const chng = changes[propName];
    const cur  = JSON.stringify(chng.currentValue);
    const prev = JSON.stringify(chng.previousValue);
    this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
  }
}

组件之间的通信(重要,必须掌握)

一、父子组件的通信(基础)

父传子

1、父组件通过属性绑定向子组件传值

import { Component } from &#39;@angular/core&#39;;
@Component({
  selector: &#39;app-parent&#39;,
  template: `
    <app-child [msg]="msg"></app-child>
  `
})
export class ParentComponent {
  msg = &#39;父组件传的值&#39;;
}

2、子组件通过@Input接收数据

import { Component, Input } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-child&#39;,
  template: `
    <p>{{msg}}</p>
  `
})
export class ChildComponent {
  @Input() msg: String;
}

子传父

1、子组件通过自定义事件,向父组件发送数据

import { Component, Input, EventEmitter, Output} from &#39;@angular/core&#39;;
,
@Component({
  selector: &#39;app-child&#39;,
  template: `
    <p>{{msg}}</p>
    <button (click)="vote()" >发送</button>
  `
})
export class ChildComponent {
  @Input() msg: String;
  @Output() voted = new EventEmitter<boolean>();
  
  vote() {
    this.voted.emit("子组件传的值");
  }
}

2、父组件通过监听自定义事件,接收子组件的传值

import { Component } from &#39;@angular/core&#39;;
@Component({
  selector: &#39;app-parent&#39;,
  template: `
    <app-child [msg]="msg" (voted)="voted($event)"></app-child>
  `
})
export class ParentComponent {
  msg = &#39;父组件传的值&#39;;
  voted(val){ //监听自定义事件的传值
      console.log(val)
  }
}

子组件怎么监听输入属性值的变化?(2种方法)

1、可以使用一个输入属性@Input()的 setter,以拦截父组件中值的变化。

import { Component, Input } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-child&#39;,
  template: &#39;<h3 id="name">"{{name}}"</h3>&#39;
})
export class ChildComponent {
  @Input()
  get name(): string { return this._name; }
  set name(name: string) {
    this._name = (name && name.trim()) || &#39;<no name set>&#39;;
  }
  private _name = &#39;&#39;;
}

2、通过ngOnChange()来截听输入属性值的变化

当需要监视多个、交互式输入属性的时候,本方法比用属性的 setter 更合适。

import { Component, Input, OnChanges, SimpleChanges } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-version-child&#39;,
  template: `
    <h3 id="Version-nbsp-major-minor">Version {{major}}.{{minor}}</h3>
    <h4 id="Change-nbsp-log">Change log:</h4>
    <ul>
      <li *ngFor="let change of changeLog">{{change}}</li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major: number;
  @Input() minor: number;
  changeLog: string[] = [];

  ngOnChanges(changes: SimpleChanges) { //ngOnchange适合监听子组件多个输入属性的变化
    const log: string[] = [];
    for (const propName in changes) {
      const changedProp = changes[propName];
      const to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        const from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(&#39;, &#39;));
  }
}

父组件怎么读取子组件的属性和调用子组件的方法?(2种方法)

1、通过本地变量代表子组件

父组件不能使用数据绑定来读取子组件的属性或调用子组件的方法。但可以在父组件模板里,新建一个本地变量来代表子组件,然后利用这个变量来读取子组件的属性和调用子组件的方法,如下例所示。

思考:父组件可以通过这种方式读取子组件的私有属性和私有方法吗?

父组件

import { Component } from &#39;@angular/core&#39;;
import { CountdownTimerComponent } from &#39;./countdown-timer.component&#39;;

@Component({
  selector: &#39;app-countdown-parent-lv&#39;,
  template: `
  <h3 id="Countdown-nbsp-to-nbsp-Liftoff-nbsp-via-nbsp-local-nbsp-variable">Countdown to Liftoff (via local variable)</h3>
  <button (click)="timer.start()">Start</button> //调用子组件方法
  <button (click)="timer.stop()">Stop</button>
  <div class="seconds">{{timer.seconds}}</div> //读取子组件属性
  <app-countdown-timer #timer></app-countdown-timer>
  `,
  styleUrls: [&#39;../assets/demo.css&#39;]
})
export class CountdownLocalVarParentComponent { }

子组件

import { Component, OnDestroy } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-countdown-timer&#39;,
  template: &#39;<p>{{message}}</p>&#39;
})
export class CountdownTimerComponent implements OnDestroy {

  intervalId = 0;
  message = &#39;&#39;;
  seconds = 11;

  ngOnDestroy() { this.clearTimer(); }

  start() { this.countDown(); }
  stop()  {
    this.clearTimer();
    this.message = `Holding at T-${this.seconds} seconds`;
  }

  private clearTimer() { clearInterval(this.intervalId); }

  private countDown() {
    this.clearTimer();
    this.intervalId = window.setInterval(() => {
      this.seconds -= 1;
      if (this.seconds === 0) {
        this.message = &#39;Blast off!&#39;;
      } else {
        if (this.seconds < 0) { this.seconds = 10; } // reset
        this.message = `T-${this.seconds} seconds and counting`;
      }
    }, 1000);
  }
}

2、父组件调用@viewChild() (基础,推荐使用)

这个本地变量方法是个简单便利的方法。但是它也有局限性(只能在模板html中使用),因为父组件-子组件的连接必须全部在父组件的模板中进行。父组件本身的ts代码对子组件没有访问权。

当父组件类需要访问子组件时,可以把子组件作为 ViewChild,注入到父组件里面。

父组件类中访问子组件的属性和方法:

import { AfterViewInit, ViewChild } from &#39;@angular/core&#39;;
import { Component } from &#39;@angular/core&#39;;
import { CountdownTimerComponent } from &#39;./countdown-timer.component&#39;; //引入子组件

@Component({
  selector: &#39;app-countdown-parent-vc&#39;,
  template: `
  <h3 id="Countdown-nbsp-to-nbsp-Liftoff-nbsp-via-nbsp-ViewChild">Countdown to Liftoff (via ViewChild)</h3>
  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>
  <div class="seconds">{{ seconds() }}</div>
  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: [&#39;../assets/demo.css&#39;]
})
export class CountdownViewChildParentComponent implements AfterViewInit {

  //通过 @ViewChild 属性装饰器,将子组件 CountdownTimerComponent 注入到私有属性 timerComponent 里面。
  @ViewChild(CountdownTimerComponent) 
  private timerComponent: CountdownTimerComponent;

  seconds() { return 0; }

  // angular创建了组件的子视图后会调用它,注意获取子组件的属性,要在子组件视图加载之后
  ngAfterViewInit() {
    // 访问子组件属性
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }

  start() { this.timerComponent.start(); } // 访问子组件的方法
  stop() { this.timerComponent.stop(); }
}

注意:(使用场景很多,必须掌握)

ngAfterViewInit() 生命周期钩子是非常重要的一步。被注入的计时器组件只有在 Angular 显示了父组件视图之后才能访问,所以它先把秒数显示为 0.

然后 Angular 会调用 ngAfterViewInit 生命周期钩子,但这时候再更新父组件视图的倒计时就已经太晚了。Angular 的单向数据流规则会阻止在同一个周期内更新父组件视图。应用在显示秒数之前会被迫再等一轮。

使用 setTimeout() 来等下一轮,然后改写 seconds() 方法,这样它接下来就会从注入的这个计时器组件里获取秒数的值。

二、组件通过服务来通信(发布订阅者模式,基础,必须掌握)

父组件和它的子组件共享同一个服务,利用该服务在组件家族内部实现双向通信。

不仅局限于父子组件,只要组件与组件共享同一个服务,就可以实现数据通信。

<!--parent.component.html-->
<p style="width: 1000px;margin: auto">
<p class="card" style="width: 500px;float: left">
 <p class="card-header"> 父组件</p>

 <p class="card-body">
  <h5 id="父组件">父组件</h5>
  <p class="form-group">
   <label for="serviceoutput">父组件服务输入:</label>
   <input type="text"
       class="form-control"
       id="serviceoutput"
       placeholder="服务输入"
       [(ngModel)]="serviceInput"
   >
  </p>

  <button class="btn btn-primary" (click)="clickService()">Service方式</button>
 </p>

</p>
<app-child></app-child>
</p>
<!--child.component.html-->
<p class="card" style="width: 500px;">
 <p class="card-header">子组件</p>

 <p class="card-body">
  <h5 id="子组件">子组件</h5>
  <p class="form-group">
   <label for="serviceoutput">子组件服务输入:</label>
   <input type="text"
       class="form-control"
       id="serviceoutput"
       placeholder="服务输入"
       [(ngModel)]="serviceInput"
   >
  </p>

  <button class="btn btn-primary" (click)="clickService()">Service方式</button>
 </p>
</p>
//服务重点
//meditor.service.ts

import {Injectable} from &#39;@angular/core&#39;;
import {Subject} from &#39;rxjs/Subject&#39;;
import {Observable} from &#39;rxjs/Observable&#39;;

@Injectable()
export class MeditorService {
 private subject = new Subject<MeditorMsg>();
 constructor() {}
 // 获取订阅者
 public getObservable(): Observable<MeditorMsg> {
  return this.subject.asObservable();
 }

 // 推送信息
 public push(msg: MeditorMsg) {
  this.subject.next(msg);
 }
}

// 中间者信息
export interface MeditorMsg {
 id: string;
 body: any;
}
subscription: Subscription = null; //初始化一个订阅对象

//子组件构造函数,用于监听数据推送
constructor(private meditor: MeditorService) {
  this.subscription = meditor.getObservable().subscribe(
   msg => {
    console.log(msg);
    if (msg.id === &#39;parent&#39;) {   //id为parent,获取父组件数据
     this.serviceInput = msg.body;
    }
   }
  );
 }
// 子组件将数据推送到中间着,给订阅者
clickService() {
  this.meditor.push({id: &#39;parent&#39;, body: this.serviceInput});
 }

//父组件构造函数,用于监听数据推送
constructor(private meditor: MeditorService) {
  this.subscription = meditor.getObservable().subscribe(
   msg => {
    console.log(msg);
    if (msg.id === &#39;child&#39;) {    //id为child,获取子组件数据
     this.serviceInput = msg.body;
    }
   }
  );
 }

// 父组件将数据推送到中间着,给订阅者
clickService() {
  this.meditor.push({id: &#39;parent&#39;, body: this.serviceInput});
 }
 
// 注意:订阅一个对象,就是在生命周期结束前,要取消订阅。
ngOnDestroy() {
    this.subscription.unsubscribe();
}

思考: 这种发布订阅者模式适合全局状态管理吗?

三、可以通过本地缓存来实现通信(Cookie,LocalStorage、SessionStorage)

<!-- catch_namae_type.ts -->
// 目的是好维护
// 项目当中用到的页面缓存,需要在这里进行声明;key-value保持一致
// 声明规则,不同类型的缓存使用前缀 session_/ local_ / cookie_
// 动态设置缓存不用在这里声明,但是需要在key后面加&#39;_noSetType_&#39;标识
export const CatchNameType = {
  session_userInfo: &#39;session_userInfo&#39;, // 用户信息
  session_toekn: &#39;session_token&#39;, // token
  local_loginInfo: &#39;local_loginInfo&#39;, // 本地缓存用户名密码
};

<!--catch.ts-->
import { Injectable } from &#39;@angular/core&#39;;
// 定义这个类,主要是看全局定义了哪些本地缓存
import { CatchNameType } from &#39;./catch_namae_type&#39;;

// -------------------------------------------------------缓存工具类(三类方法)
// Cookie           (方法有:set/get/remove)
// SStorage(sessionStorage)  (方法有:set/get/remove/clear)
// LStorage(localStorage)    (方法有:set/get/remove/clear)
@Injectable({
  providedIn: &#39;root&#39;,
})
export class Catch {
  // cookie
  public static Cookie = {
    /**
     * cookie 存贮
     * @param key  属性
     * @param value  值
     * @param String expire  过期时间,单位天
     */
    set(key: string, value: any, expire: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        const d = new Date();
        d.setDate(d.getDate() + expire);
        document.cookie = `${key}=${value};expires=${d.toDateString()}`;
      }
    },
    get(key: string): string {
      const cookieStr = unescape(document.cookie);
      const arr = cookieStr.split(&#39;; &#39;);
      let cookieValue = &#39;&#39;;
      // tslint:disable-next-line: prefer-for-of
      for (let i = 0; i < arr.length; i++) {
        const temp = arr[i].split(&#39;=&#39;);
        if (temp[0] === key) {
          cookieValue = temp[1];
          break;
        }
      }
      return cookieValue;
    },
    remove(key: string): void {
      document.cookie = `${encodeURIComponent(key)}=;expires=${new Date()}`;
    },
  };

  // sessionStorage
  public static SStorage = {
    set(key: string, value: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        sessionStorage.setItem(key, JSON.stringify(value));
      }
    },
    get(key: string): any {
      const jsonString =
        sessionStorage.getItem(key) === &#39;undefined&#39;
          ? undefined
          : sessionStorage.getItem(key);
      return jsonString ? JSON.parse(jsonString) : null;
    },

    remove(key: string): void {
      sessionStorage.removeItem(key);
    },
    clear(): void {
      sessionStorage.clear();
    },
  };

  // localStorage
  public static LStorage = {
    set(key: string, value: any): void {
      if (Catch.is_set_catch_name_type(key)) {
        localStorage.setItem(key, JSON.stringify(value));
      }
    },
    get(key: string): any {
      const jsonString =
        localStorage.getItem(key) === &#39;undefined&#39;
          ? undefined
          : localStorage.getItem(key);
      return jsonString ? JSON.parse(jsonString) : null;
    },
    remove(key: string): void {
      localStorage.removeItem(key);
    },
    clear(): void {
      localStorage.clear();
    },
  };

  // 设置缓存的时候是否在catch_name_type里面声明
  static is_set_catch_name_type(key: string): boolean {
    let allow = false;
    // 对动态设置缓存不进行检查
    if (key.indexOf(&#39;_noSetType_&#39;) !== -1) {
      allow = true;
      console.log(&#39;动态设置缓存&#39;, key);
      return allow;
    }
    // 对命名规则进行检查
    const nameRule =
      key.indexOf(&#39;session_&#39;) !== -1 ||
      key.indexOf(&#39;local_&#39;) !== -1 ||
      key.indexOf(&#39;cookie_&#39;) !== -1;
    if (!nameRule) {
      allow = false;
      console.log(&#39;命名规则错误&#39;, key);
      return allow;
    }
    // 静态设置的缓存需要配置类型
    Object.values(CatchNameType).forEach((item) => {
      if (item === key) {
        allow = true;
      }
    });
    if (!allow) {
      console.log(&#39;缓存操作失败,请检查配置缓存类型&#39;);
    }
    return allow;
  }
}

四、页面路由传参也可以实现单向通信

这部分内容,我会在路由章节整理。

组件通信总结

所以组件通信大概有如下几种:

1、父子组件通信(1、@Input() @output 2、本地变量#val 3、@viewChild())

2、通过服务

3、页面缓存

4、页面级组件传参(两个页面等同于两个组件)

动态组件

组件的模板不会永远是固定的。应用可能会需要在运行期间按需加载一些新的组件。 通过下面的例子可以了解动态组件的基本使用

1、创建组件,被引入的组件

@Component({
  template: `<span>hello world</span>`
})
export class DynamicComponent { }

2、创建容器组件,用于加载动态组件

@Component({
  selector: &#39;app-container&#39;,
  template: `
    <div #dynamicContainer></div>
    <button (click)="createComponent()">Create</button>
  `
})
export class AppContainerComponent {
  // 声明容器
  @ViewChild("dynamicContainer", { read: ViewContainerRef }) container: ViewContainerRef;
}

在AppContainerComponent组件中,通过@ViewChild装饰器来获取视图中的模板元素,如果没有指定第二个查询参数,则默认返回 组件实例或相应的DOM元素,但这个示例中,我们需要获取ViewContainerRef实例也就是视图容器。可以在视图容器中创建、插入、删除组件等。

3、动态创建组件

在创建组件之前,需要注入ComponentFactoryResolver服务对象,该服务是动态加载组件的核心,可以将一个组件实例呈现到另一个 组件视图上。

  • 使用ComponentFactoryResolve将已声明但未实例化的组件解析成为可以动态加载的component
//依赖组件类型获取对应的factory,从名字上可以看出该factory是用来初始化组件的
const componentFactory = this.ComponentFactoryResolver(DynamicComponent);
//调用视图容器的createComponent方法并将组件添加到容器上。该方法内部会调用factory的create方法来初始化组件。
const modalContainerRef = this.container.createComponent(componentFactory);

4、为组件属性赋值

通过如下的方式为组件属性进行赋值

modalContainerRef.instance.property = ***;

5、销毁组件

在使用组件后,记得要销毁组件。

modalContainerRef.destroy();

6、组件注册

为了能够动态创建组件需要将组件在模块的entryComponents中声明。因为在entryComponents中声明的组件Angular都会创建一个 ComponentFactory并将其存储在ComponentFactoryResolver中,这是动态加载必需的步骤。

更多编程相关知识,请访问:编程入门!!

위 내용은 Angular10 컴포넌트 관련 개념을 한 글에서 살펴보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 掘金--苟鸿人에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
JavaScript의 역할 : 웹 대화식 및 역동적 인 웹JavaScript의 역할 : 웹 대화식 및 역동적 인 웹Apr 24, 2025 am 12:12 AM

JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.

C 및 JavaScript : 연결이 설명되었습니다C 및 JavaScript : 연결이 설명되었습니다Apr 23, 2025 am 12:07 AM

C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.

웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션웹 사이트에서 앱으로 : 다양한 JavaScript 애플리케이션Apr 22, 2025 am 12:02 AM

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Python vs. JavaScript : 사용 사례 및 응용 프로그램 비교Apr 21, 2025 am 12:01 AM

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

JavaScript 통역사 및 컴파일러에서 C/C의 역할JavaScript 통역사 및 컴파일러에서 C/C의 역할Apr 20, 2025 am 12:01 AM

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.

자바 스크립트 행동 : 실제 예제 및 프로젝트자바 스크립트 행동 : 실제 예제 및 프로젝트Apr 19, 2025 am 12:13 AM

실제 세계에서 JavaScript의 응용 프로그램에는 프론트 엔드 및 백엔드 개발이 포함됩니다. 1) DOM 운영 및 이벤트 처리와 관련된 TODO 목록 응용 프로그램을 구축하여 프론트 엔드 애플리케이션을 표시합니다. 2) Node.js를 통해 RESTFULAPI를 구축하고 Express를 통해 백엔드 응용 프로그램을 시연하십시오.

JavaScript 및 웹 : 핵심 기능 및 사용 사례JavaScript 및 웹 : 핵심 기능 및 사용 사례Apr 18, 2025 am 12:19 AM

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

JavaScript 엔진 이해 : 구현 세부 사항JavaScript 엔진 이해 : 구현 세부 사항Apr 17, 2025 am 12:05 AM

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

See all articles

핫 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 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

맨티스BT

맨티스BT

Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

메모장++7.3.1

메모장++7.3.1

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

mPDF

mPDF

mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.