react 生命挂钩_角生命周期挂钩:ngOnChanges,ngOnInit等

react 生命挂钩

为什么我们需要生命周期挂钩? (Why do we need lifecycle hooks?)

Modern front-end frameworks move the application from state to state. Data fuels these updates. These technologies interact with the data which in turn transitions the state. With every state change, there are many specific moments where certain assets become available.

现代的前端框架将应用程序从一个州转移到另一个州。 数据助长了这些更新。 这些技术与数据交互,从而转换状态。 每次状态更改时,都有许多特定时刻可使用某些资产。

At one instance the template might be ready, in another data will have finished uploading. Coding for each instance requires a means of detection. Lifecycle hooks answer this need. Modern front-end frameworks package themselves with a variety of lifecycle hooks. Angular is no exception

在一个实例中,模板可能已准备就绪,而在另一实例中,数据已完成上传。 每个实例的编码都需要一种检测手段。 生命周期挂钩可满足此需求。 现代的前端框架通过各种生命周期挂钩将自身包装在一起。 角度也不例外

生命周期挂钩说明 (Lifecycle Hooks Explained)

Lifecycle hooks are timed methods. They differ in when and why they execute. Change detection triggers these methods. They execute depending on the conditions of the current cycle. Angular runs change detection constantly on its data. Lifecycle hooks help manage its effects.

生命周期挂钩是定时方法。 它们在执行时间和执行方式上有所不同。 更改检测将触发这些方法。 它们根据当前循环的条件执行。 Angular不断对其数据进行更改检测。 生命周期挂钩有助于管理其影响。

An important aspect of these hooks is their order of execution. It never deviates. They execute based on a predictable series of load events produced from a detection cycle. This makes them predictable.

这些挂钩的重要方面是它们的执行顺序。 它永远不会偏离。 它们基于检测周期产生的一系列可预测的负载事件执行。 这使它们可预测。

Some assets are only available after a certain hook executes. Of course, a hook only execute under certain conditions set in the current change detection cycle.

某些资产仅在执行某些挂钩后才可用。 当然,挂钩仅在当前变化检测周期中设置的特定条件下执行。

This article presents the lifecycle hooks in order of their execution (if they all execute). Certain conditions merit a hook’s activation. There are a few who only execute once after component initialization.

本文按生命周期挂钩的执行顺序(如果它们都执行)介绍了生命周期挂钩。 某些条件值得钩子激活。 有些组件在组件初始化后只执行一次。

All lifecycle methods are available from @angular/core. Although not required, Angular recommends implementing every hook. This practice leads to better error messages regarding the component.

所有生命周期方法都可以从@angular/core 。 尽管不是必需的,但Angular 建议实现每个hook 。 这种做法导致有关该组件更好的错误消息。

