为什么每次更改路线时都会重新实例化 angular2 组件? [英] Why do my angular2 components get re-instantiated each time I change the route?

查看:21
本文介绍了为什么每次更改路线时都会重新实例化 angular2 组件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试组合 Angular 2 + Rx.JS 5/Next 的演示应用程序.

我注意到每次切换路由时都会重新实例化我的组件.

这是根应用程序的代码:

import {bootstrap} from 'angular2/platform/browser';从 'angular2/http' 导入 {HTTP_PROVIDERS};从'angular2/router'导入{ROUTER_PROVIDERS};从'./app.component.ts'导入{AppComponent};bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS]);

这是根组件的代码:

import {Component} from 'angular2/core';从 'angular2/router' 导入 {RouteConfig, ROUTER_DIRECTIVES};从'./app.first-component.ts'导入{FirstComponent};从'./app.second-component.ts'导入{SecondComponent};从./app.services.ts"导入{AppService};@成分({选择器:'我的应用',提供者:[AppService,FirstComponent,SecondComponent],指令:[FirstComponent,SecondComponent,ROUTER_DIRECTIVES],模板:`<h1>Angular 2 App</h1><a [routerLink]="['First']">first-default</a><a [routerLink]="['Second']">second</a><路由器插座></路由器插座>`})@RouteConfig([{path: '/', name: 'First', 组件: FirstComponent, useAsDefault: true},{路径:'/second',名称:'Second',组件:SecondComponent}])导出类 AppComponent {}

然后是第一个组件的代码(映射到/):

import {Component, OnInit, NgZone} from "angular2/core";从./app.services.ts"导入{AppService};导入 'rxjs/Rx';@成分({选择器:'我的第一个',模板:`<div><ul><li *ngFor="#s of someStrings">一个字符串:{{ s }}

`})导出类 FirstComponent 实现 OnInit {区域:NgZone;构造函数(私有应用服务:应用服务){console.log('constructor', 'first');this.zone = new NgZone({enableLongStackTrace: false});}someStrings:string[] = [];ngOnInit() {console.log('ngOnInit', 'first');this.appService.refCounted.subscribe(字符串=>{this.zone.run(() =>this.someStrings.push(...theStrings));},错误=>控制台.log(错误));}}

第二个组件(映射到/second):

import {Component, OnInit, NgZone} from "angular2/core";从./app.services.ts"导入{AppService};@成分({选择器:'我的第二个',模板:`<div><ul><li *ngFor="#s of someStrings">一个字符串:{{ s }}

`})导出类 SecondComponent 实现 OnInit {区域:NgZone;构造函数(私有应用服务:应用服务){console.log('constructor', 'second');this.zone = new NgZone({enableLongStackTrace: false});}someStrings:string[] = [];ngOnInit() {console.log('ngOnInit', 'second');this.appService.refCounted.subscribe(字符串=>{this.zone.run(() =>this.someStrings.push(...theStrings));},错误=>控制台.log(错误));}}

最后是应用服务(与此问题的相关性略低):

import {Injectable} from "angular2/core";从rxjs/Observable"导入 {Observable};从rxjs/主题"导入{主题};导入 'rxjs/Rx';@Injectable()导出类 AppService {构造函数(){console.log('constructor', 'appService');}someObservable$:Observable= Observable.create(观察者 => {const eventSource = new EventSource('/interval-sse-observable');eventSource.onmessage = x =>观察者.next(JSON.parse(x.data));eventSource.onerror = x =>观察者.错误(console.log('事件源失败'));返回 () =>{eventSource.close();};});主题$ = 新主题();refCounted = this.someObservable$.multicast(this.subject$).refCount();someMethod_() {let someObservable$:Observable= Observable.create(观察者 => {const eventSource = new EventSource('/interval-sse-observable');eventSource.onmessage = x =>观察者.next(JSON.parse(x.data));eventSource.onerror = x =>观察者.错误(console.log('事件源失败'));返回 () =>{eventSource.close();};});返回 someObservable$;}}

所以为了调试FirstSecond组件的实例化,我在constructors/ngOnInit中添加了console.log:

我注意到每次我通过点击链接改变路线时,我得到:

构造函数优先首先 ngOnInit第二个构造函数ngOnInit 秒...

有人可以告知这是否是预期的行为吗?如果是这样,我怎样才能让 Angular2 只实例化我的组件一次?

请注意,我已经明确要求通过在根级组件中添加一个 providers 数组来实例化 FirstSecond 组件.

附言这是该项目的 github 存储库:https://github.com/balteo/demo-angular2-rxjs/tree/WITH-ROUTER

编辑:

我仍在努力寻找解决此问题的方法.我的路由器或我的组件有问题.我已将该应用推送到 github 此处希望有人可以提供建议.

解决方案

>= 2.3.0-rc.0

可以实现自定义 RouteReuseStrategy 来控制路由组件何时被销毁、重新创建或重用.

>= 2.0.0

CanReuse 在新路由器中不再存在.当仅路由参数更改而保持在同一路由上时,不会重新创建组件.

如果路由更改并导航回同一组件,则重新创建该组件.

<=RC.x

<块引用>

请注意,我已明确要求通过在根级组件中添加提供者数组来实例化 First 和 Second 组件.

providers: [] 添加组件几乎没有意义,尤其是在这个问题上.

你可以实现CanReuse

通过添加

routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) { return true;}

到您的组件,但是对于重用同一实例的哪种导航非常有限(如果您保持相同的路线并且只更改参数).

如果 CanReuse 没有解决你的问题,那么将数据移到一个服务,在那里保存实例并将组件的视图绑定到这个服务的数据,使组件显示当前数据.

I am trying to put together a demo app of Angular 2 + Rx.JS 5/Next.

I noticed that my components are re-instantiated each time I switch the route.

Here is the code for the root app:

import {bootstrap}    from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {AppComponent} from './app.component.ts';

bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS]);

Here is the code for the root component:

import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {FirstComponent} from './app.first-component.ts';
import {SecondComponent} from './app.second-component.ts';
import {AppService} from "./app.services.ts";


@Component({
    selector: 'my-app',
    providers: [AppService, FirstComponent, SecondComponent],
    directives: [FirstComponent, SecondComponent, ROUTER_DIRECTIVES],
    template: `<h1>An Angular 2 App</h1>
               <a [routerLink]="['First']">first-default</a> 
               <a [routerLink]="['Second']">second</a> 
               <router-outlet></router-outlet>`
})
@RouteConfig([
    {path: '/', name: 'First', component: FirstComponent, useAsDefault: true},
    {path: '/second', name: 'Second', component: SecondComponent}
])
export class AppComponent {
}

Then the code for the first component (mapped to /):

import {Component, OnInit, NgZone} from "angular2/core";
import {AppService} from "./app.services.ts";
import 'rxjs/Rx';


@Component({
    selector: 'my-first',
    template: `
