إنشاء بنية الخدمات المصغرة باستخدام سبا فردي (ترحيل مشروع قائم)

صورة



هذه هي المقالة الأولى حول هذا الموضوع ، تم التخطيط لإجمالي 3:



  1. * أنشئ تطبيقًا جذريًا من مشروعك الحالي ، وأضف 3 تطبيقات صغيرة إليه (vue ، رد فعل ، زاوية)
  2. التواصل بين التطبيقات الدقيقة
  3. العمل مع git (نشر ، تحديثات)


جدول المحتويات



  1. جزء مشترك
  2. لماذا هو مطلوب
  3. قم بإنشاء حاوية جذر (انظر التعريف أدناه) من المنولث الخاص بك
  4. إنشاء تطبيق VUE الصغير (تطبيق vue)
  5.  إنشاء تطبيق صغير REACT (تطبيق رد فعل)
  6.  إنشاء تطبيق صغير ANGULAR (تطبيق الزاوي)


1. الجزء العام



الهدف من هذه المقالة هو إضافة القدرة على استخدام مشروع مترابط حالي كحاوية جذرية لبنية الخدمات المصغرة.



تم إنشاء المشروع الحالي على الزاوية 9.



بالنسبة لهندسة الخدمات المصغرة ، نستخدم مكتبة المنتجع الصحي الفردي .



تحتاج إلى إضافة 3 مشاريع إلى مشروع الجذر ، فنحن نستخدم تقنيات مختلفة: vue-app ، والتطبيق الزاوي ، والتطبيق التفاعلي (انظر الصفحات 4 ، 5 ، 6)



بالتوازي مع إنشاء هذه المقالة ، أحاول تطبيق هذه البنية في مشروع إنتاج أعمل عليه حاليًا. لذلك ، سأحاول وصف جميع الأخطاء التي لدي في عملية التطوير وحلولها.



تطبيق الجذر (المشار إليه فيما يلي باسم الجذر) - جذر (الحاوية) لتطبيقنا. سنضع (نسجل) جميع خدماتنا المصغرة فيه. إذا كان لديك بالفعل أي مشروع وترغب في تنفيذ هذه البنية فيه ، فسيكون مشروعك الحالي هو التطبيق الجذر ، حيث ستحاول بمرور الوقت التخلص من أجزاء من تطبيقك ، وإنشاء خدمات صغيرة منفصلة وتسجيلها في هذه الحاوية.



سيوفر هذا النهج لإنشاء حاوية جذر فرصة ممتازة للانتقال إلى تقنية أخرى دون الكثير من الألم.



على سبيل المثال ، قررنا الانتقال من الزاوية إلى النظرة تمامًا ، لكن المشروع سمين ، وفي الوقت الحالي يجلب الكثير من المال للعمل.



بدون بنية الخدمات المصغرة ، لم يكن هذا ليظهر في الأذهان ، فقط للأشخاص اليائسين الذين يؤمنون بأحادية القرن وأننا جميعًا صورة ثلاثية الأبعاد.

من أجل التحول إلى تقنية جديدة في الواقع ، من الضروري إعادة كتابة المشروع بأكمله ، وعندها فقط يمكننا أن نرتقي من ظهوره في المعركة.



خيار آخر هو هندسة الخدمات المصغرة. يمكنك إنشاء مشروع جذر من الوحدة المتراصة ، وإضافة مشروع جديد هناك على نفس المسار ، وإعداد التجوال في الجذر ، لقد انتهيت. يمكنك الانخراط في المعركة ، وقطع قطع صغيرة تدريجيًا من جذر المشروع ونقلها إلى مشروع vue الصغير الخاص بك. هذا يترك فقط الملفات الموجودة في حاوية الجذر المطلوبة لاستيراد مشروعك الجديد.



يمكن القيام بذلك هنا والآن ، بدون خسارة ، ولا دم ، والأهم من ذلك أنه حقيقي.

سأستخدم الزاوي كجذر ، حيث تمت كتابة المشروع الحالي فيه.



الواجهة العامة التي سيتم تغليف تطبيق الصفحة الواحدة بها:



bootstrap (mounter ، bus) - تسمى بعد تحميل الخدمة ، ستخبرك بأي عنصر من المنزل تحتاج إلى تحميله ، ومنحها ناقل رسالة تشترك فيه الخدمة المصغرة وستكون قادرة على الاستماع وإرسال الطلبات وأمر