生命周期挂钩执行命令 (Order of Lifecycle Hooks' Execution)

ngOnChanges (ngOnChanges)

ngOnChanges triggers following the modification of @Input bound class members. Data bound by the @Input() decorator come from an external source. When the external source alters that data in a detectable manner, it passes through the @Input property again.

ngOnChanges在修改@Input绑定类成员ngOnChanges触发。 @Input()装饰器绑定的数据来自外部源。 当外部源以可检测的方式更改该数据时,它将再次通过@Input属性。

With this update, ngOnChanges immediately fires. It also fires upon initialization of input data. The hook receives one optional parameter of type SimpleChanges. This value contains information on the changed input-bound properties.

有了此更新, ngOnChanges立即触发。 输入数据初始化时也会触发。 挂钩接收一个类型为SimpleChanges可选参数。 此值包含有关更改的输入绑定属性的信息。

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

@Component({
  selector: 'app-child',
  template: `
  <h3>Child Component</h3>
  <p>TICKS: {{ lifecycleTicks }}</p>
  <p>DATA: {{ data }}</p>
  `
})
export class ChildComponent implements OnChanges {
  @Input() data: string;
  lifecycleTicks: number = 0;

  ngOnChanges() {
    this.lifecycleTicks++;
  }
}

@Component({
  selector: 'app-parent',
  template: `
  <h1>ngOnChanges Example</h1>
  <app-child [data]="arbitraryData"></app-child>
  `
})
export class ParentComponent {
  arbitraryData: string = 'initial';

  constructor() {
    setTimeout(() => {
      this.arbitraryData = 'final';
    }, 5000);
  }
}

Summary: ParentComponent binds input data to the ChildComponent. The component receives this data through its @Input property. ngOnChanges fires. After five seconds, the setTimeout callback triggers. ParentComponent mutates the data source of ChildComponent’s input-bound property. The new data flows through the input property. ngOnChanges fires yet again.

摘要: ParentComponent将输入数据绑定到ChildComponent。 该组件通过其@Input属性接收此数据。 ngOnChanges触发。 五秒钟后,将触发setTimeout回调。 ParentComponent更改ChildComponent的input-bound属性的数据源。 新数据流经input属性。 ngOnChanges再次触发。

ngOnInit (ngOnInit)

ngOnInit fires once upon initialization of a component’s input-bound (@Input) properties. The next example will look similar to the last one. The hook does not fire as ChildComponent receives the input data. Rather, it fires right after the data renders to the ChildComponent template.

初始化组件的输入绑定( @Input )属性后, ngOnInit会触发一次。 下一个示例看起来与上一个类似。 当ChildComponent接收输入数据时,该钩子不会触发。 而是在数据渲染到ChildComponent模板后立即触发。

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

@Component({
  selector: 'app-child',
  template: `
  <h3>Child Component</h3>
  <p>TICKS: {{ lifecycleTicks }}</p>
  <p>DATA: {{ data }}</p>
  `
})
export class ChildComponent implements OnInit {
  @Input() data: string;
  lifecycleTicks: number = 0;

  ngOnInit() {
    this.lifecycleTicks++;
  }
}

@Component({
  selector: 'app-parent',
  template: `
  <h1>ngOnInit Example</h1>
  <app-child [data]="arbitraryData"></app-child>
  `
})
export class ParentComponent {
  arbitraryData: string = 'initial';

  constructor() {
    setTimeout(() => {
      this.arbitraryData = 'final';
    }, 5000);
  }
}

Summary: ParentComponent binds input data to the ChildComponent. ChildComponent receives this data through its @Input property. The data renders to the template. ngOnInit fires. After five seconds, the setTimeout callback triggers. ParentComponent mutates the data source of ChildComponent’s input-bound property. ngOnInit DOES NOT FIRE.

摘要: ParentComponent将输入数据绑定到ChildComponent。 ChildComponent通过其@Input属性接收此数据。 数据呈现到模板。 ngOnInit触发。 五秒钟后,将触发setTimeout回调。 ParentComponent更改ChildComponent的input-bound属性的数据源。 ngOnInit 不会触发。

ngOnInit is a one-and-done hook. Initialization is its only concern.

ngOnInit是一个完成的钩子。 初始化是它唯一关心的问题。

ngDoCheck (ngDoCheck)

ngDoCheck fires with every change detection cycle. Angular runs change detection frequently. Performing any action will cause it to cycle. ngDoCheck fires with these cycles. Use it with caution. It can create performance issues when implemented incorrectly.

ngDoCheck在每个更改检测周期触发。 角行程经常检测变化。 执行任何操作都会导致其循环。 ngDoCheck在这些周期内触发。 请谨慎使用。 如果实施不正确,可能会导致性能问题。

ngDoCheck lets developers check their data manually. They can trigger a new application date conditionally. In conjunction with ChangeDetectorRef, developers can create their own checks for change detection.

ngDoCheck使开发人员可以手动检查其数据。 他们可以有条件地触发新的申请日期。 结合ChangeDetectorRef ,开发人员可以创建自己的检查以进行更改检测。

import { Component, DoCheck, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-example',
  template: `
  <h1>ngDoCheck Example</h1>
  <p>DATA: {{ data[data.length - 1] }}</p>
  `
})
export class ExampleComponent implements DoCheck {
  lifecycleTicks: number = 0;
  oldTheData: string;
  data: string[] = ['initial'];

