如何仅在需要时加载 Zone.js
How to only load Zone.js when it is needed
我正在创建一个可以动态注入 运行ning 网站的 Angular 2 应用程序。这个应用程序的重点是能够直观地修改网站内容。
当有问题的网站也没有 运行 使用 Angular 2 时,一切都按预期工作。特别是,当原始网站使用 Angular 2 和 Zone.js ,我的应用程序也尝试加载 Zone.js,但它在 error: Zone already loaded
时崩溃。我正在使用 Webpack 作为构建系统,我试图通过将构建分成 3 个部分来解决问题:manifest
、polyfill
和 app
。 Manifest 只包含 Webpack manifest,polyfill 包含 core-js
和 zone.js
,其余在 app
中。还有第四个块称为 index
。这是放置在网站上的那个,它首先检查是否定义了 window.Zone
。如果是,则仅添加 manifest
和 app
。否则也polyfill
.
问题是当 polyfill
块未加载时,该块中的所有模块都从 Webpack 中丢失。因此,当代码从需要 core-js
或 Zone.js
的 app
块到达某个 import
时(这就是我认为发生的情况),我收到此错误:
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
作为依赖项导入,而是使用已在 window 上注册的依赖项?我希望能够有条件地加载 core-js
和 zone.js
只有当它还没有加载到网站上时。
更新
我修改了我的构建 (Webpack) 以仅使用一个包。在那个包中,我尝试像这样使用 Webpack 的 dynamic imports:
// 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
is an ambient dependency 所以当我像这样使用它时会收到关于缺少定义文件的错误。我该如何解决?
如果您不想将 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.
}
我遇到了同样的情况,并将我的构建系统从 commonjs / webpack 更改为 systemjs 构建和 运行 system
在 systemjs 中,您可以加载单个文件并配置获取该文件的位置。
在多种技术、多个团队在单页面架构中将代码推送到同一页面的环境中,systemjs 给了我很好的体验。
我必须将已接受的答案和它的一些评论放在一起才能让它在 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));
}
这是几个月后我针对这个问题的解决方案和故事。基本上,很长一段时间我在我的项目中使用 并且它工作得很好。它可以检测是否需要Zone,只有在还没有加载时才加载。
但是,当底层应用程序加载缓慢并且我的应用程序将 bootstrap 加载到底层应用程序之前时,问题就出现了。在这种情况下,我的应用程序认为 Zone 没有加载并加载它,只会在它不断加载时与底层应用程序发生冲突。
还有其他问题,与本题无关。例如,我发现 Angular 与 Prototype 冲突,很多网站都使用 Prototype!
总而言之,我必须完全改变我的方法。我将我的应用程序移动到可以访问父网站但不继承其 CSS 或 JS 范围的 iframe
。这样我就可以安全地加载我的应用程序 Angular 及其所有依赖项,而不必担心某些东西可能会破坏底层网站/应用程序。
另一种方法是替换 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
的声明。这样它将在整个文件中可用。
我正在创建一个可以动态注入 运行ning 网站的 Angular 2 应用程序。这个应用程序的重点是能够直观地修改网站内容。
当有问题的网站也没有 运行 使用 Angular 2 时,一切都按预期工作。特别是,当原始网站使用 Angular 2 和 Zone.js ,我的应用程序也尝试加载 Zone.js,但它在 error: Zone already loaded
时崩溃。我正在使用 Webpack 作为构建系统,我试图通过将构建分成 3 个部分来解决问题:manifest
、polyfill
和 app
。 Manifest 只包含 Webpack manifest,polyfill 包含 core-js
和 zone.js
,其余在 app
中。还有第四个块称为 index
。这是放置在网站上的那个,它首先检查是否定义了 window.Zone
。如果是,则仅添加 manifest
和 app
。否则也polyfill
.
问题是当 polyfill
块未加载时,该块中的所有模块都从 Webpack 中丢失。因此,当代码从需要 core-js
或 Zone.js
的 app
块到达某个 import
时(这就是我认为发生的情况),我收到此错误:
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
作为依赖项导入,而是使用已在 window 上注册的依赖项?我希望能够有条件地加载 core-js
和 zone.js
只有当它还没有加载到网站上时。
更新
我修改了我的构建 (Webpack) 以仅使用一个包。在那个包中,我尝试像这样使用 Webpack 的 dynamic imports:
// 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
is an ambient dependency 所以当我像这样使用它时会收到关于缺少定义文件的错误。我该如何解决?
如果您不想将 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.
}
我遇到了同样的情况,并将我的构建系统从 commonjs / webpack 更改为 systemjs 构建和 运行 system
在 systemjs 中,您可以加载单个文件并配置获取该文件的位置。
在多种技术、多个团队在单页面架构中将代码推送到同一页面的环境中,systemjs 给了我很好的体验。
我必须将已接受的答案和它的一些评论放在一起才能让它在 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));
}
这是几个月后我针对这个问题的解决方案和故事。基本上,很长一段时间我在我的项目中使用
但是,当底层应用程序加载缓慢并且我的应用程序将 bootstrap 加载到底层应用程序之前时,问题就出现了。在这种情况下,我的应用程序认为 Zone 没有加载并加载它,只会在它不断加载时与底层应用程序发生冲突。
还有其他问题,与本题无关。例如,我发现 Angular 与 Prototype 冲突,很多网站都使用 Prototype!
总而言之,我必须完全改变我的方法。我将我的应用程序移动到可以访问父网站但不继承其 CSS 或 JS 范围的 iframe
。这样我就可以安全地加载我的应用程序 Angular 及其所有依赖项,而不必担心某些东西可能会破坏底层网站/应用程序。
另一种方法是替换 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
的声明。这样它将在整个文件中可用。