如何在Angular中使用zone.js?

时间:2019-06-03 15:28:32

标签: javascript angular zone

我想在我的Angular项目中使用zone.js(而不仅仅是runOutsideAngularZone函数)。

我试图这样包含它:

import { zone } from 'zone.js';

不幸的是,我收到此错误:

error TS2306: File 'C:/projects/MyApp/node_modules/zone.js/dist/zone.js.d.ts' is not a module.

然后我删除了{ zone }部分:

import 'zone.js';

但是现在我收到此错误:

error TS2552: Cannot find name 'zone'. Did you mean 'Zone'?

我的代码是这样的:

    let myZoneSpec = {
        beforeTask: function () {
            console.log('Before task');
        },
        afterTask: function () {
            console.log('After task');
        }
    };
    let myZone = zone.fork(myZoneSpec);
    myZone.run(() => {console.log('Task');});

如果我将żone替换为Zone,我会得到:

error TS2339: Property 'fork' does not exist on type 'ZoneType'.

如何从Angular导入和使用zone.js?

3 个答案:

答案 0 :(得分:4)

Angular为 Zone.js 提供了一个包装类,称为 ngZone 。您可以像任何服务一样将其注入到组件中。

    constructor(private zone: NgZone) { 
       this.zone.run(() => { console.log('This is zone'});
     }

但是,通过这种方法,我们无法使用 zone.js 的全部功能。为此,我们必须声明:

declare let Zone: any;
public class MyComponent {
  constructor() {
        Zone.current.fork({
            name: 'myZone'
          }).run(() => {
            console.log('in myzone? ', Zone.current.name);
          });
    }
}

此外,自v.0.6.0起,API也已更改。对于运行beforeTask和afterTask,您可以查看它here,但是,我调查了一下,但是找不到与 beforeTask afterTask 相关的任何内容。 / p>

已更新
对于运行 beforeTask afterTask ,这是在新API中的方法。

constructor() {
    const parentZone = new Zone();
    const childZone = parentZone.fork({
      name: 'child',
      onInvoke: (...args) => { 
                 console.log('invoked\n', args);
                 const valueToReturn = args[3](); // Run the function provided in the Zone.run
                 console.log('after callback is run');
                 return valueToReturn;
              }
    });
   console.log(childZone.run(() => {console.log('from run'); return 'from child run';}));

}

注意:
如果您想创建一个 scheduleMicroTask 并希望其中也具有相同的功能,则需要在其中实现 onInvokeTask 和/或 onScheduleTask ZoneSpec(在parentZone.fork()内部)。

constructor() {
   const parentZone = new Zone();
    const childZone = parentZone.fork({
      name: 'child',
      onScheduleTask: (...args) => {
        console.log('task schedule...\n', args);
        return args[3];
      },
      onInvokeTask: (...args) => {
        console.log('task invoke...\n', args);
        return args[3].callback();
      }
    });

    const microTask = childZone
                      .scheduleMicroTask(
                        'myAwesomeMicroTask',
                        () => { 
                                console.log('microTask is running'); 
                                return 'value returned from microTask'; 
                            } );
    console.log(microTask.invoke());
}

答案 1 :(得分:0)

我强烈建议您在Angular中使用NgZone。 Documentation here

答案 2 :(得分:0)

有几种解决方案可以拦截异步任务,其中一种解决方案是创建自己的区域。

通过Zone.prototype.run输入应用程序也会在您的区域中进行引导。

可以使用Zone.prototype.fork方法创建自己的区域,fork-创建一个子区域,实际上复制父区域,复制的区域成为后代:

const customZone = Zone.current.fork({
  name: 'customZone'
});

name是区域的名称,您可以指定任何字符串,这是唯一必需的参数。 fork接受名为ZoneSpec的对象。它允许您指定一堆方法来拦截任何异步作业的生命周期。生命周期为-schedule -> do synchronous things -> execute at the right time

ZoneSpec接口允许实现此类挂钩:

  • onFork-钩子拦截子区域的创建
  • onInvoke-钩子以拦截Zone.prototype.run
  • 的调用
  • onHandleError-错误处理挂钩
  • onScheduleTask-拦截任务调度,例如调用setTimeout调度宏任务
  • onInvokeTask-拦截异步API中的回调,例如,当调用setTimeout中的回调时
  • onCancelTask-拦截任务或结束的取消
  • onHasTask-每次队列更改时调用

回到您的问题:

  

我想在每个任务之前和之后调用一个函数。这就是为什么我想使用区域

您可以创建区域并实现所需的挂钩,在这种情况下,您需要onInvokeTask

const customZone = Zone.current.fork({
  name: 'customZone',
  onInvokeTask(
    parentZoneDelegate: ZoneDelegate,
    currentZone: Zone,
    targetZone: Zone,
    task: Task,
    applyThis: any,
    applyArgs: any[]
  ) {
    console.log('before task...'); // do your stuff before any task here...
    const result = parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
    console.log('after task...'); // do your stuff after any task here...
    return result;
  }
});

customZone.run(() => {
  setTimeout(() => {
    console.log('setTimeout callback...');
  });
});

您将在控制台中看到:

before task...
setTimeout callback...
after task...

您还可以获取有关该任务所需的所有信息,例如我们只想在微任务之前和之后做一些事情:

const customZone = Zone.current.fork({
  name: 'customZone',
  onInvokeTask(
    parentZoneDelegate: ZoneDelegate,
    currentZone: Zone,
    targetZone: Zone,
    task: Task,
    applyThis: any,
    applyArgs: any[]
  ) {
    const isMicroTask = task.type === 'microTask';

    if (isMicroTask) {
      console.log('before micro task...'); // do your stuff before any micro task here...
    }

    const result = parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);

    if (isMicroTask) {
      console.log('after micro task...'); // do your stuff after any micro task here...
    }

    return result;
  }
});