  constructor(private changeDetector: ChangeDetectorRef) {
    this.changeDetector.detach(); // lets the class perform its own change detection

    setTimeout(() => {
      this.oldTheData = 'final'; // intentional error
      this.data.push('intermediate');
    }, 3000);

    setTimeout(() => {
      this.data.push('final');
      this.changeDetector.markForCheck();
    }, 6000);
  }

  ngDoCheck() {
    console.log(++this.lifecycleTicks);

    if (this.data[this.data.length - 1] !== this.oldTheData) {
      this.changeDetector.detectChanges();
    }
  }
}

Pay attention to the console versus the display. The data progress up to ‘intermediate’ before freezing. Three rounds of change detection occur over this period as indicated in the console. One more round of change detection occurs as ‘final’ gets pushed to the end of this.data. One last round of change detection then occurs. The evaluation of the if statement determines no updates to the view are necessary.

注意控制台和显示屏。 冻结之前,数据将升级到“中间”状态。 如控制台所示,在此期间进行了三轮变更检测。 随着“最终”被推送到this.data的末尾,又发生了一轮变更检测。 然后发生最后一轮变更检测。 if语句的评估确定不需要对该视图进行任何更新。

Summary: Class instantiates after two rounds of change detection. Class constructor initiates setTimeout twice. After three seconds, the first setTimeout triggers change detection. ngDoCheck marks the display for an update. Three seconds later, the second setTimeout triggers change detection. No view updates needed according to the evaluation of ngDoCheck.

摘要:类在经过两轮变更检测后实例化。 类构造函数将setTimeout初始化两次。 三秒钟后,第一个setTimeout触发更改检测。 ngDoCheck将显示标记为更新。 三秒钟后,第二个setTimeout触发更改检测。 根据ngDoCheck的评估,无需更新视图。

警告 (Warning)

Before proceeding, learn the difference between the content DOM and view DOM (DOM stands for Document Object Model).

在继续之前,请了解内容DOM和视图DOM之间的区别(DOM代表文档对象模型)。

The content DOM defines the innerHTML of directive elements. Conversely, the view DOM is a component’s template excluding any template HTML nested within a directive. For a better understanding, refer to this blog post.

内容DOM定义了指令元素的innerHTML。 相反,视图DOM是组件的模板,不包括嵌套在指令中的任何模板HTML。 为了更好的理解,请参阅此博客文章

ngAfterContentInit (ngAfterContentInit)

ngAfterContentInit fires after the component’s content DOM initializes (loads for the first time). Waiting on @ContentChild(ren) queries is the hook’s primary use-case.

组件的内容DOM初始化(首次加载)后,将触发ngAfterContentInit 。 等待@ContentChild(ren)查询是该挂钩的主要用例。

@ContentChild(ren) queries yield element references for the content DOM. As such, they are not available until after the content DOM loads. Hence why ngAfterContentInit and its counterpart ngAfterContentChecked are used.

@ContentChild(ren)查询产生内容DOM的元素引用。 因此,它们只有在内容DOM加载之后才可用。 因此,为什么要使用ngAfterContentInit及其对应的ngAfterContentChecked

import { Component, ContentChild, AfterContentInit, ElementRef, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-c',
  template: `
  <p>I am C.</p>
  <p>Hello World!</p>
  `
})
export class CComponent { }

@Component({
  selector: 'app-b',
  template: `
  <p>I am B.</p>
  <ng-content></ng-content>
  `
})
export class BComponent implements AfterContentInit {
  @ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef;
  @ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef;