التحميل () - تحميل التطبيق من المنزل



unmount () - تفكيك التطبيق



unload () - تفريغ التطبيق



في الكود ، سأصف مرة أخرى تشغيل كل طريقة محليًا في مكان الاستخدام.



2. لماذا هو مطلوب



لنبدأ عند هذه النقطة بالترتيب بدقة.



هناك نوعان من العمارة:



  1. مونوليث
  2. بنية الخدمات المصغرة


صورة



مع الوحدة المتراصة ، كل شيء بسيط للغاية ومألوف قدر الإمكان لنا جميعًا. تماسك قوي ، كتل ضخمة من التعليمات البرمجية ، مستودع مشترك ، أطنان من الأساليب.



في البداية ، تكون الهندسة المعمارية المتجانسة مريحة وسريعة قدر الإمكان. لا توجد مشاكل وصعوبات في إنشاء أي ملفات تكامل ، أو طبقات بينية ، أو نماذج أحداث ، أو حافلات بيانات ، إلخ.



تظهر المشكلة عندما ينمو مشروعك ، يظهر الكثير من الوظائف الوظيفية المنفصلة والمعقدة لأغراض مختلفة. تبدأ كل هذه الوظائف في الارتباط داخل المشروع ببعض النماذج العامة ، والحالات ، والمرافق ، والواجهات ، والأساليب ، إلخ.



أيضًا ، يصبح عدد الأدلة والملفات في المشروع ضخمًا بمرور الوقت ، وهناك مشاكل في العثور على المشروع وفهمه ككل ، ويتم فقدان "العرض العلوي" ، مما يعطي وضوحًا لما نقوم به ، وأين يكمن ومن يحتاج إليه.



بالإضافة إلى كل هذا ، فإن قانون Eagleson يعمل ، والذي ينص على أن الكود الخاص بك ، الذي لم تنظر إليه لمدة 6 أشهر أو أكثر ، يبدو أنه كتبه شخص آخر.



الأمر الأكثر إيلامًا هو أن كل شيء سينمو بشكل كبير ، ونتيجة لذلك ، ستبدأ العكازات ، والتي يجب إضافتها بسبب تعقيد الحفاظ على الرمز فيما يتعلق بما سبق ، ومع مرور الوقت ، موجات المواعيد النهائية غير المسؤولة.



نتيجة لذلك ، إذا كان لديك مشروع مباشر يتطور باستمرار ، فسوف يصبح مشكلة كبيرة ، والاستياء الأبدي لفريقك ، وعدد كبير من الأشخاص - ساعات لإجراء تغييرات طفيفة على المشروع ، وعتبة دخول منخفضة للموظفين الجدد ووقت طويل لبدء المشروع في المعركة. كل هذا يؤدي إلى الفوضى ، حسنًا ، نحن نحب النظام؟



هل يحدث هذا دائمًا مع متراصة؟



بالطبع لا! كل هذا يتوقف على نوع المشروع الخاص بك ، على المشاكل التي تنشأ أثناء تطوير الفريق. قد لا يكون مشروعك كبيرًا جدًا ، لأداء مهمة عمل واحدة معقدة ، وهذا أمر طبيعي وأعتقد أنه صحيح.



بادئ ذي بدء ، نحتاج إلى الاهتمام بمعايير مشروعنا. 



سأحاول استخلاص النقاط التي يمكنك من خلالها فهم ما إذا كنا نحتاج حقًا إلى بنية خدمات مصغرة:



  • يعمل فريقان أو أكثر على المشروع ، وعدد مطوري الواجهة الأمامية هو 10+ ؛
  • يتكون مشروعك من نموذجين أو أكثر من نماذج الأعمال ، على سبيل المثال ، لديك متجر على الإنترنت يحتوي على كمية هائلة من البضائع ، والفلاتر ، والإشعارات ، ووظيفة توزيع تسليم البريد السريع (نموذجان تجاريان منفصلان وليسا صغيران يتداخلان مع بعضهما البعض) كل هذا يمكن أن يعيش بشكل منفصل ولا يعتمد على بعضه البعض.
  • تنمو مجموعة إمكانيات واجهة المستخدم يوميًا أو أسبوعيًا دون التأثير على بقية النظام.


