18 Innovative Ways to Use Docker Daily
- Running GUI Applications in Docker Containers
- How to Run a Web Browser Inside Docker
- Using Obsidian Note-Taking App via Docker
- LibreOffice in Docker: Open-Source Office Suite Containerized
- Contribute to Science: Folding@Home with Docker
- Effortless Docker Management with Docker Desktop and Portainer
- Secure Document Handling Using Dangerzone and Docker
- Containerizing CLI Tools for a Clean System
- Enhance Docker Security with Docker Scout
- Set Up an Isolated Hacking Lab with Docker and Kali Linux
- Master Docker Networks for Container Isolation
- Automate Your Docker Workflows with Docker Compose
- Explore New Operating Systems Using Docker Containers
- Running macOS in a Docker Container (Educational Purposes)
- How to Run Raspberry Pi OS via Docker
- Access a Suite of IT Tools Inside a Docker Container
- Using Docker to Containerize AI Tools like Fabric
- Safely Test Applications with Docker Containers
- Building Custom Docker Images from Scratch
- Unconventional Docker Use Cases You Need to Know
- Daily Docker Tips and Tricks for Enhanced Productivity
- Beginner's Guide to Advanced Docker Techniques
- Boost Your IT Workflow with These Docker Hacks
- Docker for Everyday Tasks: Beyond Development
اشتراکها
جزئیاتی درباره تغییرات NET Core 2.0.
It has only been about a year since .NET Core 1.0 RC came out. We are now getting close to .NET Core 2.0. We have been playing with .NET Core since the betas and feel like the quality of the 1.0 runtime was very good. Our only complaint has really been odd Visual Studio behavior. We would expect that with 2.0, the adoption of .NET Core is likely to skyrocket.
اشتراکها
سیستم بلاگ WilderBlog
OpenID Connect یک لایهی امنیتی بر روی OAuth 2.0 است. توسط آن عملیات Authentication به OAuth 2.0 که صرفا مختص به Authorization است، اضافه میشود.
نظرات مطالب
پیاده سازی Option یا Maybe در #C
نوعهای ارجاعی نال نپذیر بحث دیگری هستند. فعلا پیاده سازی مشابهی از آن به TypeScript 2.0 اضافه شدهاست: TypeScript 2.0: Non-Nullable Types
با پیشرفت HTML 5 و پدید آمدن چارچوبهای مختلف JavaScript توسعهی نرم افزارهای تک صفحه ای تحت وب (Single Page Applications) محبوب شده است.
اخیرا مطالب خوبی در رابطه با AngularJS در وبسایت جاری منتشر شده است. KnockoutJS توسط Microsoft معرفی شد و در قالب پیشفرض پروژههای SPA قرار گرفت ، بنابراین احتمالا این سوال برای افرادی مطرح شده است که تفاوت بین KnockoutJS و AngularJS چیست ؟
می توان پاسخ داد این مقایسه ممکن نیست.
KnockoutJS : یک پیاده سازی مستقل JavaScript از الگوی MVVM با امکانات Databinding میباشد. Knokcout یک کتابخانهی Databinding است نه یک کتاب خانهی SPA
AngularJS : طبق معرفی در این مطلب AngularJS فریم ورکی متن باز و نوشته شده به زبان جاوا اسکریپت است. هدف از به وجود آمدن این فریم ورک، توسعه هر چه سادهتر SPAها با الگوی طراحی MVC و تست پذیری هر چه آسانتر آنها است. این فریم ورک توسط یکی از محققان Google در سال 2009 به وجود آمد. بعدها این فریم ورک تحت مجوز MIT به صورت متن باز در آمد و اکنون گوگل آن را حمایت میکند و توسط هزاران توسعه دهنده در سرتاسر دنیا، توسعه داده میشود.
بنابراین شاید بهتر باشد ذکر شود AngularJS یک Presentation Framework مخصوص برنامههای وب تک صفحه ای میباشد در حالی که KnockoutJS کتاب خانه ای با تمرکز بر Databinding میباشد ، بنابراین مقایسهی اینها چندان صحیح نیست.
اگر قصد بر بررسی گزینههای دیگر در کنار Angular باشد ، میتوان از Durandal نام برد. Durandal یک چارچوب SPA میباشد ، این چارچوب بر فراز jQuery ، RequireJS و Knockout توسعه پیدا کرده است. (سابقا برای routing از SammyJS استفاده میکرد که در نسخههای اخیر از موتور خودش استفاده میکند.)
Durandal از Knockout جهت Databinding و از RequireJS برای مدیریت وابستگیها استفاده میکند.
Angular همهی امکانات بالا را مستقل پیاده سازی کرده و حتی نیازی به jQuery ندارد. اگر jQuery وجود داشته باشد Angular از آن استفاده میکند در غیر این صورت از jQuery Lite یا jqLite استفاده میکند. jqLite پیاده سازی توابع متداول jQuery برای دستکاری DOM میباشد. اطلاعات بیشتر در اینجا
بنابراین با استفاده تنها از KnockoutJS نمیتوان یک برنامهی کامل SPA توسعه داد ، در کنار آن نیاز به کتابخانههای دیگری مثل jQuery برای مدیریت درخواستهای AJAX و استفاده از دیگر APIها ، Sammy برای routing و RequireJS برای مدیریت وابستگیها میباشد.
در Knockout و در نتیجه Durandal عمل Databinding به این صورت است :
// JavaScript var vm = { firstName = ko.observable('John') }; ko.applyBindings(vm);
<!-- HTML --> <input data-bind="value:firstName"/>
در Angular :
// JavaScript // Inside of a personController this.firstName = 'John';
در Angular همچنین از یک روش Controller As استفاده میشود :
<!-- HTML --> <div ng-controller="personController as vm"> <input ng-model="vm.firstName"/> </div>
اگر تنها نیاز به یک کتابخانهی Databinding باشد ، Knockout گزینهی مناسبی است ، به خوبی از عمل مقید سازی دادهها پشتیبانی میکند و Syntax خوش دستی دارد اما اگر نیاز به چارچوبی برای توسعهی پروژههای SPA میباشد میتوان از Angular یا Durandal استفاده کرد.
مقایسهی Knockout با Angular همانند مقایسهی موتور بنز با ماشین پورشه میباشد.
در حین import ماژولهای TypeScript ایی پس از مدتی به یک چنین کدهایی خواهیم رسید:
در این حالت، در یک پوشه برای import ماژولی مشخص، چنین import ایی را خواهیم داشت:
و در پوشهی تو در توی دیگری، این تعریف به صورت زیر تغییر میکند:
و در آخر برنامه پر میشود از مسیرهای نسبی ‘../../../..’ مانند. به این ترتیب جابجا کردن فایلها و Refactoring آنها، مشکل میشود.
خوشبختانه کامپایلر TypeScript به همراه تنظیمات baseUrl و paths است که توسط آنها میتوان این مسیرهای نسبی را به مسیرهای مطلق تبدیل کرد و در این حالت اهمیتی ندارد که ماژول مدنظر از چه سطحی و درون چه پوشهی تو در تویی فراخوانی میشود، این مسیر import همواره ثابت خواهد بود.
تنظیمات فایل tsconfig.json برای معرفی مسیرهای مطلق ماژولها
فرض کنید میخواهید از یکی از سرویسهای Core Module استفاده کنید:
بسته به عمق پوشهی استفاده کننده، به یک چنین تعریفی خواهید رسید:
برای بهبود این وضعیت، فایل tsconfig.json و یا همان تنظیمات کامپایلر TypeScript را به نحو ذیل تکمیل میکنیم:
در اینجا baseUrl به پوشهی src برنامه اشاره میکند و مسیرهای بعدی بر این اساس محاسبه میشوند. در ادامه در قسمت paths، ابتدا یک نام مستعار ذکر میشود و سپس مسیری که ارائه دهندهی آن است. ذکر @ در اینجا اختیاری است؛ اما ذکر */ها اجباری است.
پس از این تغییرات، اکنون افزونهی پیشنهاد دهندهی imports، هر دو حالت استفادهی از مسیر مطلق بر اساس نام مستعار تعریف شده:
و یا استفادهی از مسیر نسبی را نیز پیشنهاد میدهد:
برای مثال اگر دقت کرده باشید، روش import اجزای خود Angular به صورت زیر است:
علت اینجا است که Angular از تعریف مشابهی به صورت زیر برای نگاشت پوشهی node_modules آن به angular@ استفاده میکند:
و ذکر @ اختیاری هم از اینجا اقتباس شدهاست.
یک نکتهی مهم: تنظیمات فوق بدون تنظیمات معادل webpack ناقص هستند
اگر از برنامهی Angular CLI استفاده میکنید، تنظیمات ذکر شده، تا همینجا به پایان میرسند؛ چون webpack جزئی از Angular CLI است و تنظیمات پیش فرض آن، قسمت baseUrl و paths فایل tsconfig.json را به صورت خودکار پردازش میکند. اما اگر از TypeScript در محیطهای دیگری استفاده میکنید که از webpack به صورت مجزایی استفاده میکنند، نیاز است قسمت resolve.alias فایل webpack.config.js را نیز جهت معرفی این تغییرات، اصلاح کنید. از این جهت که کامپایلر TypeScript این مسیرهای مطلق را در حین تولید فایلهای نهایی JavaScript ایی معادل، به مسیرهای نسبی بازنویسی نمیکند و در این حالت webpack نمیداند که چطور باید این ماژولها را یافته و یکی کند.
کوتاه کردن مسیرهای مطلق با معرفی فایل ویژهی index.ts
تا اینجا بجای ذکر مسیر
به مسیر مطلق زیر رسیدیم (صرفنظر از محل قرارگیری ماژولی که قرار است آنرا import کند):
این را هم میخواهیم به صورت زیر کوتاهتر کنیم:
یعنی فقط app/core@ را ذکر کنیم.
برای اینکار نیاز است فایل ویژهای را به نام index.ts، در ریشهی پوشهی core ایجاد کنیم (src\app\core\index.ts)، با این محتوا:
در اینجا تمام ماژولهایی که توسط Core Module ارائه میشوند را یکبار export میکنیم.
برای نمونه اگر به پوشهی node_modules\@angular خود مجموعهی Angular هم مراجعه کنید، هر پوشهی src آن به همراه یک فایل index.d.ts شبیه تعاریف فوق نیز هست.
پس از افزودن فایل index.ts به ریشهی پوشهی مدنظر، اکنون در لیست پیشنهادات، ذکر app/core@ تنها نیز ظاهر شده و استفادهی از آن مجاز است:
import { SpecialCollection } from "../../special"; import { LoginComponent } from "../login"; import { TextUtils } from ".../../utils/text"; import { Router } from "../../../core/router";
import { Data } from '../data';
import { Data } from '../../../data';
خوشبختانه کامپایلر TypeScript به همراه تنظیمات baseUrl و paths است که توسط آنها میتوان این مسیرهای نسبی را به مسیرهای مطلق تبدیل کرد و در این حالت اهمیتی ندارد که ماژول مدنظر از چه سطحی و درون چه پوشهی تو در تویی فراخوانی میشود، این مسیر import همواره ثابت خواهد بود.
تنظیمات فایل tsconfig.json برای معرفی مسیرهای مطلق ماژولها
فرض کنید میخواهید از یکی از سرویسهای Core Module استفاده کنید:
بسته به عمق پوشهی استفاده کننده، به یک چنین تعریفی خواهید رسید:
import { BrowserStorageService } from "./../../core/browser-storage.service";
{ "compilerOptions": { "baseUrl": "src", "paths": { "@app/*": [ "app/*" ], "@app/core/*": [ "app/core/*" ], "@app/shared/*": [ "app/shared/*" ], "@env/*": [ "environments/*" ] } } }
پس از این تغییرات، اکنون افزونهی پیشنهاد دهندهی imports، هر دو حالت استفادهی از مسیر مطلق بر اساس نام مستعار تعریف شده:
import { BrowserStorageService } from "@app/core/browser-storage.service";
import { BrowserStorageService } from "./../../core/browser-storage.service";
برای مثال اگر دقت کرده باشید، روش import اجزای خود Angular به صورت زیر است:
import { Component } from '@angular/core';
"paths": { "@angular/*": ["node_modules/@angular/*"] },
یک نکتهی مهم: تنظیمات فوق بدون تنظیمات معادل webpack ناقص هستند
اگر از برنامهی Angular CLI استفاده میکنید، تنظیمات ذکر شده، تا همینجا به پایان میرسند؛ چون webpack جزئی از Angular CLI است و تنظیمات پیش فرض آن، قسمت baseUrl و paths فایل tsconfig.json را به صورت خودکار پردازش میکند. اما اگر از TypeScript در محیطهای دیگری استفاده میکنید که از webpack به صورت مجزایی استفاده میکنند، نیاز است قسمت resolve.alias فایل webpack.config.js را نیز جهت معرفی این تغییرات، اصلاح کنید. از این جهت که کامپایلر TypeScript این مسیرهای مطلق را در حین تولید فایلهای نهایی JavaScript ایی معادل، به مسیرهای نسبی بازنویسی نمیکند و در این حالت webpack نمیداند که چطور باید این ماژولها را یافته و یکی کند.
resolve: { extensions: ['*', '.js', '.ts'], modules: [ rootDir, path.join(rootDir, 'node_modules') ], alias: { '@app': 'src/app' } },
کوتاه کردن مسیرهای مطلق با معرفی فایل ویژهی index.ts
تا اینجا بجای ذکر مسیر
import { BrowserStorageService } from "./../../core/browser-storage.service";
import { BrowserStorageService } from "@app/core/browser-storage.service";
import { BrowserStorageService } from "@app/core";
برای اینکار نیاز است فایل ویژهای را به نام index.ts، در ریشهی پوشهی core ایجاد کنیم (src\app\core\index.ts)، با این محتوا:
export * from "./browser-storage.service"; export * from "./app-config.service"; export * from "./seo-service";
برای نمونه اگر به پوشهی node_modules\@angular خود مجموعهی Angular هم مراجعه کنید، هر پوشهی src آن به همراه یک فایل index.d.ts شبیه تعاریف فوق نیز هست.
پس از افزودن فایل index.ts به ریشهی پوشهی مدنظر، اکنون در لیست پیشنهادات، ذکر app/core@ تنها نیز ظاهر شده و استفادهی از آن مجاز است:
میخواهیم زمان نمایش اولین قالب برنامه را به حداقل برسانیم تا تاثیر روانی بهتری را بر روی کاربرانی که برنامه را اجرا میکنند، بگذاریم. برای این منظور در Angular، از Lazy loading استفاده میشود. همچنین این فریم ورک به همراه قابلیت پیش بارگذاری ماژولها نیز هست تا سایر مسیرهای درخواستی را نیز با سرعت هرچه تمامتر نمایش دهد.
زمانیکه کاربری برنامهی تک صفحهای وب را در مرورگر باز میکند، ابتدا فایل index.html را در پاسخ دریافت خواهد کرد. این فایل تعاریف مداخل مورد نیاز برای رندر آنرا مانند فایلهای جاوا اسکریپت و CSS، به همراه دارد. سپس این فایلها توسط مرورگر از سرور دریافت میشوند. در این حالت با پردازش این فایلها، کامپوننت ریشهی سایت بارگذاری میشود. پس از پایان آن، قالب این کامپوننت به کاربر نمایش داده خواهد شد. بنابر سرعت دریافت فایلها توسط کاربر، این آغاز میتواند اندکی کند باشد. البته با رعایت نکات گفتهی شدهی در مطلب «Angular CLI - قسمت پنجم - ساخت و توزیع برنامه» میتوان این حجم را توسط AoT و Tree-Shaking به میزان قابل ملاحظهای کاهش داد. به علاوه با فعالسازی Lazy loading میتوان قسمتهای مختلف برنامه را تبدیل به یک سری Bundle کرد که در زمان درخواست، بارگذاری میشوند. به این ترتیب حجم فایلهای ابتدایی که باید از سرور دریافت شوند بسیار کمتر شده و به علاوه با کاهش این حجم، مرورگر نیز باید میزان کمتری از کدها را در جهت نمایش اولین کامپوننت، پردازش و اجرا کند. در این حالت زمانیکه کاربری شروع به پیمایش مسیر یک ماژول خاص را میکند، آنگاه فایلهای مرتبط با آن از سرور دریافت و در مرورگر پردازش میشوند. بنابراین اگر کاربری به قسمتی دسترسی ندارد، نیازی هم به دریافت فایلهای آن نخواهد داشت؛ چون کار به فعالسازی مسیریابی آن ماژول نمیرسد.
آماده شدن جهت Lazy loading
پیش از Lazy loading یک قسمت از برنامه (که به آن async routing هم میگویند)، این قسمت باید دارای شرایطی باشد:
- این قسمت از برنامه حتما باید در یک ماژول تعریف شده باشد. از این جهت که Lazy loading، لیست کامپوننتهای قید شدهی در تعریف یک ماژول را بارگذاری میکند.
- تمام مسیرهای این ماژول باید در ذیل یک مسیر والد، گروه بندی شده باشند. از این جهت که Lazy loading فقط بر روی مسیر ریشهی والد تنظیم و بارگذاری میشود.
- این ماژول نباید در هیچ ماژول دیگری import شده باشد. اگر این ماژول ارجاعی را در سایر ماژولها داشته باشد، هیچ راهی بجز دریافت و کامپایل کامل آن توسط Angular وجود نخواهد داشت.
در مثال جاری این سری:
- تمام ویژگیهای قسمت مدیریت محصولات، داخل ماژول product.module.ts تعریف شدهاند. بنابراین اولین شرط Lazy loading آن برقرار است.
- در فایل product-routing.module.ts، کار گروه بندی مسیریابیها ذیل یک والد مشخص انجام شدهاست (همان قسمت ششم این سری). بنابراین شرط دوم lazy loading این ماژول نیز پیشتر پیاده سازی شدهاست.
- اما اگر به فایل src\app\app.module.ts مراجعه کنیم، ارجاعی به این ماژول در قسمت imports آن وجود دارد. بنابراین باید این ارجاع را حذف کنیم. در غیراینصورت کار دریافت کامل آن به همراه سایر ماژولهای برنامه، در همان ابتدای کار صورت خواهد گرفت.
بنابراین در فایل src\app\app.module.ts، ابتدا import فایل آنرا از ابتدای ماژول حذف و سپس ارجاع به نام کلاس کامپوننت ProductModule را نیز حذف میکنیم. در این حالت اگر از طریق منوی سایت سعی در دسترسی به این مسیرها کنیم، خطای 404 را دریافت خواهیم کرد؛ چون اکنون برنامه اطلاعاتی را در مورد نحوهی مسیریابی قسمت محصولات برنامه، ندارد.
Lazy loading یک ماژول
برای بارگذاری غیرهمزمان یک ماژول و یا همان Lazy loading، میتوان از خاصیت loadChildren تنظیمات مسیریابی، استفاده کرد:
مقدار خاصیت loadChildren به صورت ذکر مسیر ماژول مرتبط به همراه یک # و سپس ذکر نام کلاس ماژول آن انجام میشود. مسیری هم که در اینجا ذکر میشود بر اساس محل قرارگیری فایل index.html، مقدار دهی شود.
با این تنظیم، زمانیکه مسیر ریشهی produtcs درخواست شد، کار بارگذاری ماژول آن صورت گرفته و تنظیمات مسیریابی آن به سیستم اضافه میشود. به علاوه کار فعالسازی و نمایش کامپوننت آن را نیز انجام خواهد داد.
به همین منظور فایل src\app\app-routing.module.ts را گشوده و تنظیم فوق را به آن اضافه میکنیم:
در این حالت اگر دستور ng serve -o را صادر کنید، خروجی آن اندکی متفاوت خواهد بود:
مورد {5} با فعالسازی lazy loading به لیست فایلهای موجود اضافه شدهاست. این فایلی است که تنها درصورت درخواست مسیر نمایش لیست محصولات، توسط مرورگر دریافت خواهد شد و هیچ ارجاع مستقیمی به آن در فایل index.html تولیدی نهایی وجود ندارد.
به علاوه اگر در منوی سایت بر روی لینک نمایش لیست محصولات کلیک کنیم، هنوز خروجی نمایش داده نمیشود (هرچند خطای 404 را هم دریافت نمیکنیم). علت اینجا است که اگر به فایل src\app\product\product-routing.module.ts مراجعه کنیم، تعریف این مسیر ریشه، در این فایل نیز وجود دارد:
بنابراین اکنون برای دسترسی به آن باید مسیر products/products را درخواست داد. به همین جهت، path و canActivate آنرا حذف کرده و هر دو را به فایل src\app\app-routing.module.ts منتقل میکنیم:
یک نکته: اکنون تنظیمات مسیریابی فایل src\app\product\product-routing.module.ts چنین شکلی را پیدا کردهاست:
تنظیمات مسیر والد به طور کامل حذف شدهاند. به علاوه دیگر نیازی به ذکر خاصیت children آن نیست و تمام تنظیمات مسیریابی فرزندان، داخل [] اصلی قرار گرفتهاند. همچنین دیگر نیازی به الحاق AuthGuard در ابتدای importهای این ماژول نیست؛ چون به فایل src\app\app-routing.module.ts منتقل شدهاست.
در این حالت اگر مسیر نمایش لیست محصولات را درخواست دهیم، مشاهده خواهیم کرد فایل 5.chunk.js که حاوی اطلاعات این ماژول است، به صورت مجزایی بارگذاری شده (lazy loading) و سپس با فعال شدن محافظ مسیر آن، صفحهی لاگین نمایش داده میشود:
این بارگذاری با تاخیر و در صورت نیاز، به دو علت آغاز برنامه را سریعتر میکند:
الف) مرورگر اطلاعی از وجود فایل 5.chunk.js در ابتدای کار نداشته و آنرا بارگذاری نمیکند (دریافت حجم کمتر، در آغاز نمایش برنامه).
ب) چون حجم کمتری از کدهای جاوا اسکریپت توسط مرورگر در آغاز کار دریافت میشود، کار پردازش و اجرای آنها نیز بسیار سریعتر خواهد شد.
بررسی محافظ canLoad
تعدادی از محافظهای مسیرها را در قسمت قبل بررسی کردیم. هنگامیکه کامپوننتها به صورت lazy loading فعالسازی شده و قالب آنها نمایش داده میشوند، میتوان از محافظ مسیر دیگری به نام canLoad نیز استفاده کرد و هدف از آن، بررسی منطقی، پیش از فعالسازی یک مسیر غیرهمزمان است. بنابراین اگر این محافظ false را برگرداند، حتی فایلهای اسکریپت این ماژول، بارگذاری اولیه نیز نخواهد شد. به این ترتیب کسانیکه دسترسی به یک مسیر را نداشته باشند، فایلهای اسکریپت متناظر با آنرا نیز دریافت نخواهند کرد.
در مثال جاری، اگر به برگهی network ابزار developer مرورگر دقت کنید، با درخواست نمایش مسیر لیست محصولات، ابتدا فایل js آن دریافت میشود که حاوی اطلاعات تمام کامپوننتها و قالبهای مرتبط با این مسیر است و سپس صفحهی login نمایش داده خواهد شد. بنابراین اگر کاربر به این قسمت دسترسی نداشته باشد، فایل js آن بیجهت دریافت و بارگذاری شدهاست. برای بهبود این وضعیت میتوان نمایش لاگین را پیش از بارگذاری فایل js این ماژول فعالسازی کرد و این مورد هدف اصلی محافظ canLoad است.
در ادامه برای تکمیل مثال جاری، میتوان AuthGuard را طوری تنظیم کرد که علاوه بر پیاده سازی CanActivate، اینترفیس CanLoad را نیز پیاده سازی کند:
همانطور که ملاحظه میکنید، متد canLoad بر خلاف متد canActivate دسترسی به سرویسهای اطلاعات مسیریابی و وضعیت مسیریابی را ندارد؛ از این جهت که هنوز در این مرحله، ماژول درخواستی حاوی تنظیمات مسیریابی، بارگذاری و فعالسازی نشدهاست.
مرحلهی بعد، تغییر فایل src\app\app-routing.module.ts و جایگزین کردن تعریف فعلی canActivate با canLoad است:
پس از این تغییر، برنامه را مجدا اجرا کرده و صفحه را refresh کنید. سپس برگهی network ابزار developers را نیز باز نگه دارید. اکنون بر روی لینک نمایش لیست محصولات کلیک کنید. مشاهده خواهید کرد که در این حالت صفحهی لاگین، بدون بارگذاری ماژول Js ایی نمایش داده میشود. در ادامه اگر لاگین کنیم، آنگاه فایل js این ماژول توسط مرورگر دریافت شده و بارگذاری میشود.
پیش بارگذاری ماژولها
با فعالسازی lazy loading، ماژولهای مورد نیاز کاربر دیگر به همراه فایلهای js ابتدایی برنامه که در فایل index.html ارجاع مستقیمی به آنها دارند، ارائه نمیشوند و تنها در صورت درخواست مشاهدهی مسیری، کار بارگذاری آنها توسط برنامه صورت خواهد گرفت. همین مساله میتواند در بار اول نمایش این ماژولها تاخیر کوتاهی را سبب شود. به همین جهت قابلیت پیش بارگذاری ماژولها نیز در سیستم مسیریاب Angular پیش بینی شدهاست. به این قابلیت preloading و یا eager lazy loading نیز میگویند. در این حالت برنامه در پشت صحنه، کار پیش واکشی ماژولها را انجام میدهد و زمانیکه کاربری مسیری را درخواست میدهد، آْن مسیر را بدون درنگ مشاهده خواهد کرد.
بدیهی است این قابلیت نباید برای ماژولهایی که قرار است توسط کاربرانی خاص مشاهده شوند فعال شود و هدف آن دسترسی سریع به ماژولهای پرکاربرد برنامهاست.
در اینجا سه استراتژی پیش بارگذاری ماژولها میسر است:
- No preloading که حالت پیش فرض است.
- Preload all سبب پیش بارگذاری تمام قسمتهای lazy load برنامه میشود.
- Custom که اجازهی تعریف یک استراتژی سفارشی را میدهد.
برای مثال برای فعالسازی حالت Preload all، باید به فایل src\app\app-routing.module.ts مراجعه کرده و تغییرات ذیل را اعمال کنیم:
در اینجا نحوهی تنظیم preloadingStrategy را به PreloadAllModules مشاهده میکنید. در این حالت پس از آغاز ابتدایی برنامه، مسیریاب بلافاصله تمام مسیرهای lazy load را در پشت صحنه بارگذاری میکند.
یک نکته: وجود محافظ canLoad، هر نوع استراتژی prealoading را غیرفعال میکند. اما prealoading با سایر انواع محافظها کار میکند.
بنابراین برای آزمایش تنظیم preloadingStrategy: PreloadAllModules، تعریف canLoad را به canActivate تغییر دهید.
تعریف استراتژیهای سفارشی پیش بارگذاری ماژولها
اگر نیاز به یک استراتژی پیش بارگذاری بهتر از هیچ یا همه باشد، میتوان یک استراتژی سفارشی را نیز تدارک دید و ایجاد آن سه مرحلهی ایجاد سرویس مرتبط، ثبت آن سرویس در ماژول و در آخر تنظیم مسیریابی را به همراه دارد.
برای این منظور ابتدا دستور ذیل را صادر کنید تا قالب ابتدایی سرویس SelectiveStrategy ایجاد شود:
که سبب تولید و به روز رسانی فایلهای ذیل در پوشهی src\app خواهد شد (چون مرتبط است به کل برنامه):
در این حالت لیست providers فایل app.module.ts نیز به صورت خودکار تکمیل میگردد.
سپس کدهای SelectiveStrategyService را به نحو ذیل تغییر دهید:
- این سرویس ویژه باید اینترفیس PreloadingStrategy را پیاده سازی کند. سپس باید متد اجباری preload آنرا افزود و تکمیل نمود.
- پارامتر اول این متد، اطلاعاتی را در مورد مسیر جاری در اختیار ما قرار میدهد و دومین پارامتر آن متدی است که کار preloading را انجام میدهد.
- در اینجا است که تصمیم میگیریم ماژولی را preload کنیم یا خیر. برای نمونه در اینجا از خاصیت data مسیریابی استفاده شدهاست. این خاصیت نیز به یک مقدار ثابت اشاره میکند (قسمت «ارسال اطلاعات ثابت به مسیرهای مختلف برنامه» قسمت چهارم). برای مثال نام دلخواه آنرا preload گذاشتهایم و اگر مقدار آن به true تنظیم شده بود، آنگاه این مسیر preload خواهد شد. فراخوانی متد load در اینجا به معنای preloading این مسیر است. در غیراینصورت null را بازگشت میدهیم.
در ادامه نیاز است در فایل src\app\app-routing.module.ts، بجای معرفی PreloadAllModules، این استراتژی سفارشی خود را معرفی کرد:
و همچنین تعریف مسیریابی برنامه به این صورت تغییر میکند:
در اینجا نحوهی مقدار دهی خاصیت data را به اطلاعات ثابت preload: true مشاهده میکنید. این اطلاعاتی است که در سرویس SelectiveStrategy سفارشی ما بررسی شده و بر اساس آن در مورد پیش بارگذاری این مسیر تصمیمگیری میشود.
برای آزمایش آن، برنامه را مجدا اجرا کرده و صفحه را refresh کنید. سپس برگهی network ابزار developers را نیز باز نگه دارید. مشاهده خواهید کرد که علاوه بر فایلهای js اصلی برنامه که در فایل index.html ارجاعی را دارند، فایل 5.chunk.js نیز پیش بارگذاری شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-09.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
زمانیکه کاربری برنامهی تک صفحهای وب را در مرورگر باز میکند، ابتدا فایل index.html را در پاسخ دریافت خواهد کرد. این فایل تعاریف مداخل مورد نیاز برای رندر آنرا مانند فایلهای جاوا اسکریپت و CSS، به همراه دارد. سپس این فایلها توسط مرورگر از سرور دریافت میشوند. در این حالت با پردازش این فایلها، کامپوننت ریشهی سایت بارگذاری میشود. پس از پایان آن، قالب این کامپوننت به کاربر نمایش داده خواهد شد. بنابر سرعت دریافت فایلها توسط کاربر، این آغاز میتواند اندکی کند باشد. البته با رعایت نکات گفتهی شدهی در مطلب «Angular CLI - قسمت پنجم - ساخت و توزیع برنامه» میتوان این حجم را توسط AoT و Tree-Shaking به میزان قابل ملاحظهای کاهش داد. به علاوه با فعالسازی Lazy loading میتوان قسمتهای مختلف برنامه را تبدیل به یک سری Bundle کرد که در زمان درخواست، بارگذاری میشوند. به این ترتیب حجم فایلهای ابتدایی که باید از سرور دریافت شوند بسیار کمتر شده و به علاوه با کاهش این حجم، مرورگر نیز باید میزان کمتری از کدها را در جهت نمایش اولین کامپوننت، پردازش و اجرا کند. در این حالت زمانیکه کاربری شروع به پیمایش مسیر یک ماژول خاص را میکند، آنگاه فایلهای مرتبط با آن از سرور دریافت و در مرورگر پردازش میشوند. بنابراین اگر کاربری به قسمتی دسترسی ندارد، نیازی هم به دریافت فایلهای آن نخواهد داشت؛ چون کار به فعالسازی مسیریابی آن ماژول نمیرسد.
آماده شدن جهت Lazy loading
پیش از Lazy loading یک قسمت از برنامه (که به آن async routing هم میگویند)، این قسمت باید دارای شرایطی باشد:
- این قسمت از برنامه حتما باید در یک ماژول تعریف شده باشد. از این جهت که Lazy loading، لیست کامپوننتهای قید شدهی در تعریف یک ماژول را بارگذاری میکند.
- تمام مسیرهای این ماژول باید در ذیل یک مسیر والد، گروه بندی شده باشند. از این جهت که Lazy loading فقط بر روی مسیر ریشهی والد تنظیم و بارگذاری میشود.
- این ماژول نباید در هیچ ماژول دیگری import شده باشد. اگر این ماژول ارجاعی را در سایر ماژولها داشته باشد، هیچ راهی بجز دریافت و کامپایل کامل آن توسط Angular وجود نخواهد داشت.
در مثال جاری این سری:
- تمام ویژگیهای قسمت مدیریت محصولات، داخل ماژول product.module.ts تعریف شدهاند. بنابراین اولین شرط Lazy loading آن برقرار است.
- در فایل product-routing.module.ts، کار گروه بندی مسیریابیها ذیل یک والد مشخص انجام شدهاست (همان قسمت ششم این سری). بنابراین شرط دوم lazy loading این ماژول نیز پیشتر پیاده سازی شدهاست.
- اما اگر به فایل src\app\app.module.ts مراجعه کنیم، ارجاعی به این ماژول در قسمت imports آن وجود دارد. بنابراین باید این ارجاع را حذف کنیم. در غیراینصورت کار دریافت کامل آن به همراه سایر ماژولهای برنامه، در همان ابتدای کار صورت خواهد گرفت.
بنابراین در فایل src\app\app.module.ts، ابتدا import فایل آنرا از ابتدای ماژول حذف و سپس ارجاع به نام کلاس کامپوننت ProductModule را نیز حذف میکنیم. در این حالت اگر از طریق منوی سایت سعی در دسترسی به این مسیرها کنیم، خطای 404 را دریافت خواهیم کرد؛ چون اکنون برنامه اطلاعاتی را در مورد نحوهی مسیریابی قسمت محصولات برنامه، ندارد.
Lazy loading یک ماژول
برای بارگذاری غیرهمزمان یک ماژول و یا همان Lazy loading، میتوان از خاصیت loadChildren تنظیمات مسیریابی، استفاده کرد:
{ path: 'products', loadChildren:'app/product/product.module#ProductModule' },
با این تنظیم، زمانیکه مسیر ریشهی produtcs درخواست شد، کار بارگذاری ماژول آن صورت گرفته و تنظیمات مسیریابی آن به سیستم اضافه میشود. به علاوه کار فعالسازی و نمایش کامپوننت آن را نیز انجام خواهد داد.
به همین منظور فایل src\app\app-routing.module.ts را گشوده و تنظیم فوق را به آن اضافه میکنیم:
const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: 'products', loadChildren: 'app/product/product.module#ProductModule' }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 165 kB {4} [initial] chunk {1} main.bundle.js, main.bundle.js.map (main) 32.7 kB {3} [initial] [rendered] chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 129 kB {4} [initial] chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.72 MB [initial] [rendered] chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] chunk {5} 5.chunk.js, 5.chunk.js.map 51.1 kB {1} [rendered]
به علاوه اگر در منوی سایت بر روی لینک نمایش لیست محصولات کلیک کنیم، هنوز خروجی نمایش داده نمیشود (هرچند خطای 404 را هم دریافت نمیکنیم). علت اینجا است که اگر به فایل src\app\product\product-routing.module.ts مراجعه کنیم، تعریف این مسیر ریشه، در این فایل نیز وجود دارد:
const routes: Routes = [ { path: 'products', canActivate: [ AuthGuard ], children: [ ] } ];
import { AuthGuard } from './user/auth.guard'; const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: 'products', loadChildren: 'app/product/product.module#ProductModule', canActivate: [AuthGuard] }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
یک نکته: اکنون تنظیمات مسیریابی فایل src\app\product\product-routing.module.ts چنین شکلی را پیدا کردهاست:
const routes: Routes = [ { path: '', component: ProductListComponent }, { path: ':id', component: ProductDetailComponent, resolve: { product: ProductResolverService } }, { path: ':id/edit', component: ProductEditComponent, resolve: { product: ProductResolverService }, canDeactivate: [ProductEditGuard], children: [ { path: '', redirectTo: 'info', pathMatch: 'full' }, { path: 'info', component: ProductEditInfoComponent }, { path: 'tags', component: ProductEditTagsComponent } ] } ];
در این حالت اگر مسیر نمایش لیست محصولات را درخواست دهیم، مشاهده خواهیم کرد فایل 5.chunk.js که حاوی اطلاعات این ماژول است، به صورت مجزایی بارگذاری شده (lazy loading) و سپس با فعال شدن محافظ مسیر آن، صفحهی لاگین نمایش داده میشود:
این بارگذاری با تاخیر و در صورت نیاز، به دو علت آغاز برنامه را سریعتر میکند:
الف) مرورگر اطلاعی از وجود فایل 5.chunk.js در ابتدای کار نداشته و آنرا بارگذاری نمیکند (دریافت حجم کمتر، در آغاز نمایش برنامه).
ب) چون حجم کمتری از کدهای جاوا اسکریپت توسط مرورگر در آغاز کار دریافت میشود، کار پردازش و اجرای آنها نیز بسیار سریعتر خواهد شد.
بررسی محافظ canLoad
تعدادی از محافظهای مسیرها را در قسمت قبل بررسی کردیم. هنگامیکه کامپوننتها به صورت lazy loading فعالسازی شده و قالب آنها نمایش داده میشوند، میتوان از محافظ مسیر دیگری به نام canLoad نیز استفاده کرد و هدف از آن، بررسی منطقی، پیش از فعالسازی یک مسیر غیرهمزمان است. بنابراین اگر این محافظ false را برگرداند، حتی فایلهای اسکریپت این ماژول، بارگذاری اولیه نیز نخواهد شد. به این ترتیب کسانیکه دسترسی به یک مسیر را نداشته باشند، فایلهای اسکریپت متناظر با آنرا نیز دریافت نخواهند کرد.
در مثال جاری، اگر به برگهی network ابزار developer مرورگر دقت کنید، با درخواست نمایش مسیر لیست محصولات، ابتدا فایل js آن دریافت میشود که حاوی اطلاعات تمام کامپوننتها و قالبهای مرتبط با این مسیر است و سپس صفحهی login نمایش داده خواهد شد. بنابراین اگر کاربر به این قسمت دسترسی نداشته باشد، فایل js آن بیجهت دریافت و بارگذاری شدهاست. برای بهبود این وضعیت میتوان نمایش لاگین را پیش از بارگذاری فایل js این ماژول فعالسازی کرد و این مورد هدف اصلی محافظ canLoad است.
در ادامه برای تکمیل مثال جاری، میتوان AuthGuard را طوری تنظیم کرد که علاوه بر پیاده سازی CanActivate، اینترفیس CanLoad را نیز پیاده سازی کند:
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router, CanLoad, Route } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate, CanLoad { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkLoggedIn(state.url); } canLoad(route: Route): boolean { return this.checkLoggedIn(route.path); } // … the same as before }
مرحلهی بعد، تغییر فایل src\app\app-routing.module.ts و جایگزین کردن تعریف فعلی canActivate با canLoad است:
{ path: 'products', loadChildren: 'app/product/product.module#ProductModule', canLoad: [AuthGuard] },
پیش بارگذاری ماژولها
با فعالسازی lazy loading، ماژولهای مورد نیاز کاربر دیگر به همراه فایلهای js ابتدایی برنامه که در فایل index.html ارجاع مستقیمی به آنها دارند، ارائه نمیشوند و تنها در صورت درخواست مشاهدهی مسیری، کار بارگذاری آنها توسط برنامه صورت خواهد گرفت. همین مساله میتواند در بار اول نمایش این ماژولها تاخیر کوتاهی را سبب شود. به همین جهت قابلیت پیش بارگذاری ماژولها نیز در سیستم مسیریاب Angular پیش بینی شدهاست. به این قابلیت preloading و یا eager lazy loading نیز میگویند. در این حالت برنامه در پشت صحنه، کار پیش واکشی ماژولها را انجام میدهد و زمانیکه کاربری مسیری را درخواست میدهد، آْن مسیر را بدون درنگ مشاهده خواهد کرد.
بدیهی است این قابلیت نباید برای ماژولهایی که قرار است توسط کاربرانی خاص مشاهده شوند فعال شود و هدف آن دسترسی سریع به ماژولهای پرکاربرد برنامهاست.
در اینجا سه استراتژی پیش بارگذاری ماژولها میسر است:
- No preloading که حالت پیش فرض است.
- Preload all سبب پیش بارگذاری تمام قسمتهای lazy load برنامه میشود.
- Custom که اجازهی تعریف یک استراتژی سفارشی را میدهد.
برای مثال برای فعالسازی حالت Preload all، باید به فایل src\app\app-routing.module.ts مراجعه کرده و تغییرات ذیل را اعمال کنیم:
import { Routes, RouterModule, PreloadAllModules } from '@angular/router'; @NgModule({ imports: [RouterModule.forRoot( routes, { enableTracing: true, preloadingStrategy: PreloadAllModules /*, useHash: true*/ } )],
یک نکته: وجود محافظ canLoad، هر نوع استراتژی prealoading را غیرفعال میکند. اما prealoading با سایر انواع محافظها کار میکند.
بنابراین برای آزمایش تنظیم preloadingStrategy: PreloadAllModules، تعریف canLoad را به canActivate تغییر دهید.
تعریف استراتژیهای سفارشی پیش بارگذاری ماژولها
اگر نیاز به یک استراتژی پیش بارگذاری بهتر از هیچ یا همه باشد، میتوان یک استراتژی سفارشی را نیز تدارک دید و ایجاد آن سه مرحلهی ایجاد سرویس مرتبط، ثبت آن سرویس در ماژول و در آخر تنظیم مسیریابی را به همراه دارد.
برای این منظور ابتدا دستور ذیل را صادر کنید تا قالب ابتدایی سرویس SelectiveStrategy ایجاد شود:
>ng g s SelectiveStrategy -m app.module
installing service create src\app\selective-strategy.service.spec.ts create src\app\selective-strategy.service.ts update src\app\app.module.ts
سپس کدهای SelectiveStrategyService را به نحو ذیل تغییر دهید:
import { Injectable } from '@angular/core'; import { Route, PreloadingStrategy } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; @Injectable() export class SelectiveStrategyService implements PreloadingStrategy { preload(route: Route, load: Function): Observable<any> { if (route.data && route.data['preload']) { return load(); } return Observable.of(null); } }
- پارامتر اول این متد، اطلاعاتی را در مورد مسیر جاری در اختیار ما قرار میدهد و دومین پارامتر آن متدی است که کار preloading را انجام میدهد.
- در اینجا است که تصمیم میگیریم ماژولی را preload کنیم یا خیر. برای نمونه در اینجا از خاصیت data مسیریابی استفاده شدهاست. این خاصیت نیز به یک مقدار ثابت اشاره میکند (قسمت «ارسال اطلاعات ثابت به مسیرهای مختلف برنامه» قسمت چهارم). برای مثال نام دلخواه آنرا preload گذاشتهایم و اگر مقدار آن به true تنظیم شده بود، آنگاه این مسیر preload خواهد شد. فراخوانی متد load در اینجا به معنای preloading این مسیر است. در غیراینصورت null را بازگشت میدهیم.
در ادامه نیاز است در فایل src\app\app-routing.module.ts، بجای معرفی PreloadAllModules، این استراتژی سفارشی خود را معرفی کرد:
import { SelectiveStrategyService } from './selective-strategy.service'; @NgModule({ imports: [RouterModule.forRoot( routes, { enableTracing: true, preloadingStrategy: SelectiveStrategyService /*, preloadingStrategy: PreloadAllModules*/ /*, useHash: true*/ } )],
{ path: 'products', loadChildren: 'app/product/product.module#ProductModule', //canLoad: [AuthGuard] canActivate: [AuthGuard], data: { preload: true } },
برای آزمایش آن، برنامه را مجدا اجرا کرده و صفحه را refresh کنید. سپس برگهی network ابزار developers را نیز باز نگه دارید. مشاهده خواهید کرد که علاوه بر فایلهای js اصلی برنامه که در فایل index.html ارجاعی را دارند، فایل 5.chunk.js نیز پیش بارگذاری شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-routing-lab-09.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.