  constructor(private renderer: Renderer2) { }

  ngAfterContentInit() {
    this.renderer.setStyle(this.hRef.nativeElement, 'background-color', 'yellow')

    this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', 'pink');
    this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', 'red');
  }
}

@Component({
  selector: 'app-a',
  template: `
  <h1>ngAfterContentInit Example</h1>
  <p>I am A.</p>
  <app-b>
    <h3 #BHeader>BComponent Content DOM</h3>
    <app-c></app-c>
  </app-b>
  `
})
export class AComponent { }

The @ContentChild query results are available from ngAfterContentInit. Renderer2 updates the content DOM of BComponent containing a h3 tag and CComponent. This is a common example of content projection.

@ContentChild查询结果可从ngAfterContentInitRenderer2更新包含h3标签和CComponent的BComponent的内容DOM。 这是内容投影的常见示例。

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the <ng-content></ng-content> element. CComponent is part of the projected content. The projected content finishes rendering. ngAfterContentInit fires. BComponent finishes rendering. AComponent finishes rendering. ngAfterContentInit will not fire again.

摘要:渲染从AComponent开始。 为此,AComponent必须呈现BComponent。 BComponent通过<ng-content></ng-content>元素投影嵌套在其元素中<ng-content></ng-content> 。 CComponent是计划内容的一部分。 投影内容完成渲染。 ngAfterContentInit触发。 BComponent完成渲染。 AComponent完成渲染。 ngAfterContentInit将不会再次触发。

ngAfterContentChecked (ngAfterContentChecked)

ngAfterContentChecked fires after every cycle of change detection targeting the content DOM. This lets developers facilitate how the content DOM reacts to change detection. ngAfterContentChecked can fire frequently and cause performance issues if poorly implemented.

ngAfterContentChecked在针对内容DOM的每个更改检测周期之后触发。 这使开发人员可以简化内容DOM对更改检测的React。 ngAfterContentChecked如果执行ngAfterContentChecked可能会经常触发并导致性能问题。

ngAfterContentChecked fires during a component’s initialization stages too. It comes right after ngAfterContentInit.

ngAfterContentChecked在组件的初始化阶段触发。 它ngAfterContentInitngAfterContentInit之后。

import { Component, ContentChild, AfterContentChecked, ElementRef, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-c',
  template: `
  <p>I am C.</p>
  <p>Hello World!</p>
  `
})
export class CComponent { }

@Component({
  selector: 'app-b',
  template: `
  <p>I am B.</p>
  <button (click)="$event">CLICK</button>
  <ng-content></ng-content>
  `
})
export class BComponent implements AfterContentChecked {
  @ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef;
  @ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef;

  constructor(private renderer: Renderer2) { }

  randomRGB(): string {
    return `rgb(${Math.floor(Math.random() * 256)},
    ${Math.floor(Math.random() * 256)},
    ${Math.floor(Math.random() * 256)})`;
  }

  ngAfterContentChecked() {
    this.renderer.setStyle(this.hRef.nativeElement, 'background-color', this.randomRGB());
    this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', this.randomRGB());
    this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', this.randomRGB());
  }
}

@Component({
  selector: 'app-a',
  template: `
  <h1>ngAfterContentChecked Example</h1>
  <p>I am A.</p>
  <app-b>
    <h3 #BHeader>BComponent Content DOM</h3>
    <app-c></app-c>
  </app-b>
  `
})
export class AComponent { }

This hardly differs from ngAfterContentInit. A mere <button></button> was added to BComponent. Clicking it causes a change detection loop. This activates the hook as indicated by the randomization of background-color.

这与ngAfterContentInit几乎没有区别。 仅将<button></button>添加到BComponent。 单击它会导致更改检测循环。 如background-color的随机化所示,这将激活钩子。

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the <ng-content></ng-content> element. CComponent is part of the projected content. The projected content finishes rendering. ngAfterContentChecked fires. BComponent finishes rendering. AComponent finishes rendering. ngAfterContentChecked may fire again through change detection.