تستخدم الواجهات الدقيقة من أجل:



  • يمكن تطوير أجزاء منفصلة من الواجهة الأمامية واختبارها ونشرها بشكل مستقل ؛
  • يمكن إضافة أجزاء من الواجهة الأمامية أو إزالتها أو استبدالها دون إعادة تجميع ؛
  •   .
  • , - «», - ( ) -.
  • ,
  • .


single-spa ?



  • (, React, Vue Angular) , .
  • Single-spa , , .
  • .


خدمة Microservice ، حسب فهمي ، هي تطبيق مستقل من صفحة واحدة سيحل مهمة مستخدم واحدة فقط. لا يحتاج هذا التطبيق أيضًا إلى حل مهمة الفريق بأكملها. 



SystemJS هي مكتبة JS مفتوحة المصدر تُستخدم بشكل شائع كمواد تعبئة للمتصفحات.



يعد polyfill جزءًا من كود JS يستخدم لتوفير وظائف حديثة للمتصفحات القديمة التي لا تدعمها.



إحدى ميزات SystemJS هي خريطة الاستيراد ، والتي تسمح لك باستيراد وحدة نمطية عبر الشبكة وتعيينها إلى اسم متغير.



على سبيل المثال ، يمكنك استخدام خريطة استيراد لمكتبة React يتم تحميلها عبر CDN:



ولكن!



إذا كنت تقوم بإنشاء مشروع من الصفر ، حتى مع الأخذ في الاعتبار أنك قد حددت جميع معايير مشروعك ، فقد قررت أنه سيكون لديك مشروع ضخم ضخم مع فريق مكون من أكثر من 30 شخصًا ، انتظر دقيقة!



تعجبني حقًا فكرة المؤسس السيئ السمعة لفكرة الخدمات المصغرة - مارتن فاولر .



اقترح الجمع بين النهج المتآلف والخدمات المصغرة في واحد (MonolithFirst). فكرتها الرئيسية هي كما يلي: 

يجب ألا تبدأ مشروعًا جديدًا بخدمات مصغرة ، حتى لو كنت واثقًا تمامًا من أن التطبيق المستقبلي سيكون كبيرًا بما يكفي لتبرير هذا النهج


سأصف أيضًا عيوب استخدام مثل هذه الهندسة هنا:



  • لا يمكن تحقيق التفاعل بين الأجزاء باستخدام طرق الأنبوب القياسية (DI ، على سبيل المثال).
  • ماذا عن التبعيات المشتركة؟ بعد كل شيء ، سينمو حجم التطبيق بسرعة فائقة إذا لم يتم إخراجها من الأجزاء.
  • يجب أن يظل شخص ما مسؤولاً عن التوجيه في التطبيق النهائي.
  • ليس من الواضح ما يجب فعله بحقيقة أن الخدمات المصغرة المختلفة يمكن أن تقع في مجالات مختلفة
  • ماذا تفعل إذا كان أحد الأجزاء غير متوفر / لا يمكن تقديمه.


3. إنشاء حاوية جذر



وهكذا ، يكفي من الناحية النظرية ، حان الوقت للبدء.



اذهب إلى وحدة التحكم



ng add single-spa-angular
npm i systemjs@6.1.4,
npm i -d @types/systemjs@6.1.0,
npm import-map-overrides@1.8.0


في ts.config.app.json ، إقرارات الاستيراد العالمية (الأنواع)



// ts.config.app.json

"compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": [
(+)     "systemjs"
    ]
},




أضف إلى app-routing.module.ts جميع التطبيقات الدقيقة التي نضيفها إلى الجذر



// app-routing.module.ts

{
    path: 'vue-app',
    children: [
        {
            path: '**',
            loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
            data: { app: '@somename/vue-app' }
        }
    ]
},
{
    path: 'angular-app',
    children: [
        {
            path: '**',
            loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
            data: { app: '@somename/angular-app' }
        }
    ]
},
{
    path: 'react-app',
    children: [
        {
            path: '**',
            loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
            data: { app: '@somename/react-app' }
        }
    ]
},


تحتاج أيضًا إلى إضافة config



// extra-webpack.config.json

module.exports = (angularWebpackConfig, options) => {
    return {
        ...angularWebpackConfig,
        module: {
            ...angularWebpackConfig.module,
            rules: [
                ...angularWebpackConfig.module.rules,
            {
                parser: {
                    system: false
                }
             }
           ]
        }
    };
}


