Angular-退出Zone.JS进行特定的回调 [英] Angular - Getting out of Zone.JS for specific callbacks
问题描述
首先,我很了解 zone.runOutsideAngular(callback)
,该文档记录在此处.
此方法的作用是在与Angular区域不同的 区域中运行回调.
我需要在 document
的 mousemove
事件中添加一个非常快速的回调(以计算空闲时间).我不想将整个Zone.JS任务处理机制附加到此特定回调的执行队列中.我真的很想让回调函数在无补丁的浏览器运行时中运行.
我拥有的事件注册代码是:
<代码>私人registerIdleCallback(callback:(idleFromSeconds:number)=> void){让idleFromSeconds = 0;const resetCounter = function(){idleFromSeconds = -1;};document.addEventListener('mousemove',resetCounter);document.addEventListener('keypress',resetCounter);document.addEventListener('touchstart',resetCounter);window.setInterval(function(){callback(++ idleFromSeconds);},1000);}
问题是如何获取此代码以使用未打补丁的 document.addEventListener
,从而实现与Zone.JS的完全分离以及真正的本机性能?
好吧,事实证明这很简单,但并不简单,因此我将发布在这里找到的解决方案.
我想做的是在Zone.JS之前将可用的 这必须在加载polyfill之前完成,因为在该区域中已经加载了Zone.而且,决不能用 所以我需要在 在 现在我有一个可用的 现在 希望这对其他人会有所帮助:这并不复杂,但是将所有位都放在适当位置会很麻烦. First and foremost, I'm well aware of What this method does is running the callback in a different zone than the Angular one. I need to attach a very quick callback to The event registration code I have is: The question is how can I get this code to use the unpatched Well, it turned out to be simple, but not straightforward, so I'm going to post the solution I found here. What I want to do is saving a usable This must be done before the loading of polyfills, because it's in there that Zone is loaded. And, it must not be done in plain code in So I need a file In Now I have a Now the stackTrace in the Hope this can be of help to someone else: it's not complicated, but having all the bits in place was a bit of a chore. 这篇关于Angular-退出Zone.JS进行特定的回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! document.addEventListener
保存在全局对象中.> polyfills.ts
中的纯代码完成此操作,因为 import
语句是在处理其他任何代码之前进行的. polyfills.ts
的同一文件夹中的文件 zone-config.ts
.在后者中,需要额外导入:
import'./zone-config';//<-在...之前将此行添加到文件中//默认情况下,Angular本身需要Zone JS.导入'zone.js/dist/zone';//包含在Angular CLI中.
zone-config.ts
中,我做惯了:
(function(global){//将本机处理程序保存在window.unpatched对象中.如果(global.unpatched)返回;如果(global.Zone){抛出错误(区域已在运行:无法访问本机侦听器");}global.unpatched = {windowAddEventListener:window.addEventListener.bind(window),windowRemoveEventListener:window.removeEventListener.bind(window),documentAddEventListener:document.addEventListener.bind(document),documentRemoveEventListener:document.removeEventListener.bind(document)};//禁用WebSocket的Zone.JS补丁-与本问题无关const propsArray = global .__ Zone_ignore_on_properties ||(global .__ Zone_ignore_on_properties = []);propsArray.push({target:WebSocket.prototype,ignoreProperties:['close','error','open','message']});//禁用addEventListenerconst evtsArray = global .__ zone_symbol__BLACK_LISTED_EVENTS ||(global .__ zone_symbol__BLACK_LISTED_EVENTS = []);evtsArray.push('message');})(< any>窗口);
window.unpatched
对象,它允许我完全退出Zone.JS,用于非常具体的任务,例如我正在工作的 IdleService
上:
从'@ angular/core'导入{Injectable,NgZone};从'rxjs'导入{BehaviorSubject,Observable};从'rxjs/operators'导入{过滤器};/*这是在zone-config.ts中初始化的对象*/const未修补:{windowAddEventListener:typeof window.addEventListener;windowRemoveEventListener:typeof window.removeEventListener;documentAddEventListener:typeof document.addEventListener;documentRemoveEventListener:typeof document.removeEventListener;} = window ['unpatched'];/**管理用于检测页面上用户活动的事件处理程序,*通过鼠标或键盘事件.*/@Injectable({providerIn:'root'})导出类IdleService {private _idleForSecond $ =新的BehaviorSubject< number>(0);构造函数(区域:NgZone){const timerCallback =(idleFromSeconds:number):void =>this._idleForSecond $ .next(idleFromSeconds);this.registerIdleCallback(timerCallback);}私人registerIdleCallback(callback:(idleFromSeconds:number)=> void){让idleFromSeconds = 0;const resetCounter =()=>(idleFromSeconds = -1);//完全不在Zone.JS中运行unpatched.documentAddEventListener('mousemove',resetCounter);unpatched.documentAddEventListener('keypress',resetCounter);unpatched.documentAddEventListener('touchstart',resetCounter);//通常在区域中运行window.setInterval(()=>回调(++ idleFromSeconds),1000);}/**返回当用户不活动时间时发出的可观察对象*超过一定阈值.** @param seconds最小不活动时间,以秒为单位.*/byAtLeast(seconds:number):可观察的< number>{返回this._idleForSecond $ .pipe(filter(idleTime => idleTime> =秒));}/**返回一个观测值,该观测值自从* ladt用户的活动.*/by():可观察的< number>{返回this._idleForSecond $ .asObservable();}}
(idleFromSeconds = -1)
回调中的stackTrace根据需要为空.zone.runOutsideAngular(callback)
, documented here .document
's mousemove
event (to count Idle time). I don't want to have the whole Zone.JS machinery of tasks appended to execution queues for this specific callback. I would really like to have the callbacks run in plain, unpatched browser runtime. private registerIdleCallback(callback: (idleFromSeconds: number) => void) {
let idleFromSeconds = 0;
const resetCounter = function() {
idleFromSeconds = -1;
};
document.addEventListener('mousemove', resetCounter);
document.addEventListener('keypress', resetCounter);
document.addEventListener('touchstart', resetCounter);
window.setInterval(function() {
callback(++idleFromSeconds);
}, 1000);
}
document.addEventListener
, accomplishing a complete separation from Zone.JS and true native performance?document.addEventListener
in a global object before Zone.JS patches the hell out of the browser's native objects.polyfills.ts
, because the import
statements are processed before any other code.zone-config.ts
in the same folder of polyfills.ts
. In the latter an extra import is needed:import './zone-config'; // <- adding this line to the file, before...
// Zone JS is required by default for Angular itself.
import 'zone.js/dist/zone'; // Included with Angular CLI.
zone-config.ts
I do the sleight-of-hand:(function(global) {
// Save native handlers in the window.unpatched object.
if (global.unpatched) return;
if (global.Zone) {
throw Error('Zone already running: cannot access native listeners');
}
global.unpatched = {
windowAddEventListener: window.addEventListener.bind(window),
windowRemoveEventListener: window.removeEventListener.bind(window),
documentAddEventListener: document.addEventListener.bind(document),
documentRemoveEventListener: document.removeEventListener.bind(document)
};
// Disable Zone.JS patching of WebSocket -- UNRELATED TO THIS QUESTION
const propsArray = global.__Zone_ignore_on_properties || (global.__Zone_ignore_on_properties = []);
propsArray.push({ target: WebSocket.prototype, ignoreProperties: ['close', 'error', 'open', 'message'] });
// disable addEventListener
const evtsArray = global.__zone_symbol__BLACK_LISTED_EVENTS || (global.__zone_symbol__BLACK_LISTED_EVENTS = []);
evtsArray.push('message');
})(<any>window);
window.unpatched
object available, that allows me to opt out of Zone.JS completely, for very specific tasks, like the IdleService
I was working on:import { Injectable, NgZone } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
/* This is the object initialized in zone-config.ts */
const unpatched: {
windowAddEventListener: typeof window.addEventListener;
windowRemoveEventListener: typeof window.removeEventListener;
documentAddEventListener: typeof document.addEventListener;
documentRemoveEventListener: typeof document.removeEventListener;
} = window['unpatched'];
/** Manages event handlers used to detect User activity on the page,
* either via mouse or keyboard events. */
@Injectable({
providedIn: 'root'
})
export class IdleService {
private _idleForSecond$ = new BehaviorSubject<number>(0);
constructor(zone: NgZone) {
const timerCallback = (idleFromSeconds: number): void => this._idleForSecond$.next(idleFromSeconds);
this.registerIdleCallback(timerCallback);
}
private registerIdleCallback(callback: (idleFromSeconds: number) => void) {
let idleFromSeconds = 0;
const resetCounter = () => (idleFromSeconds = -1);
// runs entirely out of Zone.JS
unpatched.documentAddEventListener('mousemove', resetCounter);
unpatched.documentAddEventListener('keypress', resetCounter);
unpatched.documentAddEventListener('touchstart', resetCounter);
// runs in the Zone normally
window.setInterval(() => callback(++idleFromSeconds), 1000);
}
/** Returns an observable that emits when the user's inactivity time
* surpasses a certain threshold.
*
* @param seconds The minimum inactivity time in seconds.
*/
byAtLeast(seconds: number): Observable<number> {
return this._idleForSecond$.pipe(filter(idleTime => idleTime >= seconds));
}
/** Returns an observable that emits every second the number of seconds since the
* ladt user's activity.
*/
by(): Observable<number> {
return this._idleForSecond$.asObservable();
}
}
(idleFromSeconds = -1)
callback is empty as desired.