Реализация архитектуры плагинов / системы плагинов / плагин-фреймворка в Angular 2, 4, 5, 6

Обновление от 24.05.2008: У нас сейчас +3 версии Angular из моего исходного поста, и у нас до сих пор нет окончательного работоспособного решения. Ларс Мейдам (@LarsMeijdam) предложил интересный подход, который, безусловно, стоит посмотреть. (Из-за проприетарных проблем ему пришлось временно удалить репозиторий GitHub, в котором он первоначально разместил свой образец. Однако вы можете отправить ему сообщение напрямую, если вам нужна копия. Для получения дополнительной информации см. Комментарии ниже.)

Последние архитектурные изменения в Angular 6 действительно приближают нас к решению. Кроме того, Angular Elements ( https://angular.io/guide/elements) предоставляет некоторые функциональные возможности компонента - хотя и не совсем то, что я первоначально описал в этом посте.

Если кто-то из удивительной команды Angular случайно сталкивался с этим, обратите внимание, что, похоже, есть много других людей, которые также очень заинтересованы в этой функциональности. Возможно, стоит подумать об отставании.


Я хотел бы реализовать подключаемый (плагин) фреймворк в Angular 2, Angular 4, Angular 5, или же Angular 6 приложение.

(Мой конкретный вариант использования для разработки этой подключаемой платформы - это то, что мне нужно разработать миниатюрную систему управления контентом. Angular 2/4/5/6 почти идеально подходит для большинства потребностей этой системы.)

Под подключаемой структурой (или архитектурой подключаемых модулей) я имею в виду систему, которая позволяет сторонним разработчикам создавать или расширять функциональность основного приложения посредством использования подключаемых компонентов без прямого доступа к исходному коду основного приложения или знания его. или внутренние работы.

(Эта формулировка о том, что "не имея прямого доступа к исходному коду или внутренним принципам работы или знания этих программ", является основной целью.)

Примеры подключаемых платформ включают в себя общие системы управления контентом, такие как WordPress или же Drupal,

Идеальной ситуацией (как и в Drupal) было бы просто иметь возможность поместить эти подключаемые компоненты (или подключаемые модули) в папку, заставить приложение автоматически обнаруживать или обнаруживать их, и заставить их просто волшебным образом "работать". Если бы это происходило каким-то образом с возможностью "горячей" замены, то есть во время работы приложения было бы оптимальным.

В настоящее время я пытаюсь определить ответы (с вашей помощью) на следующие пять вопросов.

  1. Практичность: это плагин рамки для Angular 2/4/5/6 приложение даже практично? (До сих пор я не нашел практического способа создать действительно подключаемый фреймворк с Angular2/4/5/6.)
  2. Ожидаемые вызовы: с какими трудностями можно столкнуться при реализации структуры плагинов для Angular 2/4/5/6 приложение?
  3. Стратегии реализации: Какие конкретные методы или стратегии могут быть использованы для реализации структуры плагинов для Angular 2/4/5/6 приложение?
  4. Лучшие практики: Каковы лучшие практики для внедрения системы плагинов для Angular 2/4/5/6 приложение?
  5. Альтернативные технологии: если плагин фреймворк не практичен в Angular 2/4/5/6 применение, какие относительно эквивалентные технологии (например, React) может подойти для современного высокореактивного веб-приложения?

В общем, использование Angular 2/4/5/6 очень желательно, потому что:

  • это естественно очень быстро - чертовски так.
  • он потребляет очень небольшую пропускную способность (после начальной загрузки)
  • он имеет относительно небольшую площадь (после AOT а также tree shaking)- и этот след продолжает сокращаться
  • это очень функциональный, и команда и сообщество Angular продолжают быстрый рост своей экосистемы
  • он хорошо сочетается со многими лучшими и новейшими веб-технологиями, такими как TypeScript а также Observables
  • Angular 5 теперь поддерживает сервисных работников ( https://medium.com/@webmaxru/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-1-theory-37d7d7647cc7)
  • при поддержке Googleэто, вероятно, будет поддерживаться и улучшаться в будущем

Я бы очень хотел использовать Angular 2/4/5/6 для моего текущего проекта. Если я могу использовать Angular 2/4/5/6Я также буду использовать Angular-CLI и, вероятно, Angular Universal (для рендеринга на стороне сервера.)

Вот мои мысли, что касается вопросов выше. Пожалуйста, просмотрите и предоставьте свой отзыв и просвещение.

  • Angular 2/4/5/6 приложения используют пакеты - но это не обязательно то же самое, что разрешать плагины в приложении. Плагин в других системах (например, Drupal) можно добавить, просто поместив папку плагина в общий каталог модулей, где она автоматически "подхватывается" системой. В Angular 2/4/5/6пакет (как плагин может быть) обычно устанавливается через npmдобавлен в package.json, а затем вручную импортировать в приложение - как в app.module, Это намного сложнее, чем Drupal метод удаления папки и автоматического определения системой пакета. Чем сложнее установить плагин, тем менее вероятно, что люди будут его использовать. Было бы намного лучше, если бы был способ Angular 2/4/5/6 автоматически обнаруживать и устанавливать плагины. Мне очень интересно найти метод, который позволяет не разработчикам устанавливать Angular 2/4/5/6 приложение и установить любые выбранные плагины без необходимости понимать всю архитектуру приложения.

  • Как правило, одним из преимуществ предоставления подключаемой архитектуры является то, что сторонним разработчикам очень легко расширить функциональные возможности системы. Очевидно, что эти разработчики не будут знакомы со всеми тонкостями кода для приложения, к которому они подключаются. После разработки плагинов другие, даже менее технические пользователи могут просто установить приложение и любые выбранные плагины. Тем не мение, Angular 2/4/5/6 является относительно сложным и имеет очень длительную кривую обучения. Для дальнейшего усложнения вещей, большинство производства Angular 2/4/5/6 приложения также используют Angular-CLI, Angular Universal, а также WebPack, Кто-то, кто внедряет плагин, вероятно, должен был бы иметь хотя бы некоторые базовые знания о том, как все они сочетаются друг с другом - вместе с сильными рабочими TypeScript и разумное знакомство с NodeJS, Являются ли требования к знаниям настолько экстремальными, что ни одна третья сторона никогда не захочет разработать плагин?

  • Большинство плагинов, скорее всего, будут иметь некоторый компонент на стороне сервера (например, для хранения / извлечения данных, связанных с плагином), а также некоторые выходные данные на стороне клиента. Angular 2/4/5 в частности (и строго) отговаривает разработчиков внедрять свои собственные шаблоны во время выполнения - так как это создает серьезную угрозу безопасности. Чтобы обрабатывать многие типы выходных данных, которые может приспосабливаться плагином (например, отображение графика), представляется, что, вероятно, необходимо разрешить пользователям создавать контент, который вводится в поток ответов, в одной форме другой. Интересно, как можно было бы удовлетворить эту потребность без изобразительного измельчения Angular 2/4/5/6Механизмы безопасности.

  • Большая часть продукции Angular 2/4/5/6 приложения предварительно скомпилированы с использованием Ahead of Time (AOT) сборник. (Вероятно, все должно быть.) Я не уверен, как плагины могут быть добавлены (или интегрированы) в предварительно скомпилированные приложения. Лучший сценарий - компиляция плагинов отдельно от основного приложения. Однако я не уверен, как заставить это работать. Резервным вариантом может быть повторная компиляция всего приложения с любыми включенными плагинами, но это немного усложняет задачу для администратора, который просто хочет установить приложение (на своем собственном сервере) вместе с любыми выбранными плагинами.

  • В Angular 2/4/5/6 Приложение, особенно предварительно скомпилированное, один фрагмент ошибочного или конфликтующего кода может сломать все приложение. Angular 2/4/5/6 приложения не всегда легче всего отлаживать. Применение плагинов плохого поведения может привести к очень неприятным событиям. В настоящее время я не знаю механизма изящной обработки плагинов плохого поведения.

10 ответов

Ith Github demo angular-plugin-архитектура

Возможно, Ivy может что-то изменить, но пока я использую решение, которое использует Angular CLI Custom Builder и отвечает следующим требованиям:

  • АОТ
  • избегайте дублирования кода (такие пакеты, как @angular/core{common,forms,router},rxjs,tslib)
  • использовать разделяемую библиотеку во всех плагинах, но НЕ ПЕРЕВОЗИТЬ сгенерированные фабрики из этой разделяемой библиотеки в каждом плагине, а использовать повторно библиотечный код и фабрики
  • тот же уровень оптимизации, который дает нам Angular CLI
  • для импорта внешних модулей нам нужно знать только одно: путь к пакетному файлу
  • наш код должен распознавать модуль и размещать плагин на странице
  • поддержка рендеринга на стороне сервера
  • загружать модуль только при необходимости

Использование простое как:

ng build --project plugins --prod --modulePath=./plugin1/plugin1.module#Plugin1Module 
         --pluginName=plugin1 --sharedLibs=shared --outputPath=./src/assets/plugins

Подробнее об этом в моей статье:

Я создал хранилище на GitHub с решением, которое может помочь. Он использует Angular 6 библиотек и 1 базовое приложение, которые лениво загружают UMD-библиотеки; https://github.com/lmeijdam/angular-umd-dynamic-example

Если у вас есть какие-либо предложения, пожалуйста, не стесняйтесь добавлять!

Я только что опубликовал новую главу для своей книги " Разработка с помощью Angular", которая посвящена теме плагинов в Angular 2+ и должна представлять большой интерес для людей, которые пытаются создавать внешние плагины.

Ключевые моменты:

  • Плагины
  • Создание компонентов на основе имен строк
  • Загрузка конфигурации из внешних источников
  • Динамически меняющиеся маршруты приложений
  • Внешние плагины
  • Создание библиотек плагинов
  • Загрузка плагинов в приложение
  • Динамические маршруты с содержимым плагина

Книга бесплатна для скачивания и имеет модель "плати сколько хочешь". Не стесняйтесь взять копию и надеюсь, что это поможет.

Пример приложения с работающей системой плагинов (спасибо Gijs за создание репозитория Github!) На основе https://github.com/PacktPublishing/Mastering-Angular-2-Components/tree/master/angular-2-components-chapter-10 на электронную книгу Мастеринг Angular 2 Компоненты

  • архитектура плагинов для расширения основных компонентов приложения
  • файловая система плагинов (для простого добавления каталогов / файлов плагинов без редактирования каких-либо основных конфигурационных файлов или необходимости перекомпилировать ваше приложение!)
  • загружать и динамически использовать плагины
  • построение элементарного менеджера плагинов для активации / деактивации плагинов на лету

Ура, Никлас

То, что вы ищете, это ленивая загрузка модуля. Вот пример этого: http://plnkr.co/edit/FDaiDvklexT68BTaNqvE?p=preview

import {Component} from '@angular/core';
import {Router} from '@angular/router';

@Component({
  selector: 'my-app',
  template: `
    <a [routerLink]="['/']">Home</a> | 
    <a [routerLink]="['/app/home']">App Home</a> |
    <a [routerLink]="['/app/lazy']">App Lazy</a>

    <hr>
    <button (click)="addRoutes()">Add Routes</button>

    <hr>
    <router-outlet></router-outlet>
  `
})
export class App {
  loaded: boolean = false;
  constructor(private router: Router) {}

  addRoutes() {
    let routerConfig = this.router.config;

    if (!this.loaded) {
      routerConfig[1].children.push({
        path: `lazy`,
        loadChildren: 'app/lazy.module#LazyModule'
      });

      this.router.resetConfig(routerConfig);
      this.loaded = true;
    }
  }
}

Лучший... Том

Я также искал систему плагинов в angular 2/4 для разработки среды RAD для корпоративного приложения на работе. После некоторых исследований я решил реализовать коллекцию псевдоангулярных компонентов, хранящихся в базе данных (но может быть в файловой системе).

Компоненты, хранящиеся в базе данных базы данных, основаны на ng-dynamic, и реализация основного компонента похожа на это:

declare var ctx: any;

@Component({
    selector: 'my-template',
    template: `
<div>
    <div *dynamicComponent="template; context: { ctx: ctx };"></div>
</div>
  `,
    providers: [EmitterService],

})

export class MyTemplateComponent implements OnMount, AfterViewInit, OnChanges {


    // name
    private _name: string;
    get name(): string {
        return this._name;
    }
    @Input()
    set name(name: string) {
        this._name = name;        
        this.initTemplate();
    }

    template: string;
    ctx: any = null;

    private initTemplate() {

        this.templateSvc.getTemplate(this.name).subscribe(res => {
            // Load external JS with ctx implementation
            let promise1 = injectScript(res.pathJs);
            // Load external CCS
            let promise2 = injectScript(res.pathCss);

            Promise.all([promise1, promise2]).then(() => {

                // assign external component code
                this.ctx = ctx; //

                // sets the template
                this.template = res.template;

                this.injectServices();

                if (this.ctx && this.ctx.onInit) {
                    this.ctx.onInit();
                }

            });

        });

    }

Внешний код JavaScript похож на угловые компоненты:

var ctx = {

// injected    
_httpService: {},
_emitterService: null,

// properies
model: {
    "title": "hello world!",
},


// events
onInit() {
    console.log('onInit');
},

onDestroy() {
    console.log('onDestroy');
},

onChanges(changes) {
    console.log('changes', changes);
},

customFunction1() {
    console.log('customFunction1');
},

childTemplateName: string = 'other-component'; 

};

А шаблоны компонентов похожи на угловые шаблоны:

<a (click)="customFunction1()">{{ ctx.model.title }}</a>
<input [(ngModel)]="ctx.model.title" type="text" />

И тоже может быть вложенным:

<a (click)="customFunction1()">{{ ctx.model.title }}</a>
<my-template [name]="childTemplateName"></my-template>

Хотя это не идеально, разработчики пользовательских компонентов имеют схожую структуру, чем в angular2/4.

Сейчас я нахожусь в том же квесте, что и вы, пытаясь сделать Pluggable/Themable версию Angular, и это не тривиальная проблема.

На самом деле я нашел довольно хорошие решения, читая книгу Genius Дениса Вуйки " Разработка с использованием Angular", он на самом деле в книге объясняет довольно хорошее решение, рассказывает о внешних плагинах на странице 356 книги и использует Rollup.js для достижения цели. Решение, он затем обрабатывает для динамической загрузки внешних плагинов, которые были ранее построены за пределами вашего приложения.

Есть также две другие библиотеки / проекты, которые помогут вам достичь этого результата. Ng-packagr и расширения Nx для Agnular (от Nrwl), которые мы связываем для реализации последнего, и я бы сказал, что это не так гладко, как мы ожидали, angular был простым не для этого, поэтому нам нужно обойти некоторые основные аспекты Angular, и NX ppls являются одними из лучших в этом.

Мы находимся только в начале нашего проекта с открытым исходным кодом, мы используем Django+Mongo+Angular, (Мы вызываем WebDjangular, и один из наших возможных подходов к этому ответу заключается в том, что Django придется написать несколько файлов конфигурации JSON и собрать приложение каждый раз, когда новый плагин или тема установлена ​​и активирована.

Что мы уже сделали, так это то, что из базы данных мы можем использовать теги для компонентов, как на плагине, и компонент будет напечатан на экране! Опять же, проект находится на очень ранних стадиях, мы немного основываем нашу архитектуру на Wordpress, и у нас есть еще много тестов, чтобы выполнить нашу мечту:D

Надеюсь, что Книга поможет вам, и, пользуясь Rollup.js, я знаю, что вы сможете решить эту нетривиальную проблему.

Это можно сделать "вручную". Поскольку webpack ничего не знает о внешних (подключаемых модулях) модулях, он не может включать их в комплект (ы). Итак, я посмотрел на код, сгенерированный webpack, и нашел этот кусок кода в main.bundle.js:

var map = {
"./dashboard/dashboard.module": ["../../../../../src/app/dashboard/dashboard.module.ts","dashboard.module"]}; 

Давайте посмотрим, что содержит этот массив:

  1. "./dashboard/dashboard.module" - это URL-адрес маршрутизации модуля, для которого мы хотим выполнить ленивую загрузку, например:{path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule'}
  2. "../../../../../src/app/dashboard/dashboard.module.ts" - это точка входа (конструктор) берет из
  3. "dashboard.module" - фактическое имя файла без chunk.js(например: dashboard.module.chunk.js)

Таким образом, теоретически, если вы добавите запись в свойство карты, сконфигурируете свою маршрутизацию и будете следовать шаблону, вы можете иметь систему плагинов. Теперь задача состоит в том, как добавить или удалить записи из этого свойства карты. Очевидно, что это не может быть сделано из углового кода, это должно быть сделано для внешнего инструмента.

Немного не по теме, но библиотеки компонентов пользовательского интерфейса могут представлять интерес для некоторых читателей, которые ищут плагины:
https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e

NativeScript имеет встроенные плагины пользовательского интерфейса:
https://docs.nativescript.org/plugins/building-plugins
Эти плагины нуждаются в Angular Wrapper:
https://docs.nativescript.org/plugins/angular-plugin

Я попытался реализовать архитектуру плагинов, используя ABP, Angular и ASP.NET Core: https://github.com/chanjunweimy/abp_plugin_with_ui

По сути, я разработал угловые плагины, используя различные угловые приложения, а затем динамически складываю их вместе.

Больше информации о том, как я этого добиваюсь:

У меня есть 2 приложения angular-cli, 1 - основное приложение angular cli, а другое - плагин angular cli. Проблема, с которой мы сталкиваемся в подходе к архитектуре плагинов Angular, заключается в том, как мы их интегрируем.

Прямо сейчас я запустил ng-build для обоих приложений и поместил их в папку "wwwroot", которая затем размещалась на сервере ASP.NET core 2.0. Более простым репозиторием, демонстрирующим эту идею, является Angular Multiple App: https://github.com/chanjunweimy/angular-multiple-app

abp_plugin_with_ui - это репозиторий, который работает над разработкой плагина, который содержит как бэкэнд, так и Angular cli. Для бэкэнда я использовал инфраструктуру aspnetboilerplate, интерфейс которой разработан с использованием приложения angular-cli.

Чтобы интегрировать основное приложение с приложением плагина, мы должны запустить "ng-build" в обоих приложениях (обратите внимание, что мы также должны перейти на href приложения плагина), затем мы перемещаем встроенное содержимое плагина приложение angular cli, к основной папке приложения wwwroot. После достижения всего этого мы можем запустить "запуск по сети" для обслуживания веб-приложения ASP.NET Core 2.0 для размещения статических файлов, созданных "ng build".

Надеюсь, это поможет. Любые комментарии приветствуются! ^^

Я сделал хак для загрузки и компиляции других модулей во время начальной загрузки, но я не решил проблему циклических зависимостей

 const moduleFile: any = require(`./${app}/${app}.module`),
                    module = moduleFile[Object.keys(moduleFile)[0]];

 route.children.push({
     path: app,
     loadChildren: (): Promise<any> => module
 });
 promises.push(this.compiler.compileModuleAndAllComponentsAsync(module));

затем в AppModule добавьте это:

{
        provide: APP_INITIALIZER,
        useFactory: AppsLoaderFactory,
        deps: [AppsLoader],
        multi: true
},

Я нашел хорошую статью от Пола Ионеску о том, как создать расширяемое приложение для плагинов в angular.

https://itnext.io/how-to-build-a-plugin-extensible-application-architecture-in-angular5-736890278f3f

Он также ссылается на пример приложения на github: https://github.com/ionepaul/angular-plugin-architecture

Другие вопросы по тегам