customZone.run(() => {
  setTimeout(() => {
    console.log('setTimeout callback...');
  });

  Promise.resolve().then(() => {
    console.log('then callback...');
  });
});

您将在控制台中看到:

before micro task...
then callback...
after micro task...
setTimeout callback...

或仅用于事件任务:

const isEventTask = task.type === 'eventTask';

// check if `eventTask` and do something only for event tasks

const result = parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);

// check if `eventTask` and do something only for event tasks

return result;

如果“任何内容”都有通过addEventListener添加的事件监听器,则此方法适用于浏览器添加到队列中的任务:

customZone.run(() => {
  document.body.addEventListener('click', () => {
    console.log('onClick callback...');
  });

  document.body.dispatchEvent(new KeyboardEvent('click'));
});

为了拦截Angular中的异步任务-您需要使用Zone.prototype.run包装应用程序自举:

const customZone = Zone.current.fork({
  name: 'customZone',
  onInvokeTask(
    parentZoneDelegate: ZoneDelegate,
    currentZone: Zone,
    targetZone: Zone,
    task: Task,
    applyThis: any,
    applyArgs: any[]
  ) {
    console.log('before task...');
    console.log('task = ', task);
    const result = parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
    console.log('after task...');
    return result;
  }
});

customZone.run(() => {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(error => console.error(error));
});

enter image description here


您还可以通过提供自定义NgZone.prototype.run来拦截ApplicationRef.prototype.tickthis._zone.run(() => this.tick())(因为它们被NgZone包裹)调用:

if (environment.production) {
  enableProdMode();
}

class CustomNgZone extends NgZone {
  run<T>(fn: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]): T {
    // do something before the callback is invoked
    const result = super.run(fn, applyThis, applyArgs);
    // do something after the callback is invoked
    return result;
  }
}

const ngZone = new CustomNgZone({ enableLongStackTrace: isDevMode() });

platformBrowserDynamic()
  .bootstrapModule(AppModule, { ngZone })
  .catch(error => console.error(error));

请注意,由于CustomNgZone的调用将锁定Angular的全局变量(enableProdMode),因此我将在isDevMode之后创建_runModeLocked = true类的实例,因此生产函数将不使用。