Angular 6能否在同一页面使用两个Angular Element组件?
你遇到的Uncaught Error: Zone already loaded.错误,本质是因为每个打包后的Angular Element JS文件都包含了全局单例的Zone.js和Angular核心运行时,重复引入就会触发这个冲突。下面是几个可行的解决方案:
方案1:拆分共享依赖,只加载一次公共资源
核心思路是把多个组件共用的依赖(runtime、polyfills、Angular核心库)单独抽成一个公共JS文件,页面只引入一次,再分别引入每个组件的业务代码文件。
步骤1:修改构建脚本,拆分公共与组件代码
原来的脚本是把所有文件合并成一个JS,现在拆分两步:
- 第一步:合并公共依赖文件
- 第二步:为每个组件单独合并其业务相关文件
示例脚本:
const fs = require('fs-extra'); const concat = require('concat'); // 1. 生成公共依赖文件 const commonFiles = [ './dist/elementsApp/runtime.js', './dist/elementsApp/polyfills.js' ]; // 2. 组件1的业务文件(假设你有两个组件:componentA和componentB) const componentAFiles = [ './dist/elementsApp/scripts.js', // 如果多个组件共用scripts也可以放到公共里 './dist/elementsApp/main-componentA.js' // 注意:需要为每个组件单独打包main文件 ]; const componentBFiles = [ './dist/elementsApp/scripts.js', './dist/elementsApp/main-componentB.js' ]; fs.ensureDir('elements') .then(() => { // 生成公共JS return concat(commonFiles, 'elements/common.js'); }) .then(() => { // 生成组件A的JS return concat(componentAFiles, 'elements/componentA.js'); }) .then(() => { // 生成组件B的JS return concat(componentBFiles, 'elements/componentB.js'); }) .catch(err => { console.error(err); });
步骤2:调整Angular项目配置,支持多组件单独打包
要为每个组件生成独立的main文件,你需要在angular.json中添加多个build配置,每个配置对应一个组件的入口文件:
"projects": { "elementsApp": { "architect": { "build": { // 默认配置... }, "build-componentA": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/elementsApp", "main": "src/app/componentA/main-componentA.ts", // 组件A的入口 "tsConfig": "src/tsconfig.app.json", // 其他配置参考默认build,确保不重复打包公共依赖 "vendorChunk": false, "outputHashing": "none" } }, "build-componentB": { // 组件B的类似配置... } } } }
步骤3:页面引入顺序
在非Angular页面中,先引入公共JS,再引入各个组件的JS:
<!-- 公共依赖只加载一次 --> <script src="elements/common.js"></script> <!-- 组件A --> <script src="elements/componentA.js"></script> <!-- 组件B --> <script src="elements/componentB.js"></script>
方案2:使用Angular Elements的自定义打包工具(可选)
如果你的项目已经使用Angular 6+,可以借助@angular/elements的官方推荐方式,结合ngx-build-plus插件来修改webpack配置,自动拆分共享依赖。安装插件后,在构建时指定--single-bundle false,让插件把公共依赖和组件代码分开。
示例命令:
ng add ngx-build-plus ng build --prod --single-bundle false
这样会生成多个文件,其中包含公共的vendor.js和每个组件的单独bundle,页面只需先引入vendor.js,再引入组件bundle即可。
为什么自执行函数没用?
Zone.js是挂载在全局window对象上的单例,即使你用自执行函数包裹,只要多次引入包含Zone.js的文件,就会重复初始化全局的Zone实例,所以无法通过作用域隔离解决这个问题,必须从依赖拆分入手。
内容的提问来源于stack exchange,提问作者hamobi