<div>
    <ul>
        <li *ngFor="#s of someStrings">
           a string: {{ s }}
        </li>
    </ul>
 </div>`
})
export class FirstComponent implements OnInit {

    zone:NgZone;

    constructor(private appService:AppService) {
        console.log('constructor', 'first');
        this.zone = new NgZone({enableLongStackTrace: false});
    }

    someStrings:string[] = [];

    ngOnInit() {
        console.log('ngOnInit', 'first');
        this.appService.refCounted.subscribe(
            theStrings=> {
                this.zone.run(() =>this.someStrings.push(...theStrings));
            },
            error=>console.log(error)
        );
    }
}

And the second component (mapped to /second):

import {Component, OnInit, NgZone} from "angular2/core";
import {AppService} from "./app.services.ts";

@Component({
    selector: 'my-second',
    template: `
<div>
    <ul>
        <li *ngFor="#s of someStrings">
           a string: {{ s }}
        </li>
    </ul>
 </div>`
})
export class SecondComponent implements OnInit {

    zone:NgZone;

    constructor(private appService:AppService) {
        console.log('constructor', 'second');
        this.zone = new NgZone({enableLongStackTrace: false});
    }

    someStrings:string[] = [];

    ngOnInit() {
        console.log('ngOnInit', 'second');
        this.appService.refCounted.subscribe(
            theStrings=> {
                this.zone.run(() =>this.someStrings.push(...theStrings));
            },
            error=>console.log(error)
        );
    }
}

And finally the app service (slightly less relevant to this question):

import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Observable";
import {Subject} from "rxjs/Subject";
import 'rxjs/Rx';


@Injectable()
export class AppService {

    constructor(){
        console.log('constructor', 'appService');
    }

    someObservable$:Observable<string[]> = Observable.create(observer => {
        const eventSource = new EventSource('/interval-sse-observable');
        eventSource.onmessage = x => observer.next(JSON.parse(x.data));
        eventSource.onerror = x => observer.error(console.log('EventSource failed'));

        return () => {
            eventSource.close();
        };
    });

    subject$ = new Subject();

    refCounted = this.someObservable$.multicast(this.subject$).refCount();

    someMethod_() {
        let someObservable$:Observable<string[]> = Observable.create(observer => {
            const eventSource = new EventSource('/interval-sse-observable');
            eventSource.onmessage = x => observer.next(JSON.parse(x.data));
            eventSource.onerror = x => observer.error(console.log('EventSource failed'));

            return () => {
                eventSource.close();
            };
        });
        return someObservable$;
    }
}

So in order to debug the instantiation of the First and Second components, I have added a console.log in the constructors/ngOnInit:

and I noticed that each time I change the route by clicking on the links, I get:

constructor first
ngOnInit first

constructor second
ngOnInit second
...

Can someone please advise whether this is expected behavior? If so how can I get Angular2 to instantiate my components only once?

Note that I have explicitly required that the First and Second components be instantiated at the root-level component by adding a providers array there.

P.S. Here is the github repository for this project: https://github.com/balteo/demo-angular2-rxjs/tree/WITH-ROUTER

edit:

I am still trying to find a solution to this problem. I am getting something wrong with the router or my components. I have pushed the app to github here hoping someone can provide advice.

解决方案

>= 2.3.0-rc.0

A custom RouteReuseStrategy can be implemented to control when routed components are destroyed and recreated or reused.

>= 2.0.0

CanReuse doesn't exist anymore in the new router. When only route parameters change while staying on the same route, the component is not recreated.

If the route is changed and navigated back to the same component the component is recreated.

<=RC.x

Note that I have explicitly required that the First and Second components be instantiated at the root-level component by adding a providers array there.

Adding components to providers: [] is mostly meaningless especially in regard to this issue.

You can implement CanReuse

by adding

routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) { return true; }

to your component, but it's quite limited for which kind of navigation the same instance is reused (if you stay on the same route and only change parameters).

If CanReuse doesn't fix your problem then move the data to a service, where instances are kept and bind the view of the component to the data of this service to make the component show current data.

这篇关于为什么每次更改路线时都会重新实例化 angular2 组件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