یکی از مواردی که با
Angular CLI 6.0 به شدت ساده شدهاست، ایجاد پروژههای «کتابخانه» Angular است. برای مثال شاید در حین استفادهی از بعضی از کتابخانهی ثالث تهیه شدهی برای Angular با خطای ذیل مواجه شده باشید:
Please open an issue in the library repository to alert its author and ask them to
package the library using the Angular Package Format (https://goo.gl/jB3GVv).
این خطا زمانی رخ میدهد که تهیه کنندهی کتابخانه، فرمت بستههای Angular را رعایت نکرده باشد و ... رعایت کردن آن نیز کار بسیار مشکلی است. نگارش 6 در پشت صحنه، پروژهی موفق ng-packagr را به مجموعهی CLI اضافه کردهاست و از این پس توسط خود CLI میتوان کتابخانههای استاندارد Angular را تولید کرد. این مورد، مزیت استاندارد سازی کتابخانههای npm حاصل را نیز به همراه دارد. مشکلی که گاهی از اوقات به علت عدم رعایت این ساختار با بستههای فعلی npm مخصوص Angular وجود دارند؛ مانند خطایی که عنوان شد. برای مثال بدون استفادهی از این ابزار، نیاز است مستندات چند صفحهای ساخت کتابخانههای Angular را سطر به سطر پیاده سازی کنید که توسط CLI 6.0 به صورت خودکار ایجاد و مدیریت میشود.
مراحل ایجاد یک پروژهی «کتابخانه» توسط Angular CLI 6.0
مرحلهی اول ایجاد یک پروژهی کتابخانه، مانند قبل، توسط دستور ng new و ایجاد یک پروژهی دلخواه جدید است:
به همراه Angular CLI 6.0، فرمت تنظیمات آن نیز تغییر کردهاست و مفهوم workspace به آن اضافه شدهاست که در آن میتوان چندین پروژه را تعریف کرد.
پس از ایجاد پروژهی my-lib-test توسط دستور فوق و وارد شدن به پوشهی اصلی آن توسط خط فرمان، میتوان با اجرای دستور زیر، پروژههای دیگری را به پروژهی جاری افزود:
ng generate application my-app-name
اما اگر در اینجا بجای ذکر application، از نام library استفاده کنیم، یک کتابخانه را بجای یک برنامه، به workspace جاری اضافه میکند:
ng generate library my-lib
پس از اجرای این دستور اگر به فایل angular.json دقت کنیم، این پروژه در ذیل projects اضافه شدهاست:
همچنین یک پوشهی جدید به نام projects نیز ایجاد شده و پروژهی my-lib داخل آن قرار گرفتهاست.
فایل جدید public_api.ts
پس از ایجاد کتابخانهی جدید «my-lib»، فایل جدیدی به نام projects\my-lib\src\public_api.ts نیز به آن اضافه شدهاست:
با این محتوا:
/*
* Public API Surface of my-lib
*/
export * from './lib/my-lib.service';
export * from './lib/my-lib.component';
export * from './lib/my-lib.module';
هر خروجی که در اینجا ذکر شود توسط استفاده کنندگان از این کتابخانه قابل دسترسی خواهد بود. برای مثال دستور «ng generate library my-lib» مطابق تصویر فوق، یک سرویس جدید را به نام my-lib.service، یک کامپوننت جدید را به نام my-lib.component و یک ماژول جدید را به نام my-lib.module به صورت پیشفرض ایجاد کرده و درون پوشهی lib قرار دادهاست. اکنون آنها را توسط فایل public_api.ts، به نحوی که مشاهده میکنید در معرض دید استفاده کنندگان قرار میدهد.
برای مثال اگر فایل جدید projects\my-lib\src\lib\my-lib.models.ts را به این کتابخانه اضافه کنیم که شامل تعدادی مدل و اینترفیس قابل دسترسی توسط استفاده کنندگان باشد، باید یک سطر زیر را به انتهای فایل public_api.ts اضافه کنیم:
export * from './lib/my-lib.models';
این پروژهی کتابخانه حتی به همراه فایلهای package.json, tsconfig.json, tslint.json مخصوص به خود نیز میباشد تا بتوان آنها را صرفا جهت این پروژه سفارشی سازی کرد.
ساختار my-lib.service پیشفرض یک پروژهی کتابخانه
اگر به فایل projects\my-lib\src\lib\my-lib.service.ts دقت کنیم:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MyLibService {
constructor() { }
}
تمام قسمتهای آن مانند قبل است، منهای 'providedIn: 'root آن. این مورد تنظیم جدیدی است که در پروژههای Angular 6 قابل استفادهاست. هدف از آن، ارائهی یک سرویس، بدون نیاز به ثبت صریح آن در قسمت providers یک NgModule است.
شاید بپرسید چرا؟ هدف اصلی از آن، بهبود فرآیند tree-shaking یا حذف کدهای مرده و استفاده نشدهاست. ممکن است سرویسی را تعریف کنید، اما در برنامه استفاده نشود. این حالت خصوصا در پروژههای کتابخانههای ثالث ممکن است زیاد رخ دهد. به همین جهت با ارائهی این قابلیت، امکان حذف سادهتر سرویسهایی که در برنامه استفاده نشدهاند از خروجی نهایی کامپایل شده، وجود خواهد داشت.
چگونه به پروژهی کتابخانهی جدید، یک کامپوننت جدید را اضافه کنیم؟
تمام دستورات Angular CLI، در اینجا نیز کار میکنند. تنها تفاوت آنها، ذکر صریح نام پروژهی مورد استفاده است:
ng generate component show-data --project=my-lib
دستور فوق کامپوننت جدید show-data را به پروژهی my-lib اضافه خواهد کرد؛ به همراه به روز رسانی خودکار فایل projects/my-lib/src/lib/my-lib.module.ts این پروژه، جهت ثبت کامپوننت اضافه شده.
البته در اینجا باید فایل my-lib.module.ts را اندکی ویرایش کرد و ShowDataComponent را به قسمت exports نیز افزود:
@NgModule({
imports: [
CommonModule,
HttpClientModule
],
declarations: [MyLibComponent, ShowDataComponent],
exports: [MyLibComponent, ShowDataComponent]
})
export class MyLibModule { }
به صورت پیشفرض، کامپوننت جدید را در قسمت declarations معرفی میکند. یک چنین کامپوننتی فقط داخل همان lib قابل استفادهاست. اگر قرار است خارج از این lib نیز به آن دسترسی داشته باشیم، باید آنرا در قسمت exports نیز قید کنیم.
همچنین قسمت imports آن نیز به صورت پیشفرض خالی است. اگر نیاز است با ngIf کار کنید، باید CommonModule را در اینجا قید کنید و اگر نیاز است تبادلات HTTP وجود داشته باشد، ذکر HttpClientModule نیز ضروری است.
مرحلهی ساخت پروژه
پیش از استفادهی از این پروژهی کتابخانه، باید آنرا build کرد:
در اینجا نیز دستور ng build مانند قبل است، با این تفاوت که نام پروژهی کتابخانه نیز در اینجا ذکر شدهاست.
پس از اجرای این دستور، خروجی ذیل مشاهده میشود:
Building Angular Package
Building entry point 'my-lib'
Rendering Stylesheets
Rendering Templates
Compiling TypeScript sources through ngc
Downleveling ESM2015 sources through tsc
Bundling to FESM2015
Bundling to FESM5
Bundling to UMD
Minifying UMD bundle
Remap source maps
Relocating source maps
Copying declaration files
Writing package metadata
Removing scripts section in package.json as it's considered a potential security vulnerability.
Built my-lib
Built Angular Package!
- from: D:\my-lib-test\projects\my-lib
- to: D:\my-lib-test\dist\my-lib
همانطور که ملاحظه میکنید، پس از طی مراحل خاص تولید یک کتابخانه، خروجی نهایی آنرا در پوشهی dist\my-lib قرار دادهاست.
استفادهی از کتابخانهی تولید شده
پس از پایان موفقیت آمیز مرحلهی Build، اکنون نوبت به استفادهی از این کتابخانه است. استفادهی از آن نیز همانند تمام کتابخانهها و وابستگیهای ثالثی است که تا پیش از این از آنها استفاده کردهایم. برای مثال ماژول آنرا در قسمت imports مربوط به NgModule کلاس AppModule معرفی میکنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و MyLibModule را به نحو ذیل اضافه میکنیم:
import { MyLibModule } from "my-lib";
@NgModule({
imports: [
BrowserModule,
MyLibModule
]
})
export class AppModule { }
نکتهی مهمی که در اینجا باید به آن دقت داشت این است که هرچند در این پروژه، MyLibModule داخل پوشهی projects\my-lib\src\lib قرار دارد، اما نباید مسیر نسبی آنرا در اینجا ذکر کرد و باید صرفا نام پوشهی my-lib واقع در پوشهی node_modules را در اینجا در حین مسیر دهی import آن معرفی کرد (همانند تمام وابستگیهای ثالث دیگر).
اما سؤال اینجا است که آیا این پوشه پس از build، داخل پوشهی node_modules نیز کپی شدهاست؟ پاسخ آن خیر است و برای مدیریت خودکار آن، به صورت زیر عمل شدهاست:
اگر به فایل tsconfig.json اصلی و واقع در ریشهی workspace دقت کنید، پس از اجرای دستور «ng generate library my-lib»، قسمت paths آن نیز به صورت خودکار ویرایش شدهاست:
{
"compilerOptions": {
"paths": {
"my-lib": [
"dist/my-lib"
]
}
}
}
معنای آن این است که هرگاه import ایی در برنامه به my-lib اشاره کند، کامپایلر TypeScript میداند که باید آنرا از پوشهی dist/my-lib دریافت و پردازش کند. به همین جهت در اینجا دیگر نیازی به کپی دستی این پوشه، به پوشهی node_modules وجود ندارد.
برای نمونه اگر شارهگر ماوس را بر روی my-lib قرار دهید، به درستی مسیر خوانده شدن آن، تشخیص داده میشود.
به این ترتیب مسیر این import، چه در این پروژهی محلی و چه برای کسانیکه پوشهی dist/my-lib را به صورت یک بستهی npm جدید دریافت کردهاند، یکی خواهد بود.
در ادامه اگر به فایل app.component.html مراجعه کرده و selector کامپوننت show-data را به آن اضافه کنیم:
<lib-show-data></lib-show-data>
میتوان محتویات این کامپوننت دریافت شدهی از کتابخانه را مشاهده کرد.
توزیع کتابخانهی ایجاد شده برای عموم
برای اینکه این کتابخانهی تولیدی را در اختیار عموم، در سایت npm قرار دهیم، ابتدا باید کتابخانه را در حالت production build تولید و سپس آنرا publish کرد:
ng build my-lib --prod
cd dist/my-lib
npm publish
سطر اول، کتابخانهی my-lib را در حالت production تواید میکند. سپس به پوشهی فایلهای نهایی تولید شده وارد میشویم و دستور npm publish را صادر میکنیم.
البته دستور آخر نیاز به ایجاد یک اکانت در سایت npm و وارد شدن به آنرا دارد. جزئیات بیشتر آن
در اینجا.