دعنا نغير ملف package.json ونضيف إليه كل ما هو ضروري للعمل



// package.json

"dependencies": {
      ...,
(+) "single-spa": "^5.4.2",
(+) "single-spa-angular": "^4.2.0",
(+) "import-map-overrides": "^1.8.0",
(+) "systemjs": "^6.1.4",
}
"devDependencies": {
      ...,
(+)  "@angular-builders/custom-webpack": "^9",
(+)  "@types/systemjs": "^6.1.0",
}


أضف المكتبات المطلوبة إلى angular.json



// angular.json

{
    ...,
    "architect": {
        "build": {
            ...,
            "scripts": [
                ...,
(+)            "node_modules/systemjs/dist/system.min.js",
(+)            "node_modules/systemjs/dist/extras/amd.min.js",
(+)            "node_modules/systemjs/dist/extras/named-exports.min.js",
(+)            "node_modules/systemjs/dist/extras/named-register.min.js",
(+)            "node_modules/import-map-overrides/dist/import-map-overrides.js"
             ]
        }
     }
},


قم بإنشاء مجلد سبا فردي في جذر المشروع . دعنا نضيف ملفين إليها.



1. route-reuse- Strategy.ts - ملف توجيه الخدمات المصغرة الخاص بنا.

إذا كان تطبيق فرعي يقوم بالتوجيه داخليًا ، فإن هذا التطبيق يفسر ذلك على أنه تغيير في المسار.



    بشكل افتراضي ، سيؤدي هذا إلى تدمير المكون الحالي واستبداله بمثيل جديد من نفس مكون مضيف spa.



تبحث إستراتيجية إعادة استخدام المسار هذه في routeData.app لتحديد ما إذا كان يجب معاملة المسار الجديد على أنه نفس المسار السابق ، مما يضمن أننا لا نعيد تحميل التطبيق الفرعي عند توجيهات التطبيق الفرعي المحدد داخليًا.



// route-reuse-strategy.ts

import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
import { Injectable } from '@angular/core';
@Injectable()
export class MicroFrontendRouteReuseStrategy extends RouteReuseStrategy {
    shouldDetach(): boolean {
        //   
        return false;
    }
    store(): void { }
    shouldAttach(): boolean {
        return false;
    }
    //   
    retrieve(): DetachedRouteHandle {
        return null;
    }
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig || (future.data.app && (future.data.app === curr.data.app));
    }
}


2. خدمة واحدة spa.service.ts ستخزن



الخدمة طريقة تركيب (تركيب) وفك (إلغاء) تطبيقات الواجهة الأمامية الصغيرة.



    mount هي إحدى وظائف دورة الحياة التي سيتم استدعاؤها عندما لا يتم تحميل التطبيق المسجل ، ولكن وظيفة النشاط الخاصة به تعود صحيحة. عند الاستدعاء ، يجب أن تنظر هذه الوظيفة إلى عنوان URL لتحديد المسار النشط ، ثم إنشاء عناصر DOM ، وأحداث DOM ، وما إلى ذلك.



    unmount هي إحدى وظائف دورة الحياة التي سيتم استدعاؤها كلما تم تحميل تطبيق مسجل ، لكن وظيفة نشاطها ترجع خطأ. عند الاستدعاء ، يجب أن تمسح هذه الوظيفة جميع عناصر DOM.



//single-spa.service.ts

import { Injectable } from '@angular/core';
import { mountRootParcel, Parcel, ParcelConfig } from 'single-spa';
import { Observable, from, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class SingleSpaService {
    private loadedParcels: {
        [appName: string]: Parcel;
    } = {};
    mount(appName: string, domElement: HTMLElement): Observable<unknown> {
        return from(System.import<ParcelConfig>(appName)).pipe(
            tap((app: ParcelConfig) => {
                this.loadedParcels[appName] = mountRootParcel(app, {
                    domElement
                });
            })
        );
    }
    unmount(appName: string): Observable<unknown> {
        return from(this.loadedParcels[appName].unmount()).pipe(
            tap(( ) => delete this.loadedParcels[appName])
        );
    }
}


بعد ذلك ، نقوم بإنشاء حاوية دليل / app / spa-host .



ستنفذ هذه الوحدة التسجيل ورسم الخرائط لتطبيقات الواجهة الأمامية المصغرة الخاصة بنا إلى الجذر.



دعنا نضيف 3 ملفات إلى الوحدة.



1. وحدة spa-host.module.ts نفسها



//spa-host.module.ts

import { RouterModule, Routes } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SpaUnmountGuard } from './spa-unmount.guard';
import { SpaHostComponent } from './spa-host.component';
const routes: Routes = [
    {
        path: '',
        canDeactivate: [SpaUnmountGuard],
        component: SpaHostComponent,
    },
];
@NgModule({
    declarations: [SpaHostComponent],
    imports: [CommonModule, RouterModule.forChild(routes)]
})
export class SpaHostModule {}


