首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >角应用程序中的角元素在优化时会产生无限重加载问题。

角应用程序中的角元素在优化时会产生无限重加载问题。
EN

Stack Overflow用户
提问于 2019-03-28 12:54:52
回答 1查看 2.7K关注 0票数 10

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已经加载的问题,这是真的:

代码语言:javascript
复制
// import 'zone.js/dist/zone';  // Included with Angular CLI.

tsconfig.json中,我将"target": "es5"改为"target": "es2015",以解决角如何创建元素的问题。不太确定这是如何工作的,但堆栈溢出建议它和它的诀窍。

我将app.module.ts编辑成这样的内容,下面是本教程中的一些想法,以及我失去链接的其他一些内容:

代码语言:javascript
复制
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中,我这样做是为了在模板中显示一些属性:

代码语言:javascript
复制
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看起来是这样的:

代码语言:javascript
复制
Some content:
<pre>{{content | json}}</pre>

package.json文件中,我有三个脚本来构建所有内容:

代码语言:javascript
复制
{
    "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与不同的目标名称相同):

代码语言:javascript
复制
'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更改为如下所示:

代码语言:javascript
复制
<div id="embedded-container" class="container border border-primary rounded mt-5 p-3" #container>
    <pre>Loading...</pre>
</div>

最后,embedded.component.ts最后显示了实际的加载机制:

代码语言:javascript
复制
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来修复它的,那么,我就试着调试这个问题(事实证明这是不可能的),然后突然加载了它。

我看了一下时间,已经晚了两个小时才能进行生产部署,所以我添加了一个丑陋的机制,根据环境加载不同的构建。它是有效的,无论在开发和生产,但我不感到自豪。

抱歉如果我的英语不好..。不,我的英语很差,对不起,^__^

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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部分。

代码语言:javascript
复制
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的同一个目录中。该文件只包含以下内容:

代码语言:javascript
复制
module.exports = {
    output: {
        jsonpFunction: '<webcomponent-prefix>-webpackJsonp'
    }
}

jsonpFunction的值默认为webpackJsonp,如果您将其更改为除它之外的任何内容,它就会正常工作。我决定维护函数名,但是为我的web组件应用程序添加一个前缀。理论上,只要每个DOM都有唯一的jsonpFunction,就可以在相同的DOM上下文中运行N个webpack配置

再次构建您的web组件,瞧,它是有效的!

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55398196

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档