کامپوننت standalone نوعی کامپوننت است که به هیچ ماژولی تعلق ندارد. قبل از انگولار نسخه 14، زمانی که یک کامپوننت را ایجاد می کردیم، معمولاً باید آن را در آرایه declaration یک ماژول قرار میدادیم. در غیر این صورت، در حین کامپایل با خطا روبرو میشدیم. با این حال، از نسخه 14 انگولار، می توانیم کامپوننت های مستقل(standalone) ایجاد کنیم که به هیچ ماژول خاصی متصل نیستند. علاوه بر کامپوننت ها، همچنین می توانیم دایرکتیو ها و پایپ های مستقل ایجاد کنیم.
برای شروع، مطمئن شوید که از انگولار نسخه 14 یا بالاتر استفاده میکنید. میتوانید یک کامپوننت standalone با استفاده از پرچم --standalone با دستور ng generate component ایجاد کنید:
ng g c component_name --standalone
کامپوننت های standalone چه مزیتی هایی دارند؟ فایده اصلی standalone component ها در انگولار احتمالا چیزی نیست که داری فکر میکنی. :)
اینجا قصد داریم همه چیز را در مورد standalone component ها یاد بگیریم و بررسی کنیم چطور آنها بهتر از NgModule-based کامپوننت ها هستند.
در ادامه تمام مزایای standalone component ها را بیان میکنیم و توضیح می دهیم که چرا برخی از معیاب مطرح شده برای آنها مشکل بزرگی نیستند. همچنین یاد میگیریم که چگونه standalone component ها به سریعتر شدن برنامه کمک می کنند.
فهرست مطالب
کامپوننت standalone چیست؟
نوع جدیدی از کامپوننت های انگولار هستند که نیازی به معرفی (declared) در NgModule ندارند. این کامپوننت ها می توانند به صورت مستقیم در template کامپوننت دیگری استفاده شوند بدون اینکه در NgModule ایمپورت شوند.
ابتدا بهتر است تفاوت standalone component را با کامپوننت های معمولی بررسی کنیم. اینجا یک کامپوننت معمولی داریک که standalone نیست.
@Component({ selector: 'hello', template: `Hello {{ name }}`, }) class HelloComponent { }
کامپوننت های معمولی مثل این باید در NgModule معرفی شوند، در غیر اینصورت نمی توان از آنها در template کامپوننت های دیگر استفاده کرد. در قطعه کد زیر کامپوننت HelloComponent در AppModule ایمپورت شده است:
@NgModule({ declarations: [AppComponent, HelloComponent], imports: [BrowserModule, FormsModule], providers: [], bootstrap: [AppComponent], }) export class AppModule { }
بیایید HelloComponent را به یک standalone component تبدیل کنیم. برای این کار، تنها کاری که باید انجام دهیم این است که ویژگی standalone: true را در دکوریتور(decorator)، Component@ اضافه کنیم.
@Component({ selector: 'hello', template: `Hello {{ name }}`, standalone: true, }) class HelloComponent { }
فقط همین!!!
حالا برای استفاده از این کامپوننت، به سادگی باید آن را در کامپوننتی که میخواهیم از آن استفاده کنیم، ایمپورت کنیم.
برای مثال، فرض کنید میخواهیم از HelloComponent در ParentComponent استفاده کنیم:
@Component({ selector: 'parent', template: `<hello></hello>`, imports: [HelloComponent], }) class ParentComponent { }
همانطور که می بینید فقط باید آن را در آرایه imports وارد کنیم و تمام! این ایمپورت ها ممکن است دشوار به نظر برسد، اما اینطور نیست، بعداً در مورد آن بیشتر توضیح خواهیم داد. توجه داشته باشید که بدون ایمپورت کردن، ParentComponent آنطور که در نظر گرفته شده کار نخواهد کرد، و اغلب خطایی رخ نخواهد داد.
استفاده از Directive های داخلی(built-in) در standalone component
حتما از دایرکتیوهای built-in انگولار استفاده کردید. انگولار بیشتر دایرکتیوهای داخلی خود را به عنوان standalone directives در دسترس قرار داده. برای استفاده از آنها در standalone component ها فقط کافیه در آرایه imports اضافه شوند:
import { NgClass } from '@angular/core' @Component({ selector: 'app-hello', template: ` <div [ngClass]='{ highlight: true }'>Hello World!</div> `, imports: [NgClass], standalone: true, }) class StandaloneComponent { }
همانطور که در قطعه کد بالا مشاهده می کنید، دایرکتیو ngClass در آرایه imports یک standalone component اضافه شده است بنابراین می توان از آن در template استفاده کرد. بدون این ایمپورت ngClass آنطور که درنظر گرفته شده است کار نمی کند.
باز هم، این ایمپورت های دستی می تواند به عنوان یک مزاحم دیده شود، بیشتر در مورد آن در بخش بعدی صحبت می کنیم.
چرا standalone component
همانطور که می بینید standalone component ها در نگاه اول اصلا جذاب نیستند!
شما مفهوم NgModule را حذف کردید اما آن را با ایمپورت های دستی جایگزین کردید. پس چرا آنها بسیار بهتر از کامپوننت های معمولی هستند؟
دلایل متعددی وجود دارد...
یکی از دلایل معرفی standalone component ها در انگولار حذف مفهوم NgModule بود. مفهوم ماژول در انگولار غیر ضروری به نظر می رسید و یادگیری انگولار را برای مبتدیان کمی سخت تر می کرد. این یک مفهوم اضافی برای یادگیری بود، و مشخص نبود که چرا به آن نیاز است. برای حل این مشکل، انگولار standalone component ها را معرفی کرد. اکنون با standalone component ها، میتوانید کامپوننت های خود را بدون نیاز به معرفی(declare) آنها در هر ماژولی ایجاد کنید، که بسیار راحتتر است.
وقتی آنها برای اولین بار معرفی شدند با این اشکال مواجه بودند که باید به صورت دستی وابستگی هایی را که کامپوننت به طور مستقیم به آن نیاز داشت ایمپورت کنید. همانطور که دیدیم، حتی دایرکتیوهای اصلی مانند ngClass یا ngStyle باید به صورت دستی در هر کامپوننت ایمپورت شوند.
یکی از مزایای NgModules این بود که شما میتوانید یک وابستگی را تنها در یک مکان در داخل ماژول وارد کنید، درست است؟
بنابراین برای لحظه ای کوتاه، ما فکر کردیم که استفاده از standalone component ها در سطح برنامه دست و پا گیر خواهد بود، زیرا دائماً باید همه چیز را ایمپورت کنیم. اما به لطف IDE ها تمام وابستگی های کامپوننت را می توان به صورت خودکار ایمپورت کرد.
مزیت اصلی standalone component
حذف NgModule مزیت اصلی و دلیل مهاجرت به standalone component ها نیست.
مزیت اصلی standalone component ها این است که توسعه یک برنامه کاملاً lazy-load را بسیار آسان میکنند.
اگر مدتی است که از کامپوننت های NgModule استفاده می کنید، احتمالاً متوجه موارد زیر شده اید:
برنامه(application) ایی که با NgModule توسعه یافته است احتمالاً نسبتاً یکپارچه(monolithic) است، حتی اگر NgModules قرار است به ماژولار کردن برنامه کمک کند.
اگر از lazy load استفاده نکرده اید ریفکتور یک برنامه موجود به lazy load دشوار است و اگر از آن استفاده کرده اید، احتمالاً فقط چند ماژول lazy load دارید، اما هر ماژول همچنان دارای صفحه نمایش های زیادی است. بنابراین برنامه شما احتمالاً از lazy load تا حد امکان استفاده نمی کند.
برای پیاده سازی lazy load نیاز به ایجاد یک ماژول برای هر صفحه داریم که همین ایجاد ماژول های جداگانه سربار زیادی ایجاد می کند.
اگر از standalone component ها استفاده کنید، اصلا مهم نیست که از lazy load استفاده کردید یا نه.
تنها کاری که باید انجام دهید این است که با استفاده از ویژگی جدید loadComponent، چند تغییر جزئی در پیکربندی مسیریابی(routing) خود ایجاد کنید.
پایپ(Pipe) standalone
درست مانند کامپوننت، می توانیم standalone pipe ایجاد کنیم.
@Pipe({ name: 'capitalise', standalone: true, }) export class CapitalisePipe implements PipeTransform { transform(word: string): string { return word.toLocaleUpperCase(); } }
با افزودن ویژگی standalone: true به دکوریتور Pipe@ آن را به صورت standalone ایجاد کردیم و برای استفاده از آن مانند قطعه کد زیر باید آن را در آرایه imports وارد کنیم.
@Component({ selector: 'app-hello', template: `Hello {{ name | capitalise }}`, standalone: true, imports: [CapitalisePipe], }) class AppComponent { }
دایرکتیو(Directive) standalone
برای ایجاد دایرکتیوهای standalone دقیقا مانند pipe و کامپوننت عمل میکنیم.
@Directive({ selector: '[example-directive]', standalone: true, }) class ExampleDirective { }
و برای استفاده:
@Component({ selector: 'app-hello', template: ` <div example-directive>Hello {{ name | capitalise }}</div>`, standalone: true, imports: [CapitalisePipe, ExampleDirective], }) class StandaloneComponent { }
توجه داشته باشید که تا کنون، نمونههایی از نحوه استفاده از کامپوننت ها، دایرکتیوها و پایپ های مستقل در داخل سایر اجزای مستقل را نشان دادهایم. اما نگران نباشید، اگر نیاز به استفاده از عناصر مستقل در مؤلفه های مبتنی بر NgModule دارید، می توانید این کار را نیز انجام دهید. قابلیت همکاری کامل در هر دو جهت وجود دارد. بیایید نگاهی به این بیندازیم که این قابلیت همکاری چگونه است.
استفاده از standalone component در کامپوننت های NgModule-based
کامپوننتهای standalone را میتوان در کامپوننتهای مبتنی بر NgModule نیز مانند هر کامپوننت دیگر استفاده کرد. برای استفاده از یک کامپوننت standalone در یک برنامه مبتنی بر NgModule، فقط باید کامپوننت standalone را در بخش declarations وارد کنید:
@NgModule({ declarations: [ AppComponent, StandaloneComponent ], imports: [BrowserModule], providers: [], bootstrap: [AppComponent], }) export class AppModule { }
همانطور که می بینید، استفاده از کامپوننتهای standalone در برنامه های مبتنی بر ماژول بسیار ساده است. اما برعکسش چطور؟
استفاده از کامپوننت NgModule-based در standalone component
همچنین می توانیم به راحتی از کامپوننت های مبتنی بر NgModule در داخل یک کامپوننت standalone استفاده کنیم. ابتدا یک NgModule ایجاد می کنیم و کامپوننت مبتنی بر NgModule را export می کنیم:
@NgModule({ declarations: [TraditionalComponent], // export the NgModule-based component exports: [TraditionalComponent], }) export class MyModule { }
سپس NgModule را در کامپوننت standalone ایمپورت می کنیم:
@Component({ selector: 'app-standalone', template: `I'm a standalone component 😁`, standalone: true, // Import the NgModule that declares the component imports: [MyModule], }) class StandaloneComponent { }
توجه داشته باشید که از آنجایی که ما کل MyModule را وارد کردیم، هر کامپوننت دیگری که در این ماژول معرفی شده است نیز می تواند در کامپوننت standalone استفاده شود. همانطور که می بینید، قابلیت همکاری بین کامپوننت Ng-Module و کامپوننت standalone وجود دارد. ما به راحتی می توانیم دو نوع کامپوننت را در برنامه خود بدون هیچ مشکلی ترکیب و مطابقت دهیم.
بارگذاری تنبل(Lazy loading) با standalone component
با معرفی کامپوننت های standalone بارگذاری تنبل(Lazy loading) بسیار ساده تر شد. در ابتدا، بدون کامپوننت های standalone، بارگذاری تنبل از طریق ماژول ها انجام می شد. ما باید یک ماژول برای مجموعهای از کامپوننت ها، پایپ ها یا دایرکتیوهایی ایجاد میکردیم که میخواستیم آنها را Lazy load کنیم. این کار وقت گیر بود، و شامل مراحل زیر بود:
در اینجا نمونه ای از نحوه پیکربندی مسیریابی یک ماژول با Lazy load را مشاهده می کنید:
const routes: Routes = [ { path: 'one', loadChildren: () => import('./module-one/moduleone.module'). then((m) => m.ModuleOneModule), }, ];
سربار ماژول های Lazy load از پیکربندی مسیریابی به دست نمی آید. این از نیاز به ایجاد یک ماژول و تعریف تمام وابستگی های آن ناشی می شود.
نه به این دلیل که ما می خواهیم ویژگی های خاصی را کپسوله کنیم و آنها را قابل استفاده مجدد کنیم، بلکه فقط برای هدف Lazy load کردن آن ها این ماژول ها را ایجاد می کنیم.
export const ROUTES: Route[] = [ { path: 'lazy-hello', loadComponent: () => import('./app-hello') .then((m) => m.StandaloneComponent), }, ];
اکنون تنها کاری که باید انجام دهیم این است که از loadComponent استفاده کنیم و به کامپوننت کانتینر اشاره کنیم و تمام! دیگر مجبور نیستیم برای هر صفحه یک ماژول ایجاد کنیم و همه وابستگی هایی را که هر صفحه به آن نیاز دارد وارد کنیم. بنابراین با کامپوننت های standalone، بارگذاری Lazy دیگر دردسرساز نیست و استفاده از آن بسیار آسانتر است.
راه اندازی برنامه با standalone component
برای استفاده کامل از کامپوننت های standalone، توصیه میشود به جای NgModules، برنامه را با استفاده از APIهای مستقل بوت استرپ کنید:
import { bootstrapApplication } from '@angular/platform-browser' bootstrapApplication(AppComponent );
در bootstrapApplication، ما فقط باید یک کامپوننت standalone را که میخواهیم به root برنامه تبدیل کنیم، وارد کنیم. همچنین میتوانیم وابستگیهایی را که نیاز داریم، مانند روتر، فرمها و غیره با استفاده از importProvidersFrom وارد کنیم.
import { importProvidersFrom } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { RouterModule } from '@angular/router'; import { AppComponent } from './app/app.component'; import { routes } from './routes'; bootstrapApplication(AppComponent , { providers: [importProvidersFrom(RouterModule.forRoot(routes))], }).catch((err) => console.error(err));
مهاجرت به Standalone Components
با استفاده از Angular CLI می توانید برنامه خود را به طور خودکار به Standalone Components منتقل کنید. ممکن است هنوز مجبور به انجام برخی اصلاحات جزئی باشید، اما CLI بیشتر کار را برای شما انجام خواهد داد. مهاجرت در 3 مرحله جداگانه انجام می شود. توصیه می کنم آنها را در یک شاخه انجام دهید و سه کامیت جداگانه ایجاد کنید.
برای هر مرحله، هر بار باید دستور زیر را اجرا کنید:
ng generate @angular/core:standalone
در اینجا 3 مرحله وجود دارد:
مرحله 1: دستور فوق را اجرا کنید و گزینه "Convert all components, directives, and pipes to standalone" را انتخاب کنید. با این کار ویژگی standalone به همه جا اضافه می شود و آرایه imports هر کامپوننت را پر میکند.
مرحله 2: دستور فوق را دوباره اجرا کنید، اما این بار گزینه "Remove unnecessary NgModule classes" را انتخاب کنید. با این کار سعی می شود تا حد امکان NgModules حذف شود، اما احتمالاً نمی تواند همه آنها را حذف کند، بنابراین باید کد را در اینجا مرور کنید و هر ماژول را که می توانید به صورت دستی حذف کنید.
مرحله 3: دستور فوق را برای آخرین بار اجرا کنید و گزینه "Bootstrap the application using standalone APIs" را انتخاب کنید. با این کار استفاده از AppModule برای بوت استرپ برنامه حذف می شود و به جای آن از API های standalone استفاده می شود. این مرحله آخر بسیار مهم است تا بتوان از اجزای مستقل بیشترین بهره را برد. برای مثال، اگر این مرحله آخر را انجام ندهید، lazy load مبتنی بر کامپوننت با loadComponent به درستی کار نخواهد کرد.
بعد از هر مرحله برنامه را تست کنید و تغییرات را جداگانه انجام دهید تا اگر مشکلی پیش آمد بتوانید به مرحله قبل برگردید.
مهاجرت به راحتی انجام می شود، اما احتمالاً هنوز هم مجبور خواهید بود تغییرات جزئی را خودتان انجام دهید تا به طور کامل همه NgModules را از پایگاه کد حذف کنید.
در نهایت، فقط به تنظیمات مسیریابی خود بروید و هر جا که ویژگی component را دیدید، آن را با loadComponent جایگزین کنید و تمام!
برنامه شما به یک برنامه کاملاً lazy load و سریعتر تبدیل شده است.
خلاصه
منابع