2. مكون spa-host.component.ts - ينسق تثبيت وتفكيك تطبيقات الواجهة الأمامية الصغيرة



// spa-host.component.ts 

import { Component, OnInit, ViewChild, ElementRef, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import {SingleSpaService} from '../../single-spa/single-spa.service';
@Component({
selector: 'app-spa-host',
template: '<div #appContainer></div>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpaHostComponent implements OnInit {
    @ViewChild('appContainer', { static: true })
    appContainerRef: ElementRef;
    appName: string;
    constructor(private singleSpaService: SingleSpaService, private route: ActivatedRoute) { }
    ngOnInit() {
        //    
        this.appName = this.route.snapshot.data.app;
        this.mount().subscribe();
    }
     //       
    mount(): Observable<unknown> {
        return this.singleSpaService.mount(this.appName, this.appContainerRef.nativeElement);
    }
    // 
    unmount(): Observable<unknown> {
        return this.singleSpaService.unmount(this.appName);
    }
}


3. spa-unmount.guard.ts - للتحقق مما إذا كان اسم التطبيق في المسار مختلفًا ، قم بتحليل الخدمة السابقة ، إذا كانت كذلك ، فما عليك سوى الانتقال إليها. 



// spa-unmount.guard.ts

import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SpaHostComponent } from './spa-host.component';
@Injectable({ providedIn: 'root' })
export class SpaUnmountGuard implements CanDeactivate<SpaHostComponent> {
    canDeactivate(
        component: SpaHostComponent,
        currentRoute: ActivatedRouteSnapshot,
        currentState: RouterStateSnapshot,
        nextState: RouterStateSnapshot
    ): boolean | Observable<boolean> {
        const currentApp = component.appName;
        const nextApp = this.extractAppDataFromRouteTree(nextState.root);
        
        if (currentApp === nextApp) {
            return true;
        }
        return component.unmount().pipe(map(_ => true));
    }
    private extractAppDataFromRouteTree(routeFragment: ActivatedRouteSnapshot): string {
        if (routeFragment.data && routeFragment.data.app) {
            return routeFragment.data.app;
        }
        if (!routeFragment.children.length) {
            return null;
        }
        return routeFragment.children.map(r => this.extractAppDataFromRouteTree(r)).find(r => r !== null);    
    }
}


نسجل كل ما أضفناه إلى وحدة app.module



// app.module.ts

providers: [
      ...,
      {
(+)     provide: RouteReuseStrategy,
(+)     useClass: MicroFrontendRouteReuseStrategy
      }
]


دعنا نغير main.js.



// main.ts

import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { start as singleSpaStart } from 'single-spa';
import { getSingleSpaExtraProviders } from 'single-spa-angular';
import { AppModule } from './app/app.module';
import { PlatformLocation } from '@angular/common';
if (environment.production) {
    enableProdMode();
}
singleSpaStart();
//  

const appId = 'container-app';

//      ,     getSingleSpaExtraProviders. 
platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule).then(module => {
    NgZone.isInAngularZone = () => {
    // @ts-ignore
        return window.Zone.current._properties[appId] === true;
    };
    const rootPlatformLocation = module.injector.get(PlatformLocation) as any;
    const rootZone = module.injector.get(NgZone);
    // tslint:disable-next-line:no-string-literal
    rootZone['_inner']._properties[appId] = true;
    rootPlatformLocation.setNgZone(rootZone);
})
.catch(err => {});


بعد ذلك ، نقوم بإنشاء ملف import-map.json في مجلد المشاركة. الملف مطلوب لإضافة خرائط الاستيراد.

في الوقت الحالي ، سيكون لدينا فارغة ونملأها حيث تتم إضافة التطبيقات إلى الجذر.



<head>
<!doctype html>
<html lang="en">
<head>
       <meta charset="utf-8">
       <title>My first microfrontend root project</title>
       <base href="/">
       ...
