7

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

当有问题的网站也没有使用 Angular 2 运行时,一切都按预期工作。特别是,当原始网站使用 Angular 2 和 Zone.js 时,我的应用程序也尝试加载 Zone.js,但它在error: Zone already loaded. 我使用 Webpack 作为构建系统,我试图通过将构建分成 3 个部分来解决这个问题:manifestpolyfillapp. Manifest 只包含 Webpack 清单,polyfill 包含core-jsand zone.js,其余的在app. 还有第四个块叫做index. 这是放置在网站中的内容,它首先检查是否window.Zone已定义。如果是,则仅添加manifestapp。否则也polyfill

问题是当polyfill块没有加载时,该块中的所有模块都从 Webpack 中丢失。因此,当代码从需要或(我认为正在发生的事情)import的块中到达一些时,我会收到此错误:appcore-jsZone.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)

清单文件中的某处:

/******/   // Execute the module function
/******/   modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

问题

如何将 Webpack 或 Angular 配置为不zone.js作为依赖项导入,而是使用已在窗口上注册的依赖项?我希望能够有条件地加载core-js,并且zone.js仅当它尚未加载到网站上时。

更新

我修改了我的构建(Webpack),只使用一个包。在那个包中,我尝试使用 Webpack 的动态导入,如下所示:

// import reflect metadata shim
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';


// Angular and application imports
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ViewEncapsulation } from '@angular/core';


// load the editor in the next frame
requestAnimationFrame(() => {

    if (window.Zone) {
        bootstrap();
    }
    else {
        import('zone.js/dist/zone') // <-- PROBLEM HERE
            .then(bootstrap);
    }

});


// bootstraps the editor
function bootstrap() {

    // add the root component to the DOM
    const root = document.createElement('efe-root');
    document.body.appendChild(root);

    // bootstrap the Angular app
    platformBrowserDynamic()
        .bootstrapModule(AppModule, {
            defaultEncapsulation: ViewEncapsulation.Native
        })
        .then(() => console.debug('Exponea Free Editor has bootstrapped'));

}

我正在使用 Typescript,这需要定义文件。问题是这Zone.js是一个环境依赖,所以当我像这样使用它时,我收到一个关于缺少定义文件的错误。我该如何解决这个问题?

4

5 回答 5

12

如果您不想加载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.
}
于 2017-08-18T12:43:40.067 回答
6

我必须把接受的答案和它的一些评论放在一起,才能让它在 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));
}
于 2018-05-31T17:03:57.383 回答
2

另一种方法是替换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.
}

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

于 2018-10-08T09:19:13.433 回答
0

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

在 systemjs 中,您可以加载单个文件并配置它将在哪里获取该文件。

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

于 2017-08-18T15:35:26.013 回答
0

几个月后,这是我的解决方案和这个问题的故事。基本上,很长一段时间以来,我在我的项目中使用了公认答案的解决方案,并且效果很好。它可以检测是否需要 Zone 并仅在尚未加载时才加载它。

但是,当底层应用程序加载缓慢并且我的应用程序会在底层应用程序之前引导时,就会出现问题。在这种情况下,我的应用程序会认为 Zone 没有加载并加载它,只是在它不断加载时与底层应用程序发生冲突。

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

总而言之,我必须完全改变我的方法。我将我的应用程序移至可以iframe访问父网站但不继承其 CSS 或 JS 范围的应用程序。这样我就可以安全地加载我的应用程序、Angular 及其所有依赖项,而不必担心某些东西可能会破坏底层网站/应用程序。

于 2018-06-01T06:53:43.827 回答