Angular:为整个 class(服务或组件)禁用更改检测器
Angular: Disable Change Detector for an entire class (service or component)
如果通常会导致更改检测的事件 运行(setTimeout、setInterval、浏览器事件、ajax 调用等),是否有办法完全禁用 Angular 的更改检测器..),是否来自特定的 class(服务或组件)?
也就是说,当我发现在我的服务中注册的 setInterval
导致全局变化检测每秒 运行 时,我似乎完全错了..
我知道我可以将我的代码包装在 NgZone.runOutsideAngular
方法的回调中,但我更喜欢可以为整个 class 禁用更改检测器的解决方案,因为我有服务中的其他代码块也不必要地 运行检测。
感谢您的帮助。
更改检测从组件树的最顶端开始,由 zone.js 触发。
像 setTimeout 这样的异步操作被 zone.js 接收,它通知 angular 可能发生了变化。 Angular 然后 运行 在组件树上进行自上而下的更改检测。
从变更检测器中分离单个 class 将从变更检测中删除 class(即指令),但不会阻止变更检测成为组件树其余部分的 运行。
根据您的需要,ngZone.runOutsideAngular(...)
是可行的方法,因为它会阻止 zone.js 通知 angular 您的异步操作,从而完全阻止变更检测 运行.
您可以使用以下方法更改单个组件的检测策略:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'ws-layout-page',
templateUrl: './layout-page.component.html',
styleUrls: ['./layout-page.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LayoutPageComponent {
}
我不知道有任何其他方法可以实现一些选择性转向on/of取决于信息来自哪里
一个可能的解决方案可能是为您的服务使用以下 @RunOutsideAngular
装饰器:
declare let Zone: any;
export function RunOutsideAngular(target: any) {
Object.getOwnPropertyNames(target.prototype)
.filter(p => typeof target.prototype[p] === 'function')
.forEach(p => {
let originalMethod = target.prototype[p];
target.prototype[p] = function (...args) {
let self = this;
Zone.root.run(() => originalMethod.apply(self, args));
}
});
let ctor: any = function (...args) {
let self = this;
return Zone.root.run(() => target.apply(self, args));
};
ctor.prototype = target.prototype;
return ctor;
}
如果您只想在某些 class 中禁用 setTimeout
和 setInterval
,您可以修补这些功能
function patchTimers(timers: any[]) {
timers.forEach((timer) => {
let originalMethod = window[timer];
window[timer] = function (...args) {
let self = this;
if (Zone.current['__runOutsideAngular__'] === true && Zone.current.name === 'angular') {
Zone.root.run(() => originalMethod.apply(self, args));
} else {
originalMethod.apply(this, arguments);
}
};
})
}
patchTimers(['setTimeout', 'setInterval']);
并像这样创建装饰器
export function RunOutsideAngular(target: any) {
Object.getOwnPropertyNames(target.prototype)
.filter(p => typeof target.prototype[p] === 'function')
.forEach(p => {
let originalMethod = target.prototype[p];
target.prototype[p] = function (...args) {
Zone.current['__runOutsideAngular__'] = true;
originalMethod.apply(this, args);
delete Zone.current['__runOutsideAngular__'];
}
});
let ctor: any = function (...args) {
Zone.current['__runOutsideAngular__'] = true;
let instance = target.apply(this, args);
delete Zone.current['__runOutsideAngular__'];
return instance;
};
ctor.prototype = target.prototype;
return ctor;
}
那么就可以如下使用了
@RunOutsideAngular
export class Service {
constructor() {
setInterval(() => {
console.log('ctor tick');
}, 1000);
}
run() {
setTimeout(() => {
console.log('tick');
}, 1000);
setInterval(() => {
console.log('tick interval');
}, 1000)
}
}
如果通常会导致更改检测的事件 运行(setTimeout、setInterval、浏览器事件、ajax 调用等),是否有办法完全禁用 Angular 的更改检测器..),是否来自特定的 class(服务或组件)?
也就是说,当我发现在我的服务中注册的 setInterval
导致全局变化检测每秒 运行 时,我似乎完全错了..
我知道我可以将我的代码包装在 NgZone.runOutsideAngular
方法的回调中,但我更喜欢可以为整个 class 禁用更改检测器的解决方案,因为我有服务中的其他代码块也不必要地 运行检测。
感谢您的帮助。
更改检测从组件树的最顶端开始,由 zone.js 触发。
像 setTimeout 这样的异步操作被 zone.js 接收,它通知 angular 可能发生了变化。 Angular 然后 运行 在组件树上进行自上而下的更改检测。
从变更检测器中分离单个 class 将从变更检测中删除 class(即指令),但不会阻止变更检测成为组件树其余部分的 运行。
根据您的需要,ngZone.runOutsideAngular(...)
是可行的方法,因为它会阻止 zone.js 通知 angular 您的异步操作,从而完全阻止变更检测 运行.
您可以使用以下方法更改单个组件的检测策略:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'ws-layout-page',
templateUrl: './layout-page.component.html',
styleUrls: ['./layout-page.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LayoutPageComponent {
}
我不知道有任何其他方法可以实现一些选择性转向on/of取决于信息来自哪里
一个可能的解决方案可能是为您的服务使用以下 @RunOutsideAngular
装饰器:
declare let Zone: any;
export function RunOutsideAngular(target: any) {
Object.getOwnPropertyNames(target.prototype)
.filter(p => typeof target.prototype[p] === 'function')
.forEach(p => {
let originalMethod = target.prototype[p];
target.prototype[p] = function (...args) {
let self = this;
Zone.root.run(() => originalMethod.apply(self, args));
}
});
let ctor: any = function (...args) {
let self = this;
return Zone.root.run(() => target.apply(self, args));
};
ctor.prototype = target.prototype;
return ctor;
}
如果您只想在某些 class 中禁用 setTimeout
和 setInterval
,您可以修补这些功能
function patchTimers(timers: any[]) {
timers.forEach((timer) => {
let originalMethod = window[timer];
window[timer] = function (...args) {
let self = this;
if (Zone.current['__runOutsideAngular__'] === true && Zone.current.name === 'angular') {
Zone.root.run(() => originalMethod.apply(self, args));
} else {
originalMethod.apply(this, arguments);
}
};
})
}
patchTimers(['setTimeout', 'setInterval']);
并像这样创建装饰器
export function RunOutsideAngular(target: any) {
Object.getOwnPropertyNames(target.prototype)
.filter(p => typeof target.prototype[p] === 'function')
.forEach(p => {
let originalMethod = target.prototype[p];
target.prototype[p] = function (...args) {
Zone.current['__runOutsideAngular__'] = true;
originalMethod.apply(this, args);
delete Zone.current['__runOutsideAngular__'];
}
});
let ctor: any = function (...args) {
Zone.current['__runOutsideAngular__'] = true;
let instance = target.apply(this, args);
delete Zone.current['__runOutsideAngular__'];
return instance;
};
ctor.prototype = target.prototype;
return ctor;
}
那么就可以如下使用了
@RunOutsideAngular
export class Service {
constructor() {
setInterval(() => {
console.log('ctor tick');
}, 1000);
}
run() {
setTimeout(() => {
console.log('tick');
}, 1000);
setInterval(() => {
console.log('tick interval');
}, 1000)
}
}