(+)  <meta name="importmap-type" content="systemjs-importmap" />
    <script type="systemjs-importmap" src="/assets/import-map.json"></script>
</head>
<body>
    <app-root></app-root>
    <import-map-overrides-full></import-map-overrides-full>
    <noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>
    

4. إنشاء تطبيق VUE الصغير (تطبيق vue)



الآن بعد أن أضفنا القدرة على أن نصبح تطبيقًا جذريًا لمشروعنا المترابط ، حان الوقت لإنشاء أول تطبيق خارجي صغير لنا مع منتجع صحي فردي.



أولاً ، نحتاج إلى تثبيت create-single-spa عالميًا ، وهي واجهة سطر أوامر ستساعدنا في إنشاء مشاريع سبا فردية جديدة بأوامر بسيطة.



اذهب إلى وحدة التحكم



npm install --global create-single-spa


قم بإنشاء تطبيق vue بسيط باستخدام أمر في وحدة التحكم



create-single-spa


ستطالبك واجهة سطر الأوامر بتحديد دليل واسم مشروع وتنظيم ونوع التطبيق المراد إنشاؤه



صورة



? Directory for new project vue-app 
? Select type to generate single-spa application / parcel 
? Which framework do you want to use? vue 
? Which package manager do you want to use? npm 
? Organization name (use lowercase and dashes) somename 


نطلق تطبيقنا الصغير



npm i 
npm run serve --port 8000


عندما ندخل المسار في المتصفح localhost : 8080 / ، في حالة vue ، سنرى شاشة فارغة. ماذا حدث؟ 

نظرًا لعدم وجود ملف index.js في التطبيق المصغر الذي تم إنشاؤه.  



يوفر Single-spa ملعبًا لتنزيل التطبيق منه عبر الإنترنت ، لذلك دعونا نستخدمه أولاً.



أضف إلى index.js 

single-spa-playground.org/playground/instant-test؟name=@some-name/vue-app&url=8000
عند إنشاء التطبيق الجذر ، أضفنا خريطة مسبقًا لتحميل مشروع vue الخاص بنا. 



{
"imports": {
    ... ,
    "vue": "https://unpkg.com/vue",     
    "vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js",
    "@somename/vue-app": "//localhost:8080/js/app.js"
}
}


جاهز! الآن من مشروع الجذر الزاوي ، يمكننا تحميل تطبيقات دقيقة مكتوبة بلغة vue.



5. إنشاء رد فعل لتطبيق صغير (تطبيق رد فعل)



نقوم بإنشاء تطبيق تفاعل بسيط مماثل باستخدام الأمر الموجود في وحدة التحكم



create-single-spa


اسم المنظمة: اسم



المشروع اسم المشروع: رد فعل التطبيق



? Directory for new project react-app 
? Select type to generate single-spa application / parcel 
? Which framework do you want to use? react 
? Which package manager do you want to use? npm 
? Organization name (use lowercase and dashes) somename 


دعنا نتحقق مما إذا كنا قد أضفنا خريطة استيراد في تطبيق الجذر الخاص بنا



{
"imports": {
    ... ,
       "react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.development.js",
       "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.development.js",
       "@somename/react-app": "//localhost:8081/somename-projname.js",
	}
}


منجز! الآن ، في مسار تطبيق التفاعل الخاص بنا ، نقوم بتحميل المشروع الصغير للتفاعل. 



6. إنشاء تطبيق صغير ANGULAR (تطبيق الزاوية)



نقوم بإنشاء تطبيق Angular الصغير بالطريقة نفسها تمامًا مثل التطبيق 2 السابق



create-single-spa


اسم المنظمة: اسم



المشروع اسم المشروع: الزاوي التطبيق



? Directory for new project angular-app 
? Select type to generate single-spa application / parcel 
? Which framework do you want to use? angular 
? Which package manager do you want to use? npm 
? Organization name (use lowercase and dashes) somename 


دعنا نتحقق مما إذا كنا قد أضفنا خريطة استيراد في تطبيق الجذر الخاص بنا



{
    "imports": {
        ... ,
       "@somename/angular-app": "//localhost:8082/main.js",
     }
}


نطلق ، تحقق ، كل شيء يجب أن يعمل.



هذه أول رسالة لي على Habré ، سأكون ممتنًا جدًا لتعليقاتك.



All Articles