摘要:渲染从AComponent开始。 为此,AComponent必须呈现BComponent。 BComponent通过<ng-content></ng-content>元素投影嵌套在其元素中<ng-content></ng-content> 。 CComponent是计划内容的一部分。 投影内容完成渲染。 ngAfterContentChecked触发。 BComponent完成渲染。 AComponent完成渲染。 ngAfterContentChecked可能会通过更改检测再次触发。

ngAfterViewInit (ngAfterViewInit)

ngAfterViewInit fires once after the view DOM finishes initializing. The view always loads right after the content. ngAfterViewInit waits on @ViewChild(ren) queries to resolve. These elements are queried from within the same view of the component.

视图DOM完成初始化后, ngAfterViewInit会触发一次。 视图始终在内容之后立即加载。 ngAfterViewInit等待@ViewChild(ren)查询解决。 从组件的同一视图中查询这些元素。

In the example below, BComponent’s h3 header is queried. ngAfterViewInit executes as soon as the query’s results are available.

在下面的示例中,查询BComponent的h3标头。 ngAfterViewInit查询结果可用, ngAfterViewInit执行。

import { Component, ViewChild, AfterViewInit, ElementRef, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-c',
  template: `
  <p>I am C.</p>
  <p>Hello World!</p>
  `
})
export class CComponent { }

@Component({
  selector: 'app-b',
  template: `
  <p #BStatement>I am B.</p>
  <ng-content></ng-content>
  `
})
export class BComponent implements AfterViewInit {
  @ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef;

  constructor(private renderer: Renderer2) { }

  ngAfterViewInit() {
    this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', 'yellow');
  }
}

@Component({
  selector: 'app-a',
  template: `
  <h1>ngAfterViewInit Example</h1>
  <p>I am A.</p>
  <app-b>
    <h3>BComponent Content DOM</h3>
    <app-c></app-c>
  </app-b>
  `
})
export class AComponent { }

Renderer2 changes the background color of BComponent’s header. This indicates the view element was successfully queried thanks to ngAfterViewInit.

Renderer2更改BComponent标题的背景颜色。 这表明通过ngAfterViewInit成功查询view元素。

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the <ng-content></ng-content> element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering. ngAfterViewInit fires. AComponent finishes rendering. ngAfterViewInit will not fire again.

摘要:渲染从AComponent开始。 为此,AComponent必须呈现BComponent。 BComponent通过<ng-content></ng-content>元素投影嵌套在其元素中<ng-content></ng-content> 。 CComponent是计划内容的一部分。 投影内容完成渲染。 BComponent完成渲染。 ngAfterViewInit触发。 AComponent完成渲染。 ngAfterViewInit不会再次触发。

ngAfterViewChecked (ngAfterViewChecked)

ngAfterViewChecked fires after any change detection cycle targeting the component’s view. The ngAfterViewChecked hook lets developers facilitate how change detection affects the view DOM.

ngAfterViewChecked在针对组件视图的任何更改检测周期之后触发。 ngAfterViewChecked挂钩使开发人员可以方便地进行更改检测如何影响视图DOM。

import { Component, ViewChild, AfterViewChecked, ElementRef, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-c',
  template: `
  <p>I am C.</p>
  <p>Hello World!</p>
  `
})
export class CComponent { }

@Component({
  selector: 'app-b',
  template: `
  <p #BStatement>I am B.</p>
  <button (click)="$event">CLICK</button>
  <ng-content></ng-content>
  `
})
export class BComponent implements AfterViewChecked {
  @ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef;

  constructor(private renderer: Renderer2) { }

  randomRGB(): string {
    return `rgb(${Math.floor(Math.random() * 256)},
    ${Math.floor(Math.random() * 256)},
    ${Math.floor(Math.random() * 256)})`;
  }

