TL;博士:我正在尝试使用角元素作为角度应用程序的插件。如果我用--prod构建元素,它可以在我的应用程序(开发设置)上使用ng serve,但是当我在应用程序上使用ng serve --prod或者在我的应用程序的ng build --prod (产品安装)之后使用它时,它会进入无限重加载。
不过,如果我构建了添加--optimization=false的元素,则可以使用我的生产性应用程序,但在我的开发设置中不能这样做。
问题是,我原以为用--prod构建一个角元素对于这两种情况都是可以的。
问题:有解决这个问题的方法吗?
现在,长时间阅读。
在工作中,我们试图使用可配置插件在我们的角度站点,服务器是一个告诉哪个插件是活动的或不活动的。
我们试图动态加载角度模块,但这是一个完全不同的头痛,我们搁置了另一天。
所以,接下来我们要尝试的是角度元素,它有点工作,除非我们用它应该的方式来构建所有的东西。
首先,我开始学习本教程https://scotch.io/tutorials/build-a-reusable-component-with-angular-elements/amp,并忽略了有关okta的所有内容,因为我的功能是不同的。
创建:
我使用下一个命令创建了我的核心应用程序,这个应用程序将托管插件:
ng new core --routing --skip-git --style scss --skip-tests --minimal然后,我使用以下命令创建了一个插件/角元素:
ng new plugin --skip-git --style scss --skip-tests --minimal插件:
在创建了我的插件并在polyfills.ts中评论了这一行之后,我在这个站点的某个地方读到,它解决了NgZone已经加载的问题,这是真的:
// import 'zone.js/dist/zone'; // Included with Angular CLI.在tsconfig.json中,我将"target": "es5"改为"target": "es2015",以解决角如何创建元素的问题。不太确定这是如何工作的,但堆栈溢出建议它和它的诀窍。
我将app.module.ts编辑成这样的内容,下面是本教程中的一些想法,以及我失去链接的其他一些内容:
import { BrowserModule } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, Injector, NgModule } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
],
providers: [
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA,
],
entryComponents: [
AppComponent,
],
})
export class AppModule {
constructor(private injector: Injector) {
const elem = createCustomElement(AppComponent, { injector: this.injector });
customElements.define('my-plugin', elem);
}
ngDoBootstrap() {
}
}注意:我添加CUSTOM_ELEMENTS_SCHEMA是因为我在某个地方找到了它,但它没有解决这个问题(而且,我也不知道它能做什么)。
在app.component.ts中,我这样做是为了在模板中显示一些属性:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
public content: any = {
a: 10,
b: '20',
}
}app.component.html看起来是这样的:
Some content:
<pre>{{content | json}}</pre>在package.json文件中,我有三个脚本来构建所有内容:
{
"scripts": {
"build": "npm run build:opt && npm run build:noopt",
"build:opt": "ng build --prod --output-hashing none && node build-elements.js",
"build:noopt": "ng build --prod --output-hashing none --optimization=false && node build-elements.noopt.js"
}
}文件build-elements.js看起来如下(build-elements.noopt.js与不同的目标名称相同):
'use strict';
const concat = require('concat');
const fs = require('fs-extra');
const path = require('path');
(async function build() {
const files = [
'./dist/plugin/runtime.js',
'./dist/plugin/polyfills.js',
'./dist/plugin/scripts.js',
'./dist/plugin/main.js',
];
const destinationDir = path.join(__dirname, '../core/src/assets');
await fs.ensureDir(destinationDir);
await concat(files, path.join(destinationDir, 'my-plugin.js'));
})();核心:
对于主机应用程序,我添加了一个名为embedded的组件,默认的路由会到达它。
然后,我使用一些引导类将embedded.component.html更改为如下所示:
<div id="embedded-container" class="container border border-primary rounded mt-5 p-3" #container>
<pre>Loading...</pre>
</div>最后,embedded.component.ts最后显示了实际的加载机制:
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { environment } from '../../environments/environment';
@Component({
selector: 'app-embedded',
templateUrl: './embedded.component.html',
})
export class EmbeddedComponent implements OnInit {
@ViewChild('container') public container: ElementRef;
constructor(protected activatedRoute: ActivatedRoute) {
}
ngOnInit() {
this.activatedRoute.queryParams.subscribe((params: Params) => {
const script = document.createElement('script');
if (params['how-it-should-be'] !== undefined) {
script.src = environment.production ? '/assets/my-plugin.js' : '/assets/my-plugin-no-optimization.js';
} else {
script.src = environment.production ? '/assets/my-plugin-no-optimization.js' : '/assets/my-plugin.js';
}
document.body.appendChild(script);
const div = document.createElement('div');
div.innerHTML = '<my-plugin></my-plugin>';
this.container.nativeElement.innerHTML = '';
this.container.nativeElement.appendChild(div);
});
}
}运行:
如果我运行ng serve并浏览到http://localhost:4200,页面将毫无问题地加载,注入插件,将新元素添加到DOM中,并显示来自插件的消息。如果调试应用程序,您将看到它加载了/assets/my-plugin.js,这是为生产而构建的。除了调试之外,这不会是一个问题。
然后,如果我运行ng serve --prod (或者为生产构建它),它也可以正常工作,但是它会加载/assets/my-plugin-no-optimization.js,这是为“调试”而构建的。
这是我最后在我们的实际应用程序中使用的解决方案,但正如您所看到的,我并没有在我的插件中使用优化的代码来进行生产,这不太好…完全没有。
为了证明我的观点,如果我浏览到http://localhost:4200/?how-it-should-be,它将尝试加载ng serve --prod的优化插件和ng serve的调试插件。请注意,这将使您进入无限重装,打开您的浏览器开发工具来查看它。
我们正在使用的最终产品要复杂得多,但这些示例具有根本无法工作的基本逻辑。
我还创建了一个GitHub存储库,您可以在其中看到这些代码,并自己尝试这个问题,或者将其用作您自己的想法的示例。
如果您想知道我是如何发现使用--optimization=false来修复它的,那么,我就试着调试这个问题(事实证明这是不可能的),然后突然加载了它。
我看了一下时间,已经晚了两个小时才能进行生产部署,所以我添加了一个丑陋的机制,根据环境加载不同的构建。它是有效的,无论在开发和生产,但我不感到自豪。
抱歉如果我的英语不好..。不,我的英语很差,对不起,^__^
发布于 2019-06-10 20:39:13
我找到解决办法了!
事实证明,问题在于webpack的多个项目在同一个环境中运行。我对这个问题的理解本质上是,当你与webpack一起构建项目时,他们会在运行时进行webpack引导,并依赖于在全局上下文(webpackJsonp)中定义的某个函数。当多个webpack配置尝试在同一个DOM上下文中进行引导时,它会创建这里定义的症状。(在这里可以找到更详细的解释- https://github.com/angular/angular/issues/23732#issuecomment-388670907)
这个GitHub注释描述了一个解决方案,但没有描述如何到https://github.com/angular/angular/issues/30028#issuecomment-489758172的解决方案。下面我将展示我是如何具体解决这个问题的。
我们可以使用webpack的配置为我们的web组件重命名webpackJsonp,这样两个角度的项目(或者任何用webpack构建的项目)都不会相互干扰。
溶液
首先,我们安装了@角-建设者/自定义webpack软件包,使我们能够修改在角CLI内建webpack配置。
npm install --save-dev @angular-builders/custom-webpack
接下来,我们更新我们的angular.json文件,使其既使用我们的新构建器,又使用名为customWebpackConfig的选项中的新属性值。这包括我们将要创建的一个新文件的路径和一个mergeStrategy。这种合并策略表明,我们希望将配置附加到webpack配置的output部分。
angular.json
...
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js",
"mergeStrategies": { "output": "append"}
},
...最后,我们简单地将extra-webpack.config.js文件添加到包含angular.json的同一个目录中。该文件只包含以下内容:
module.exports = {
output: {
jsonpFunction: '<webcomponent-prefix>-webpackJsonp'
}
}jsonpFunction的值默认为webpackJsonp,如果您将其更改为除它之外的任何内容,它就会正常工作。我决定维护函数名,但是为我的web组件应用程序添加一个前缀。理论上,只要每个DOM都有唯一的jsonpFunction,就可以在相同的DOM上下文中运行N个webpack配置
再次构建您的web组件,瞧,它是有效的!
https://stackoverflow.com/questions/55398196
复制相似问题