چند وقتی هست که در پروژه انگولار شرکت، دارم از کلاس های standalone استفاده میکنم. تو این مقاله خواستم تجربه م رو باهاتون به اشتراک بگذارم. در این مقاله دو موضوع رو با هم مرور میکنیم:
کلاس های قابل تعریف ( declarable ) در انگولار عبارتند از component ها، directive ها و pipe ها . این کلاس ها علاوه بر تعریف خودشون، شامل یک decorator هم هستند (که با @ مشخص میشوند و با نام های Component، Directive و Pipe معرفی میشوند). این decorator در حقیقت یک آبجکت metadata هست و تعریف میکنه که در زمان compile time چطوری باید از این کلاس استفاده بشه:
در مثال بالا پیش از کلاس MenuComponent یک بلاک metadata تعریف شده و شامل نام تگ مربوط به این کامپوننت، تمپلت و فایل css آن است.
یک ویژگی مهم کلاس های declarable تا قبل از تعریف standalone این بود که حتما باید در یک NgModule معرفی میشدند.
مفهوم NgModule در انگولار مفهوم نسبتا پیچیده ایه. با معرفی استایل standalone در کلاس های declarable ( component ها، directive ها و pipe ها ) عملا میتوان کلاس هایی تعریف کرد که الزامی به معرفی شدن در NgModule ندارند و خودشان scope خودشان را دارند. با آمدن استایل standalone میتوان NgModule را در توسعه اپلیکیشن های قدیمی کمرنگ کرد و حتی در اپلیکیشن های جدید آن را کاملا کنار گذاشت.
یکی از روش های مرسوم در انگولار همیشه این بود که امکانات مشترک در کل پروژه در یک SharedModule بزرگ تعریف و وارد سیستم میشد. در حالیکه در کامپوننت فعلی فقط بخش کوچکی از اون SharedModule استفاده میشد. در حالت هوشمند تر برای هر کامپوننت یک ماژول مجزا نوشته میشد ( مثل کتابخانه Angular Material ) و بر اساس مورد استفاده فقط ماژول های مورد نیاز به سیستم وارد میشدند. در واقع یکی از دلایل کنار گذاشتن NgModule ها این است که برای استفاده از امکانات Shared در اپلیکیشن نیازی نیست یک SharedModule خیلی بزرگ رو وارد اپلیکیشن کنیم و بعد یک بخش از اون رو استفاده کنیم. و همچنین نیاز به تعریف تعداد زیادی ماژول برای تعداد زیادی امکانات Shared نیست. با آمدن استایل standalone ، کامپوننت های Shared تنها چند کلاس در یک پوشه هستند و ساختار سبک تری دارند و توسعه آنها هم ساده تر است. این کلاس ها در واقع NgModule خودشان را به صورت پنهانی ( پشت پرده) همراه خودشون دارند و به همین دلیل قابل import شدن در NgModule های دیگه هم هستند.
در مثال های بخش بعدی این موضوع وضوح بیشتری پیدا میکنه.
قدم اول: برای معرفی یک کلاس قابل تعریف (declarable) به کاپایلر به عنوان کلاس standalone، کافیه مقدار standalone در metadata رو true کنیم:
قدم دوم: اون کلاس رو در NgModule مربوطه از لیست declaration در بیاریم و به لیست imports وارد کنیم:
قدم سوم: تا اینجا کلاس standalone همچنان از طریق همین ماژول در کل پروژه کار میکنه . پس کلاس رو از ماژولش کامل در میاریم و مستقیما در NgModule هایی که به کلاس نیاز دارند import میکنیم.
قدم چهارم: مشکلی که ممکنه پیش بیاد اینه که کلاس ما از ماژول های دیگه ای استفاده کرده باشه (dependency داشته باشه). وقتی استایل standalone رو برای یک کلاس استفاده میکنیم، امکان استفاده از imports در آبجکت metadata فعال میشه و ما میتونیم dependency های کلاس رو بهش import کنیم:
یک مدل دیگه:
نکته: کلاس هایی که از استایل standalone استفاده نمی کنند امکان imports رو در metadata ندارند.
من با تکرار چهار قدم بالا همه کلاس های SharedModule پروژه رو به کلاس های standalone تبدیل کردم و عملا ماژول SharedModule رو از پروژه حذف کردم. این رو برای هر ماژول دیگه ای هم میتونیم انجام بدیم.
اما تبدیل AppModule و استفاده از استایل standalone در کامپوننت اصلی پروژه (به صورت پیش فرض AppComponent) باعث میشه برای شروع به کار اپلیکیشن به مشکل بخوریم. برای شروع به کار یک اپلیکیشن بدون NgModule نیاز به تغییراتی در main.ts و app.module.ts و همچنین app-routing.module.ts داریم:
برای این کار اول کلاس AppComponent رو با همون چهار قدم بالا به استایل standalone در میاریم و AppModule عملا بی خاصیت میشه. حالا دو تا ماژول داریم که باید از دستشون خلاص بشیم. یکی AppModule و یکی AppRoutingModule .
قدم اول اینه که در فایل app-routing.module.ts باید routes رو تعریف و export کنیم که بتونیم در main.ts ازش استفاده کنیم. ممکنه این متغیر رو تعریف نکرده باشیم یا export نشده باشه. در نمونه کد پایین، خط های استایل قدیمی رو حذف نکردم که در کنار استایل جدید قابل دیدن باشه. فقط comment شون کردم:
در قدم دوم میریم سراغ main.ts. فایل main.ts انگولار به صورت پیش فرض به شکل زیره:
برای شروع اپلیکیشن با کامپوننت standalone، بجای ارسال AppModule به تابع bootstrapModule، باید کامپوننت AppComponent رو به تابع bootstrapApplication ارسال کنیم و همچنین مستقیما از متغیر routes استفاده کنیم:
اما استفاده از این روش برای boostrap اپلیکیشن انگولار باعث محدودیت هایی میشه که باید در نظر گرفته بشه:
ngDoBootstrap
در AppComponent استفاده کنیم.من شخصا تا الان که دارم این مقاله رو مینویسم نیازی به هیچ کدوم از این سه مورد نداشتم.
در این مقاله با استایل standalone آشنا شدیم و دلایل حرکت انگولار به این سمت رو فهمیدیم. یاد گرفتیم با چهار قدم کامپوننت ها رو از مدل وابسته به NgModule به مدل standalone تبدیل کنیم. و یاد گرفتیم اپلیکیشن رو از وابستگی به NgModule در بیاریم و محدودیت هاش رو هم در نظر بگیریم.