如何仅在需要时加载Zone.js

时间:2017-08-18 10:32:32

标签: angular typescript webpack polyfills zonejs

我正在创建一个可以动态注入正在运行的网站的Angular 2应用程序。这个应用程序的目的是能够直观地修改网站内容。

当有问题的网站也没有使用Angular 2运行时,一切都按预期工作。特别是,当原始网站使用Angular 2和Zone.js时,我的应用程序也尝试加载Zone.js但它崩溃{{{ 3}}。我正在使用Webpack作为构建系统,我试图通过将构建分为3个部分来解决问题:manifestpolyfillapp。 Manifest仅包含Webpack清单,polyfill包含core-jszone.js,其余部分位于app。还有第四个名为index的大块。这是放入网站的那个,它首先检查是否定义了window.Zone。如果是,则仅添加manifestapp。否则也polyfill

问题是当没有加载polyfill块时,Webpack中缺少该块中的所有模块。因此,当代码从需要importapp的{​​{1}}块达到某些core-js时(我认为这就是我发生的事情),我收到此错误:

Zone.js

清单文件中的某处:

TypeError: Cannot read property 'call' of undefined
    at __webpack_require__ (manifest.js:55)
    at Object.<anonymous> (app.js:15016)
    at __webpack_require__ (manifest.js:55)
    at Object.<anonymous> (app.js:65567)
    at __webpack_require__ (manifest.js:55)
    at Object.<anonymous> (app.js:65163)
    at __webpack_require__ (manifest.js:55)
    at Object.defineProperty.value (app.js:65137)
    at __webpack_require__ (manifest.js:55)
    at webpackJsonpCallback (manifest.js:26)

问题

如何将Webpack或Angular配置为不导入/******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 作为依赖项,而是使用已在窗口中注册的那个?我希望能够有条件地加载zone.jscore-js,只有当它尚未加载到网站上时才会加载。

更新

我修改了我的构建(Webpack)只使用一个bundle。在该捆绑包中,我尝试使用Webpack的error: Zone already loaded,如下所示:

zone.js

我正在使用Typescript,这需要定义文件。问题是dynamic imports所以当我像这样使用它时,我得到一个关于缺少定义文件的错误。我该如何解决这个问题?

5 个答案:

答案 0 :(得分:9)

如果您不想将zone.js加载到捆绑包中,只需从polyfills.ts中删除导入内容:

import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';  <---- remove this line

如果你想在运行时检查Zone.js的存在并根据结果决定使用它:

declare var System;
if (!window['Zone']) {
    System.import('zone.js/dist/zone');  // Included with Angular CLI.
}

答案 1 :(得分:5)

我必须将接受的答案和一些评论放在一起,以便在tsc 2.8.1中使用它,所以这里的一切都在一起:

首先,确保tsconfig.app.json具有支持动态加载的模块版本:

"module": "esnext" 

其次,通过注释掉这一行来防止在polyfills.ts中自动导入zone.js:

// import 'zone.js/dist/zone';  // Included with Angular CLI.

第三,在main.ts中添加以下内容:

// if the zone has already been loaded, go ahead an bootstrap the app
if (window['Zone']) {
    bootstrap();

// otherwise, wait to bootstrap the app until zone.js is imported
} else {
    import('zone.js/dist/zone')
        .then(() => bootstrap());
}

function bootstrap() {
    platformBrowserDynamic().bootstrapModule(AppModule)
        .catch(err => console.log(err));
}

答案 2 :(得分:0)

我遇到了同样的情况,并将我的构建系统从commonjs / webpack更改为systemjs构建并运行系统

在systemjs中,您可以加载单个文件并配置获取该文件的位置。

在多种技术的环境中,多个团队在单页面体系结构中将代码推送到同一页面,systemjs给了我很好的体验。

答案 3 :(得分:0)

这是我的解决方案和几个月后这个问题的故事。基本上,很长一段时间我在我的项目中使用accepted answer's solution并且它运行得很好。它可以检测是否需要Zone并仅在尚未加载时加载它。

然而,当底层应用程序加载缓慢并且我的应用程序将在底层应用程序之前引导时出现问题。在这种情况下,我的应用程序可能会没有加载和加载Zone,只会在它继续加载时与底层应用程序冲突。

还有其他问题,特别是与这个问题无关。例如,我发现Angular与Prototype冲突,许多网站使用Prototype!

总而言之,我必须彻底改变方法。我将我的应用程序移动到iframe,该{{1}}可以访问父网站但不继承其CSS或JS范围。通过这种方式,我可以安全地加载我的应用程序,Angular及其所有依赖项,而不必担心某些内容可能会破坏底层网站/应用程序。

答案 4 :(得分:0)

另一种方法是替换 polyfills.ts 中的以下语句:

import 'zone.js/dist/zone';  // Included with Angular CLI.

使用:

declare var require: any
if (!window['Zone']) {
  require('zone.js/dist/zone'); // Included with Angular CLI.
}

注意:您可以在所有import语句的顶部添加require的声明。这样,在整个文件中都可以使用它。