  ngAfterViewChecked() {
    this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', this.randomRGB());
  }
}

@Component({
  selector: 'app-a',
  template: `
  <h1>ngAfterViewChecked Example</h1>
  <p>I am A.</p>
  <app-b>
    <h3>BComponent Content DOM</h3>
    <app-c></app-c>
  </app-b>
  `
})
export class AComponent { }

Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the <ng-content></ng-content> element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering. ngAfterViewChecked fires. AComponent finishes rendering. ngAfterViewChecked may fire again through change detection.

摘要:渲染从AComponent开始。 为此,AComponent必须呈现BComponent。 BComponent通过<ng-content></ng-content>元素投影嵌套在其元素中<ng-content></ng-content> 。 CComponent是计划内容的一部分。 投影内容完成渲染。 BComponent完成渲染。 ngAfterViewChecked触发。 AComponent完成渲染。 ngAfterViewChecked可能会通过更改检测再次触发。

Clicking the <button></button> element initiates a round of change detection. ngAfterContentChecked fires and randomizes the background-color of the queried elements each button click.

单击<button></button>元素将启动一轮更改检测。 ngAfterContentChecked触发并随机化每个按钮单击的查询元素的background-color

ngOnDestroy (ngOnDestroy)

ngOnDestroy fires upon a component’s removal from the view and subsequent DOM. This hook provides a chance to clean up any loose ends before a component’s deletion.

ngOnDestroy在从视图和后续DOM中删除组件时触发。 该挂钩可在删除组件之前清理所有松动的末端。

import { Directive, Component, OnDestroy } from '@angular/core';

@Directive({
  selector: '[appDestroyListener]'
})
export class DestroyListenerDirective implements OnDestroy {
  ngOnDestroy() {
    console.log("Goodbye World!");
  }
}

@Component({
  selector: 'app-example',
  template: `
  <h1>ngOnDestroy Example</h1>
  <button (click)="toggleDestroy()">TOGGLE DESTROY</button>
  <p appDestroyListener *ngIf="destroy">I can be destroyed!</p>
  `
})
export class ExampleComponent {
  destroy: boolean = true;

  toggleDestroy() {
    this.destroy = !this.destroy;
  }
}

Summary: The button is clicked. ExampleComponent’s destroy member toggles false. The structural directive *ngIf evaluates to false. ngOnDestroy fires. *ngIf removes its host <p></p>. This process repeats any number of times clicking the button to toggle destroy to false.

摘要:单击该按钮。 ExampleComponent的destroy成员切换为false。 结构指令*ngIf计算结果为false。 ngOnDestroy触发。 *ngIf删除其主机<p></p> 。 单击该按钮以将destroy切换为false时,此过程将重复任意次数。

结论 (Conclusion)

Remember that certain conditions must be met for each hook. They will always execute in order of each other regardless. This makes hooks predictable enough to work with even if some do not execute.

请记住,每个挂钩必须满足某些条件。 无论如何,它们将始终按彼此的顺序执行。 即使没有执行钩子,这也足以使钩子可预测地工作。

With lifecycle hooks, timing the execution of a class is easy. They let developers track where change detection is occurring and how the application should react. They stall for code that requires load-based dependencies available only after sometime.

使用生命周期挂钩,计时类的执行很容易。 它们使开发人员可以跟踪发生更改检测的位置以及应用程序应如何应对。 它们停顿一些需要基于负载的依赖项的代码,这些代码仅在一段时间后才可用。

The component lifecycle characterizes modern front end frameworks. Angular lays out its lifecycle by providing the aforementioned hooks.

组件生命周期是现代前端框架的特征。 Angular通过提供上述钩子来规划其生命周期。

资料来源 (Sources)

资源资源 (Resources )

翻译自: https://www.freecodecamp.org/news/angular-lifecycle-hooks/

react 生命挂钩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值