آموزش بلیزور برای مبتدیان
ترجمهٔ صفحهٔ 1 از ۱۲۱بازگشت به فهرست
بلیزر: راهنمای مبتدیان
راهنمای شروع سریع برای بهرهوری با بلیزر (Blazor)
اِد شاربنو (Ed Charbeneau)
کتاب الکترونیکی
بازنمایی درونخطی صفحهٔ 1 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 2 از ۱۲۱بازگشت به فهرست
بلیزر: راهنمای مبتدیان
راهنمای شروع سریع برای بهرهوری با بلیزر
اِد شاربنو
بازنمایی درونخطی صفحهٔ 2 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 3 از ۱۲۱بازگشت به فهرست
تقدیمنامه
این کتاب به جامعهٔ .NET تقدیم میشود. بدون پشتیبانی شما، چیزی به نام «حمایت از توسعهدهندگان» (Developer Advocacy) وجود نداشت؛ عنوانی که سخت تلاش میکنم شایستهٔ آن باشم و اطمینان دهم محصولاتی که با آنها سروکار داریم، تا جای ممکن بهترین باشند.
بازنمایی درونخطی صفحهٔ 3 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 4 از ۱۲۱بازگشت به فهرست
حقنشر
کتاب «بلیزر: راهنمای مبتدیان» © ۲۰۲۰، اثر اِد شاربنو. تمامی حقوق محفوظ است.
تمامی حقوق محفوظ است. هیچ بخشی از این کتاب، بدون اجازهٔ کتبی نویسنده، نباید به هیچ شکل یا با هیچ وسیلهٔ الکترونیکی یا مکانیکی - از جمله سامانههای ذخیرهسازی و بازیابی اطلاعات - تکثیر شود. تنها استثنا، منتقدی است که میتواند در یک نقد، بخشهای کوتاهی از اثر را نقل کند.
طراحی جلد: Progress Software
تمام نامهای تجاری و نامهای محصولاتِ آمده در این کتاب، علامت تجاری، علامت تجاری ثبتشده یا نام تجاریِ صاحبان مربوطهاند. ما با هیچیک از محصولات یا فروشندگان مطرحشده در این کتاب وابستگی نداریم.
اِد شاربنو
وبسایت نویسنده: www.EdCharbeneau.com
تولیدشده توسط Progress Software
رابط کاربری Telerik برای بلیزر: www.telerik.com/blazor-ui
ویرایش نخست: مارس ۲۰۲۰
اِد شاربنو
فروش یا انتشار این ترجمه منوط به داشتن مجوز لازم از صاحب حقوق اثر است.
بازنمایی درونخطی صفحهٔ 4 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 5 از ۱۲۱بازگشت به فهرست
دربارهٔ این کتاب
کتاب «بلیزر: راهنمای مبتدیان» برای توسعهدهندگانی نوشته شده است که مقداری تجربهٔ .NET دارند. اگر پیشینهٔ توسعهٔ شما خارج از .NET است، ممکن است در طول مسیر برخی مبانی .NET را نیز بیاموزید؛ بااینحال، مطالعهٔ منابع تکمیلی دربارهٔ C# و .NET کمک زیادی به شما خواهد کرد.
کتاب با دیدگاه شخصی نویسنده دربارهٔ وباسمبلی (WebAssembly)، اهمیت برخورداری از حق انتخاب در توسعهٔ وب و نگاهی به علت شکلگیری بلیزر آغاز میشود. سپس وارد یک مرور کلی میشوید تا با چارچوب و پشتهٔ فناوری آشنا شوید. با پیشرفت کتاب، نمونههای عملی را خواهید دید که مفاهیم اصلی را در سطحی گسترده آموزش میدهند. در بخشهای بعدی، موضوعاتی که به دانش عمیقتری نیاز دارند دوباره بررسی میشوند تا جزئیات ظریف آنها را درک کنید و بتوانید دربارهٔ شیوهٔ ساخت برنامهها و مؤلفههایتان تصمیمهای آگاهانه بگیرید.
هدف از چیدمان کتاب این است که شما را هرچه سریعتر به بهرهوری برساند. هنگام مطالعهٔ فصلها باید بتوانید بهراحتی در پروژههای بلیزر حرکت کنید و پروژههای جدید بسازید؛ نه اینکه برای آغاز یک پروژه ناچار باشید ابتدا کل کتاب را به پایان برسانید.
بازنمایی درونخطی صفحهٔ 5 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 6 از ۱۲۱بازگشت به فهرست
پیشگفتار
در اجلاس Microsoft MVP (Most Valuable Professional) سال ۲۰۱۷، استیو سندرسون را دیدم که چارچوب آزمایشی خود را در اتاقی پر از توسعهدهندگان .NET معرفی کرد. دیدم که هیجان سراسر اتاق را فراگرفت و حاضران چند بار ایستاده تشویق کردند. این هیجان، نظر شخصی مرا تأیید کرد: این همان چیزی بود که توسعهدهندگان .NET مانند من همیشه برای وب میخواستند. این کتاب را نوشتم تا به جامعهٔ .NET کمک کنم آنچه بلیزر ارائه میدهد سریعتر درک کند و در هیجانی که همهٔ ما در اجلاس MVP احساس کردیم سهیم شود.
از آن زمان، نوشتههای وبلاگی زیادی دربارهٔ این موضوع منتشر کردهام که با هر انتشار ماهانهٔ بلیزر قدیمی میشدند. بهترینِ آن مطالب را انتخاب کردم، بازبینی و بهروزرسانی کردم و بخشهایی از آنها را بازنویسی نمودم؛ سپس آنها را همراه با مقدار زیادی محتوای کاملاً جدید در این کتاب قرار دادم. با گردآوری بهترین نوشتههایم در یک اثر واحد و در قالب کتاب الکترونیکی، میتوان این منبع یکپارچهٔ دانش را همزمان با انتشار نسخههای جدید بهروز نگه داشت.
بازنمایی درونخطی صفحهٔ 6 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 7 از ۱۲۱بازگشت به فهرست
وباسمبلی
شما اینجا هستید
توسعهٔ وب همواره مترادف با توسعهٔ جاوااسکریپت بوده است؛ دستکم تا امروز.
توسعهٔ وب همیشه با توسعهٔ جاوااسکریپت هممعنا بوده است. اما اکنون گونهٔ تازهای از توسعهٔ وب در حال شکلگیری است که نوید جایگزینی برای جاوااسکریپت را میدهد: وباسمبلی (WebAssembly). بهعنوان توسعهدهندهٔ نرمافزاری با سالها تجربه در توسعهٔ وب، این مسیر جدید توجه مرا به خود جلب کرده است.
وباسمبلی (wasm) یک قالب دستورالعمل دودویی برای مرورگرهای وب است که طراحی شده تا مقصد کامپایل زبانهای سطحبالا، مانند C++، باشد. در سال ۲۰۱۷، مایکروسافت آزمایش با وباسمبلی را آغاز کرد تا با استفاده از زماناجرای مونو (Mono runtime)، .NET را به مرورگر بیاورد.
بازنمایی درونخطی صفحهٔ 7 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 8 از ۱۲۱بازگشت به فهرست
مونو زیرساخت لازم را فراهم میکند تا کتابخانههای .NET با پسوند .dll روی وباسمبلی اجرا شوند. بلیزر روی مونو اجرا میشود؛ بلیزر یک چارچوب برنامهٔ وب تکصفحهای (Single-Page Application یا SPA) است که بر پایهٔ .NET ساخته شده است. پشتهٔ WebAssembly-Mono-Blazor این ظرفیت را دارد که بستری تمامپشته برای برنامههای .NET در اختیار توسعهدهندگان وب قرار دهد؛ بستری که نیازی به نوشتن جاوااسکریپت ندارد و علاوه بر آن، به نصب افزونهای در مرورگر کاربر وابسته نیست.
معرفی مفهوم «وبِ بدون جاوااسکریپت» بلافاصله پرسشهایی را مطرح میکند؛ و کاملاً هم بجاست.
وباسمبلی چه چیزی ارائه میدهد که جاوااسکریپت یا تایپاسکریپت ارائه نمیکنند؟
پاسخ من سرشار از جانبداری و نظر شخصی است و به گمانم باید هم چنین باشد؛ زیرا همهٔ توسعهدهندگان، پروژهها و ابزارها یکسان نیستند. برای من پاسخ روشن و کوتاه است: «انتخاب». گسترش توسعهٔ وب به فراتر از جاوااسکریپت یعنی حق انتخاب و آزادی برای انتخاب نهفقط جاوااسکریپت یا .NET، بلکه مجموعهای بسیار گستردهتر از گزینهها. دقیقتر و شخصیتر بگویم، من میتوانم یک برنامهٔ وب را با ابزارها و زبانهایی توسعه دهم که در بخشهای دیگر نیز از آنها استفاده میکنم.
جایگزینهایی برای npm و Webpack
بازنمایی درونخطی صفحهٔ 8 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 9 از ۱۲۱بازگشت به فهرست
یکی از مزایای گشودن وب به روی .NET این است که اکنون برای npm و Webpack جایگزین داریم. بهعنوان توسعهدهندهای قدیمی در .NET، با اشتیاق از مدیر بستهٔ NuGet و MSBuild استقبال میکنم. برای من این فناوریها دردسر کمتری دارند، آشناترند و بهرهوری بسیار بیشتری ایجاد میکنند. هیچ ابزاری بینقص نیست، اما تجربهٔ من با NuGet و MSBuild عمدتاً مثبت بوده است.
در نگاه اول شاید چنین برداشت شود که npm و Webpack بهنوعی ابزارهای بدی هستند و من توصیه میکنم آنها را کنار بگذاریم؛ اما واقعیت دقیقاً برعکس است. npm و Webpack ابزارهای بسیار خوبیاند و احتمالاً مدت زیادی باقی خواهند ماند. اگر ابزارهای جاوااسکریپتی شما برای خودتان و برنامههایی که میسازید خوب کار میکنند، این موضوع بسیار عالی است. بهدلیل سابقهٔ طولانیام در وب، میدانم چرا npm و Webpack به وجود آمدهاند و برای دستاوردهای گذشته و آیندهٔ آنها ارزش قائلم.
کاهش منحنی یادگیری
یکی از چیزهایی که دربارهٔ بلیزر مرا شگفتزده کرد، سادگی واقعی کار با آن است. بلیزر سهولت نحو نشانهگذاری Razor را با مفاهیم دیگر .NET ترکیب میکند. این چارچوب بهترین الگوها را از چارچوبهای محبوب جاوااسکریپت مانند Angular و React وام گرفته است، درعینحال از قالبهای Razor بهره میبرد و با قراردادهای دیگر .NET همترازی دارد. این ترکیب ویژگیها امکان استفادهٔ دوباره از مهارتها را به شکلی فراهم میکند که پیش از این در دسترس نبود. همین سخن دربارهٔ توسعهدهندگان Node نیز صدق میکند؛ کسانی که در برنامههای تمامپشتهٔ جاوااسکریپت از یک زبان و مفاهیم آشنا استفاده میکنند.
قابلیت تعامل
استفاده از وباسمبلی به این معنا نیست که میتوان جاوااسکریپت را بهطور کامل کنار گذاشت. در حال حاضر، وباسمبلی باید بهوسیلهٔ جاوااسکریپت بارگذاری شود. (بله، صدای خشخش ناگهانی صفحهٔ گرامافون را میشنوم!)
بازنمایی درونخطی صفحهٔ 9 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 10 از ۱۲۱بازگشت به فهرست
ضرورت جاوااسکریپت به همینجا ختم نمیشود. وباسمبلی به هیچیک از APIهای پلتفرم دسترسی ندارد و برای دسترسی به APIهای پلتفرم، جاوااسکریپت لازم است.
در حال حاضر، وباسمبلی باید بهوسیلهٔ جاوااسکریپت بارگذاری شود. (بله، صدای خشخش ناگهانی صفحهٔ گرامافون را میشنوم!)
برنامههای وباسمبلی میتوانند توابع جاوااسکریپت را فراخوانی کنند و به این ترتیب، برای APIهایی که خارج از دسترس وباسمبلی خالص هستند مسیر مهاجرتی فراهم میشود. این قابلیت در چارچوب بلیزر نیز به کار میرود. چون بلیزر جدید است، قابلیت تعامل بلیزر (Blazor interop) به توسعهدهندگان اجازه میدهد هرجا خود وباسمبلی محدودیت دارد یا چارچوب بلیزر دسترسی لازم را فراهم نمیکند، به جاوااسکریپت بازگردند. نمودار بلوکی پشتهٔ برنامه در شکل ۱ نمایش داده شده است.
لایهٔ تعامل، یک لایهٔ انتزاعی است که بسیاری از توسعهدهندگان در C# با آن کار میکنند و لازم نیست نگران باشند که فناوری زیرین همچنان کد جاوااسکریپت اجرا میکند. با بلوغ وباسمبلی، نیاز به این انتزاعها بهمرور کاهش خواهد یافت. جزئیات استفاده از قابلیت تعامل با جاوااسکریپت، همراه با نمونههای کامل، در بخشهای بعدی کتاب بررسی میشود.
بازنمایی درونخطی صفحهٔ 10 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 11 از ۱۲۱بازگشت به فهرست
شکل ۱: نمودار بلوکی چارچوب بلیزر و مرورگر وب.
ادامهٔ مسیر
اگر توسعهٔ وب با استفاده از جایگزینهای جاوااسکریپت برایتان جذاب است، صرف زمان برای وباسمبلی و چارچوبهایی مانند بلیزر ارزشمند خواهد بود. هنوز در روزهای آغازین وباسمبلی و فناوریهای مبتنی بر آن قرار داریم، اما نوید شکلگیری زیستبومی گستردهتر توجه مرا جلب کرده است. بهعنوان یکی از علاقهمندان جدی توسعهٔ وب، دوست دارم این حوزه رو به جلو حرکت کند و دیدگاه ما دربارهٔ شیوهٔ نوشتن برنامهها برای این پلتفرم را گسترش دهد.
چشمانداز استفاده از سالها تجربهٔ .NET برای ساخت برنامهها به روشی که بهرهوری مرا افزایش دهد، دستکم بسیار هیجانانگیز است. در کنار آن، پایهٔ محکمی از مهارتهای جاوااسکریپت نیز ساختهام و هر روز آن را توسعه میدهم. این تنوع مهارتها، دیدگاههای گوناگون و روشهای منحصربهفردی برای حل مسائل مهندسی در اختیارم میگذارد.
بازنمایی درونخطی صفحهٔ 11 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 12 از ۱۲۱بازگشت به فهرست
آشنایی با بلیزر
در فصل پیش با دیدگاه نویسنده دربارهٔ اهمیت بلیزر برای زیستبوم توسعهٔ وب آشنا شدیم. در این فصل تمرکز خود را به یادگیری مفاهیم کلی چارچوب بلیزر و انتخابهایی که در اختیار ما قرار میدهد معطوف میکنیم.
بلیزر، چارچوب وب جدیدی است که وضعیت موجود را به چالش میکشد و درعین قدرتمندبودن، بهرهوری بالایی نیز دارد. بلیزر از معماری .NET Core استفاده میکند تا الگوها و شیوههای رایج را در سراسر پشته به کار گیرد. این چارچوب با پشتیبانی از مدل میزبانی سمت کاربر و سمت سرور، از قابلیت .NET Core برای اجرا در هر محیطی بهره میبرد.
این دوگانگی در بلیزر، حق انتخاب ایجاد میکند و درعینحال آنقدر انعطافپذیر است که بتوان بهآسانی بین حالتهای میزبانی جابهجا شد. بلیزر سادگی Razor را با مفاهیم دیگر .NET Core مانند تزریق وابستگی (Dependency Injection)، پیکربندی و مسیریابی ترکیب میکند. این چارچوب بهترین الگوها را از چارچوبهای محبوب جاوااسکریپت مانند Angular و React وام گرفته، از قالبهای Razor بهره برده و با قراردادها و ابزارهای دیگر .NET همترازی ایجاد کرده است.
بازنمایی درونخطی صفحهٔ 12 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 13 از ۱۲۱بازگشت به فهرست
مدل مؤلفهٔ Razor
کار با مدل مؤلفهٔ Razor (Razor Component) برای کسانی که با ASP.NET MVC یا Razor Pages کار کردهاند بسیار آشنا خواهد بود. مؤلفههای Razor، نحو Razor را با روشی جدید برای کپسولهکردن نشانهگذاری در مؤلفههای قابلاستفادهٔ مجدد ترکیب میکنند. با مؤلفههای Razor میتوانید بهسرعت بلوکهای سازندهٔ رابط کاربری (User Interface یا UI) برنامهتان را ایجاد کنید.
مؤلفهها کلاسهای .NET هستند که ویژگیهای آنها بهعنوان پارامترهای مؤلفه عمل میکنند؛ همین سامانهٔ ساده، نوشتن مؤلفههای Razor را آسان میسازد. با تقسیم مدل مؤلفه به سه مفهومِ «دستورها»، «نشانهگذاری» و «کد»، میتوانیم نحوهٔ ساختهشدن آنها را درک کنیم.
گرچه عبارتهای Razor Component و Blazor Component بهطور گسترده بهجای یکدیگر استفاده میشوند، اصطلاح دقیقتر Razor Component است. این نکته هنگام جستوجو در اینترنت اهمیت دارد، زیرا هر دو عبارت بسیار رایجاند. بااینحال، در سراسر این کتاب از مؤلفهها با عنوان «مؤلفههای Razor» یاد میکنیم.
شکل ۲: تجزیهٔ ساختار یک مؤلفهٔ Razor؛ بنیادیترین بلوک سازنده در یک برنامهٔ بلیزر.
در شکل ۲، مؤلفهها از دستورها (Directives) برای افزودن قابلیتهای ویژهای مانند مسیریابی یا تزریق وابستگی استفاده میکنند. نحو دستورها شبیه چیزی است که در ASP.NET MVC یا Razor Pages به کار میرود. نشانهگذاری مؤلفه عمدتاً HTML است که با نحو Razor غنیتر میشود. نحو Razor اجازه میدهد C# بهصورت درونخطی در کنار نشانهگذاری استفاده شود و مقادیر را در رابط کاربری نمایش دهد.
منطق مؤلفه درون یک بلوک @code نوشته میشود. پارامترهای مؤلفه و مقادیر متصل به داده در همین بخش تعریف میشوند. در روشی جایگزین، میتوان کد را با رویکرد کدِ پشتصحنه (Code-Behind)، شبیه ASP.NET WebForms، ارجاع داد. این رویکرد را در فصلی بعد بررسی میکنیم.
بازنمایی درونخطی صفحهٔ 13 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 14 از ۱۲۱بازگشت به فهرست
فعلاً کافی است بدانیم چنین گزینهای وجود دارد. مدل مؤلفهٔ Razor به ما اجازه میدهد رابط کاربری را به بخشهای قابلکنترل تقسیم کنیم. سپس میتوان مؤلفهها را کنار هم قرار داد و رابطهای کاربری بزرگتر، مؤلفههای پیچیدهتر و صفحهها را ساخت.
داستان دو بلیزر
بلیزر در آغاز بهعنوان یک چارچوب برنامهٔ تکصفحهای (SPA) سمت کاربر شکل گرفت.
هدف بلیزر این است که برنامههای .NET با استفاده از وباسمبلی و نحو Razor در مرورگر اجرا شوند. نام Blazor نیز از ترکیب browser + Razor به وجود آمده است. در طول توسعه، تیم ASP.NET یک مدل میزبانی سمت سرور نیز به بلیزر افزود. هرچند هر مدل میزبانی نقاط قوت بنیادی متفاوتی دارد، هر دو بر معماری زیربنایی یکسانی تکیه میکنند.
این رویکرد به توسعهدهندگان امکان میدهد بیشتر کد خود را مستقل از مدل میزبانی بنویسند. در حالت ایدهآل، تنها جایی که کد بهروشنی سمت سرور یا سمت کاربر محسوب میشود، هنگام دریافت داده است. برای درک بهتر کاربردپذیری این دو مدل، آنها را دقیقتر بررسی میکنیم.
سمت کاربر: Blazor WebAssembly
در سمت کاربر، اجرای بلیزر با کمک وباسمبلی ممکن میشود و به آن «برنامهٔ Blazor WebAssembly» میگویند. وباسمبلی (wasm) یک قالب دستورالعمل دودویی برای مرورگرهای وب است که بهعنوان مقصد کامپایل زبانهای سطحبالا مانند C++ طراحی شده است. بلیزر از طریق زماناجرای Mono - که به یک ماژول وباسمبلی کامپایل شده - از این فناوری بهره میبرد. همانطور که در شکل ۳ میبینیم، آوردن زماناجرای .NET به مرورگر باعث میشود کتابخانههای .NET یا همان فایلهای DLL مستقیماً روی کاربر اجرا شوند.
بازنمایی درونخطی صفحهٔ 14 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 15 از ۱۲۱بازگشت به فهرست
شکل ۳: این نمودار، زماناجرای .NET را درون مرورگر و با استفاده از وباسمبلی نشان میدهد. بلیزر از این زماناجرا برای کار مستقیم با کتابخانههای استاندارد .NET استفاده میکند.
این رویکرد از نظر کارایی و اندازهٔ بسته، مصالحههایی دارد؛ اما نقطهٔ قوت اصلی آن در نهایت قابلیت حمل است. چون برنامههای بلیزر از کتابخانههای .NET استفاده میکنند، لازم نیست کد موجود دوباره کامپایل شود یا از کامپایلر ویژهای با مقصد خاص استفاده کنیم. کد برنامه را میتوان میان پروژههای .NET به اشتراک گذاشت و در نتیجه کد کمتری نوشت.
در یک برنامهٔ معمولِ فرانتاند جاوااسکریپت، کد اعتبارسنجی و انتقال داده اغلب تکرار میشود، زیرا هم در لایهٔ .NET و هم در لایهٔ جاوااسکریپت لازم است. این تکرار در برنامهٔ بلیزر ضروری نیست، چون کد سمت کاربر و سمت سرور میتوانند به یک کتابخانهٔ مشترک ارجاع دهند.
مانند هر فناوری سمت کاربر، برنامههای Blazor WebAssembly برای داده به وبسرویسهای RESTful وابستهاند. دریافت داده با HttpClient انجام میشود که استاندارد زیستبوم .NET است. هنگامی که در بلیزر یک فراخوانی HTTP انجام میدهیم، داده بهصورت خودکار به شیء کلاس مشخصشده سریالسازی میشود. برای درک سادگی این فرایند، فراخوانی متد GetJsonAsync<T> از HttpClient را بررسی کنیم.
در یک برنامهٔ Web API، کنترلری به نام WeatherForecast داریم که یک نقطهٔ پایانی با خروجی IEnumerable<WeatherForecast> ارائه میکند. وقتی متد Get فراخوانی شود، ASP.NET پاسخ را بهطور خودکار به یک رشتهٔ JSON سریالسازی میکند.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
بازنمایی درونخطی صفحهٔ 15 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 16 از ۱۲۱بازگشت به فهرست
{
[HttpGet]
public IEnumerable<WeatherForecast> Get() {…}
}
هنگام دریافت داده در یک برنامهٔ بلیزر، یک فیلد برای نگهداری آرایهای از WeatherForecast ایجاد میکنیم. در این نمونه از متد چرخهٔ عمر OnInitializedAsync برای مقداردهی فیلد forecasts استفاده میکنیم. با GetJsonAsync یک درخواست GET به کنترلر WeatherForecast میفرستیم. چون GetJsonAsync با نوع مشخص WeatherForecast[] فراخوانی شده است، نتیجهٔ سریالشدهای از همین نوع بازمیگرداند.
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http
.GetJsonAsync<WeatherForecast[]>("WeatherForecast");
}
هم Web API و هم برنامهٔ سمت کاربر میتوانند از کلاس یکسان WeatherForecast استفاده کنند؛ نتیجه، کد کمتر، آزمون کمتر و پیچیدگی پایینتر است.
میتوانیم با اجرای بلیزر در سمت سرور، پیچیدگی برنامه را باز هم کاهش دهیم. در حالت سمت سرور، بلیزر میتواند مستقیماً از Entity Framework استفاده کند و نیاز به یک برنامهٔ Web API را بهطور کامل از میان بردارد. پس از آشنایی بیشتر با مفاهیم اصلی، این گزینهها را در فصلهای بعد بررسی خواهیم کرد.
Blazor Server با SignalR
یک برنامهٔ Blazor Server برای ساخت برنامهٔ وب .NET رویکردی متفاوت از Blazor WebAssembly دارد. در Blazor Server، بهجای وباسمبلی، مرورگر بهعنوان یک کاربر سبک (Thin Client) در نظر گرفته میشود. تمام کد برنامه با زماناجرای .NET Core روی سرور اجرا میشود.
برای ممکنشدن این عملیات کاربر سبک، یک کتابخانهٔ کمحجم SignalR در سمت کاربر راهاندازی میشود تا کد سرور بتواند از طریق WebSocket بهروزرسانیهای ناهمگام ارسال کند. پیامهای ردوبدلشده میان سرور و کاربر فقط شامل رویدادها و بهروزرسانیها هستند. همانطور که در شکل ۴ میبینیم، بلیزر روی سرور اجرا میشود و از طریق SignalR با مرورگر ارتباط دارد؛ در همین حال، کاربر سبک بهروزرسانیهای DOM (Document Object Model یا مدل شیء سند) را اعمال میکند.
بازنمایی درونخطی صفحهٔ 16 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 17 از ۱۲۱بازگشت به فهرست
شکل ۴: این نمودار بلیزر را در حال اجرا در سمت سرور و تعامل با مرورگر از طریق اتصال SignalR نشان میدهد.
چون کد برنامه کاملاً در سمت سرور اجرا میشود، لایهٔ داده میتواند بدون نیاز به لایهٔ انتزاعی جداگانه، اتصال نزدیکی با برنامه داشته باشد. این رویکرد برای برنامههای سازمانی (Line-of-Business) که اتصال پایدار در دسترس دارند ایدهآل است.
وقتی داده روی همان سرور قرار دارد، برنامههای Blazor Server برای ارتباط با پایگاه داده یا سرویسها به یک Web API نیاز ندارند. بهجای استفاده از HttpClient مانند Blazor WebAssembly، میتوان سرویسها را مستقیماً به برنامه تزریق و استفاده کرد. در نهایت، برنامههای Blazor Server به انتزاعهای کمتری نیاز دارند و میتوان آنها را در یک پروژهٔ واحد ساخت. اندازه و دامنهٔ پروژه نیز ممکن است بر سطح انتزاع اثر بگذارد، اما داشتن امکان انتخابِ راهحل سادهتر مزیت مهمی است.
دریافت داده در Blazor Server، هنگام استفادهٔ مستقیم از Entity Framework Core، کار سادهای است. ابتدا فیلدی میسازیم که دادهها را نگه دارد. در این نمونه از متد چرخهٔ عمر OnInitializedAsync برای مقداردهی فیلد blogs استفاده میکنیم. با dbContext متد ToArrayAsync را فراخوانی میکنیم. چون برنامه کاملاً در سمت سرور اجرا میشود، نه نقطهٔ پایانی Web API وجود دارد و نه نگرانی دربارهٔ سریالسازی و بازسریالسازی.
Blog[] blogs;
protected override async Task OnInitializedAsync()
{
blogs = await dbContext.Blogs.ToArrayAsync();
}
بازنمایی درونخطی صفحهٔ 17 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 18 از ۱۲۱بازگشت به فهرست
استفاده از بلیزر این انتخاب را در اختیارتان میگذارد که یا یک برنامهٔ RESTful سمت کاربر را با فناوریهای .NET بهجای جاوااسکریپت بسازید، یا یک برنامهٔ وب .NET سمت سرور با پیچیدگی کمتر ایجاد کنید.
مدلهای میزبانی بلیزر: مقایسهٔ نقاط قوت و مصالحهها
| سمت کاربر |
سمت سرور |
| + سربار کم یا صفر برای سرور |
+ اندازهٔ بسیار کوچک دادهٔ ارسالی |
| + RESTful |
+ انتزاع کمتر |
| + قابلیت کار آفلاین و PWA |
+ پیشرندر بهصورت پیشفرض |
| - اندازهٔ بزرگتر دادهٔ ارسالی |
- سربار سرور |
| - محیط جداشده از سرور |
- نیازمند اتصال |
بازنمایی درونخطی صفحهٔ 18 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 19 از ۱۲۱بازگشت به فهرست
انواع پروژه در بلیزر
در فصل پیش، دوگانگی بلیزر و مفاهیم کلی هر مدل میزبانی را بررسی کردیم. در این فصل، بیشتر بر انواع پروژهای تمرکز میکنیم که از انتخابهای ارائهشده توسط چارچوب پدید میآیند.
برای آنکه همه از نقطهای یکسان شروع کنیم، ابتدا پیشنیازهای بلیزر را مرور میکنیم. برخی از ابزارهایی که با آنها کار خواهیم کرد، بهصورت پیشفرض همراه با .NET SDK یا محیط توسعهٔ یکپارچهٔ Visual Studio نصب میشوند. بااینحال، بخشهایی از بلیزر در زمان نگارش هنوز در وضعیت پیشنمایش هستند و برای آنکه قالبها و کد بهدرستی کار کنند به مراحل نصب اضافی نیاز دارند.
برای آغاز توسعه با بلیزر، موارد زیر باید روی رایانهٔ شما نصب باشند:
- جدیدترین نسخهٔ .NET Core 3.x SDK.
- قالب Blazor WebAssembly.
- جدیدترین نسخهٔ Preview از Visual Studio 2019، همراه با بارکاری ASP.NET and web development.
برای دستورالعمل دقیق نصب، بهتر است مراحل موجود در مستندات رسمی مایکروسافت در Blazor.net را دنبال کنید؛ زیرا این مستندات میتوانند شمارهٔ جدیدترین نسخهٔ هر نرمافزار را همواره بهروز نگه دارند.
بازنمایی درونخطی صفحهٔ 19 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 20 از ۱۲۱بازگشت به فهرست
پس از نصب نرمافزارهای لازم، آمادهایم قالبهای پروژهٔ جدید بلیزر را بررسی کنیم. برای ایجاد یک برنامهٔ جدید بلیزر در Visual Studio، مسیر File > New Project را انتخاب کنید و در پنجرهٔ Create a new project گزینهٔ Blazor App را برگزینید.
شکل ۵: پنجرهٔ ایجاد برنامهٔ جدید بلیزر سه قالب برای انتخاب ارائه میکند.
انواع پروژهٔ موجود در پنجرهٔ Create a new Blazor app در شکل ۵ عبارتاند از:
Blazor Server App - سمت سرور
Blazor WebAssembly - سمت کاربر
Blazor WebAssembly - سمت کاربر، همراه با میزبان ASP.NET و Web API (معروف به Full-Stack)
اکنون که میدانیم هر قالب پروژه را کجا پیدا کنیم، میتوانیم جزئیات هر انتخاب را بررسی کنیم.
Blazor WebAssembly
قالب سمت کاربر بلیزر را میتوان با یک برنامهٔ HTML، CSS و JavaScript در دیگر چارچوبهای SPA مقایسه کرد. بااینحال، در بلیزر، جاوااسکریپت از طریق وباسمبلی با .NET و C# تکمیل میشود. برخلاف پروژههای ASP.NET MVC که ادامهٔ توضیح آن در صفحهٔ بعد آمده است، رندر اصلی این مدل در سمت کاربر انجام میشود.
بازنمایی درونخطی صفحهٔ 20 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 21 از ۱۲۱بازگشت به فهرست
در پروژههای سمت کاربر بلیزر، هرچند از Razor استفاده میشود، خروجی روی سرور رندر نمیشود؛ در عوض، چارچوب بلیزر در سمت کاربر HTML را در مرورگر رندر میکند. مرورگر همهچیز را پردازش میکند و محتوای این پروژه بهصورت منابع ایستا در نظر گرفته میشود.
چون پروژه را میتوان مجموعهای از منابع ایستا دانست، امکانات جالبی پدید میآید که پیش از این در زیستبوم ASP.NET دیده نشده بود. پروژههای ساختهشده با این قالب را تقریباً روی هر سرویسی که توان ارائهٔ فایلهای ایستا را دارد میتوان میزبانی کرد؛ برای نمونه GitHub Pages و قابلیت Static website hosting در Azure Storage.
این قالب، نمونههایی برای شروع کار با بلیزر و همچنین چیدمانهای پایهٔ صفحه، کنترلهای ناوبری و استایلهای Bootstrap را در خود دارد.
شکل ۶: راهحل، ساختار پروژهای آشنا برای برنامههای ASP.NET دارد که چند بخش ویژهٔ بلیزر نیز به آن افزوده شده است.
ساختار پروژه ساده است و فقط منابع اصلی زیر را در بر میگیرد:
/wwwroot: منابع ایستای استاندارد وب، شامل CSS، JavaScript، JSON، تصویر و HTML
/Pages: صفحهها و قابلیتهای Razor برنامه
/Shared: مؤلفههای مشترک و چیدمانهای صفحه
App.razor: مؤلفهٔ ریشه و ظرف کل برنامه
Program.cs: راهاندازی اولیه و پیکربندی برنامه
بازنمایی درونخطی صفحهٔ 21 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 22 از ۱۲۱بازگشت به فهرست
در نگاه نخست، چند مفهوم آشنا دیده میشود، زیرا بلیزر رویکردی شبیه برنامههای ASP.NET دارد؛ این موضوع در شکل ۶ مشخص است. Program.cs نهتنها یکی از اجزای رایج برنامههای .NET است، بلکه بلیزر از مفهومی مشابه ASP.NET Core Razor Pages نیز استفاده میکند. صفحهها یا قابلیتهای برنامهٔ بلیزر در مسیر پروژهٔ Pages قرار میگیرند و مسیریابی از طریق دستور @page همان صفحه انجام میشود.
@page "/myRoute"
<!-- component markup -->
@code {
// component logic
}
تمام پروژههای بلیزر شامل صفحههای نمونهٔ Index، Counter و Fetch Data هستند. این موارد در همهٔ انواع پروژه یکساناند؛ بهجز Fetch Data، زیرا تفاوت اصلی انواع پروژه در محل میزبانی برنامه نسبت به دادهای است که مصرف میکند.
بررسی را با صفحهٔ Counter و نشانهگذاری و منطق مؤلفهٔ آن آغاز میکنیم.
Counter
صفحهٔ Counter یک مؤلفهٔ ساده است که با دستور صفحه تزئین شده است. این مؤلفه، ترکیب پایهٔ یک مؤلفهٔ بلیزر شامل مسیریابی، اتصال داده و اتصال/مدیریت رویداد را نشان میدهد. هر بخش از ساختار مؤلفه در شکل ۷ برجسته شده است.
بازنمایی درونخطی صفحهٔ 22 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 23 از ۱۲۱بازگشت به فهرست
شکل ۷: ساختار مؤلفهٔ Counter شامل دستورها، نشانهگذاری و منطقی است که با اتصال داده به هم پیوند خوردهاند.
مؤلفهٔ Counter از یک دکمهٔ سادهٔ HTML برای افزایش فیلد شمارنده استفاده میکند و مقدار شمارنده درون یک تگ پاراگراف نمایش داده میشود. چون بلیزر بهصورت برنامهٔ تکصفحهای عمل میکند، تمام تعاملهای مؤلفه در سمت کاربر رخ میدهند. چارچوب بلیزر با استفاده از اتصال داده، بهروزرسانی DOM مرورگر را مدیریت میکند. خروجی رندرشدهٔ مؤلفهٔ Counter در شکل ۸ دیده میشود.
شکل ۸: مؤلفهٔ Counter که در مرورگر رندر شده است.
بازنمایی درونخطی صفحهٔ 23 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 24 از ۱۲۱بازگشت به فهرست
با رفتن به صفحهٔ Fetch Data میبینیم بلیزر چگونه منابع دادهٔ محلی را مدیریت میکند.
Fetch Data
در نوع پروژهٔ WebAssembly، صفحهٔ Fetch Data مؤلفهای است که از دادههای یک فایل ایستای محلی استفاده میکند. مؤلفهٔ Fetch Data تزریق وابستگی و مفاهیم پایهٔ قالب Razor را نمایش میدهد. این نسخه از Fetch Data بسیار شبیه نمونهٔ موجود در قالب Full-Stack است؛ تفاوت اصلی در محلی است که داده از آن بارگذاری میشود.
در بالای مؤلفه و پس از دستور مسیریابی، تزریق وابستگی تعریف شده است. دستور @inject به بلیزر میگوید نمونهای از HttpClient را برای متغیر Http فراهم کند. سپس منطق مؤلفه با استفاده از HttpClient و متد GetJsonAsync داده را دریافت میکند و دادهٔ درخواست JSON را به آرایهای از اشیای WeatherForecast متصل میسازد:
@page "/fetchdata"
@inject HttpClient Http
<h1>Weather forecast</h1>
// omitted for brevity
@code {
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http
.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}
…
}
بازنمایی درونخطی صفحهٔ 24 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 25 از ۱۲۱بازگشت به فهرست
نمایش دادههای WeatherForecast با پیمایش مجموعهٔ forecasts و اتصال مقادیر به یک جدول HTML انجام میشود:
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
برای این نمونههای پایه هیچ سروری استفاده نمیشود و نیازی نیز به آن نیست. اگر قصد داریم برنامه را همراه با میزبانی و وبسرویسها توسعه دهیم، قالبهای Full-Stack یا Server-Side میتوانند نقطهٔ شروع بهتری باشند.
Blazor Full-Stack
قالب Blazor Full-Stack همان ساختار پروژهٔ قالب Client-Side را با چند بخش افزوده در بر میگیرد. مانند قالب Client-Side، هیچ HTMLای روی سرور رندر نمیشود و تمام فایلها - از جمله فایلهای دودویی .NET - بهصورت فایل ایستا به کاربر تحویل داده میشوند. تفاوت، اضافهشدن میزبانی ASP.NET Core، Web API و یک پروژهٔ Shared برای منطق مشترک برنامه است.
بازنمایی درونخطی صفحهٔ 25 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 26 از ۱۲۱بازگشت به فهرست
این قالب سه پروژه دارد:
- برنامهٔ بلیزر سمت کاربر:
Blazor.Client
- برنامهٔ سرور ASP.NET Core:
Blazor.Server
- پروژهٔ مشترک .NET Standard برای منطق مشترک برنامه:
Blazor.Shared
Blazor.Server
برنامهٔ سرور مسئول ارائهٔ برنامه و فراهمکردن نقاط پایانی Web API برای دادهها است. در Startup.cs تنظیمات MimeType را میبینیم که سرور را طوری پیکربندی میکند تا فایلهای *.wasm و *.dll قابل ارائه باشند. علاوه بر این، فشردهسازی فعال میشود تا اندازهٔ فایلهای دودویی هنگام انتقال به کاربر کاهش یابد. فشردهسازی از طریق میانافزار AddResponseCompression فعال میشود.
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults
.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
فرایند Startup همچنین جایی است که میانافزار برنامهٔ بلیزر با app.UseClientSideBlazorFiles<Client.Startup>() مقداردهی اولیه میشود. این دستور مشخص میکند که برنامهٔ Blazor.Client باید ارائه شود.
// This method gets called by the runtime. Use this method
// to configure the HTTP request pipeline.
app.UseClientSideBlazorFiles<Client.Startup>();
برنامه یک نمونهٔ ساده از کنترلر Web API و اکشن آن را نیز در بر دارد. در WeatherForecastController، اکشن WeatherForecasts مجموعهای تصادفی از پیشبینیهای هوا تولید میکند. در قالب Full-Stack، وب API مربوط به WeatherForecasts جایگزین فایل ایستای weather.json در قالب Client-Side میشود.
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
…
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
…
return Enumerable.Range(1, 5)
بازنمایی درونخطی صفحهٔ 26 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 27 از ۱۲۱بازگشت به فهرست
.Select(index => new WeatherForecast…)
.ToArray();
}
}
Blazor.Client
تقریباً تمام برنامهٔ Client با قالب Client-Side یکسان است. بااینحال، نمونهٔ FetchData اندکی تفاوت دارد و در فراخوانی GetJsonAsync داده را از نقطهٔ پایانی Web API مربوط به WeatherForecast درخواست میکند.
@code {
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http
.GetJsonAsync<WeatherForecast[]>("WeatherForecast");
}
}
Blazor.Shared
چون راهحل شامل برنامهٔ سرور و کاربری است که هر دو از .NET استفاده میکنند، امکان اشتراک کد میان دو برنامه وجود دارد. این سناریو ویژهٔ بلیزر است، زیرا کاربر بهجای جاوااسکریپت، .NET را در سمت کاربر اجرا میکند. نمونهٔ موجود در قالب پروژه، از همان کلاس WeatherForecast در برنامهٔ سرور و سمت کاربر استفاده میکند.
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF =>
32 + (int)(TemperatureC / 0.5556);
}
کلاس WeatherForecast تنها یک نمونهٔ ابتدایی از کد مشترک است. کد مشترک دیگر میتواند شامل اعتبارسنجی، تبدیلکنندهها و منطق کسبوکار باشد؛ مشروط بر آنکه از مفاهیم وابسته به سیستم، ورودی/خروجی یا وب جدا شده باشد. اگر این ایده را گسترش دهیم، یک برنامهٔ فرضی میتواند کتابخانهها را با چارچوبهای دیگر .NET مانند Xamarin، Windows Desktop یا برنامههای وب مبتنی بر .NET به اشتراک بگذارد.
بازنمایی درونخطی صفحهٔ 27 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 28 از ۱۲۱بازگشت به فهرست
رندر در سرور
در دیگر فناوریهای وب .NET مانند ASP.NET MVC و Core MVC، قالبهای Razor روی سرور رندر و بهصورت HTML برای کاربر ارسال میشوند. برخی چارچوبهای جاوااسکریپت مانند Angular و React، در فرایندی به نام کامپایل پیش از اجرا (Ahead-of-Time Compilation یا AOT) یا رندر همشکل (Isomorphic Rendering)، مسئولیت رندر را میان کاربر و سرور تقسیم میکنند.
با وجود اینکه برنامهٔ کاربری بلیزر روی یک سرور .NET میزبانی میشود، در هر دو قالب Client-Side و Full-Stack همهٔ نماها در سمت کاربر رندر میشوند. در زمان نگارش، میتوان با سفارشیسازی گستردهٔ قالب Full-Stack، پیشرندر سمت سرور را فعال کرد.
Blazor Server App
قالب پروژهٔ Blazor Server-Side در شیوهٔ تحویل برنامهٔ بلیزر و تعامل آن با مرورگر، رویکردی کاملاً متفاوت دارد. در پیکربندی سمت سرور، بلیزر با استقرار یک برنامهٔ جاوااسکریپتی SignalR روی کاربر، مرورگر را بهعنوان «کاربر سبک» به کار میگیرد. در سمت سرور، بلیزر یک هاب SignalR پیادهسازی میکند که از طریق WebSocket با کاربر ارتباط دارد.
در مدل میزبانی سمت سرور، بلیزر درون یک برنامهٔ ASP.NET Core و روی سرور اجرا میشود. بهروزرسانیهای رابط کاربری، مدیریت رویدادها و فراخوانیهای جاوااسکریپت از طریق اتصال SignalR انجام میشوند. در این پیکربندی نیازی به وباسمبلی نیست و بلیزر روی زماناجرای ASP.NET Core سرور اجرا میشود.
در شکل ۹، نمودار بلوکی پیادهسازی Blazor Server را میبینیم. همهٔ بهروزرسانیهای رابط کاربری بهصورت تفاوتها (Diffs)، دوسویه و در قالب بستههای دودویی از طریق WebSocket ارسال میشوند. از دید کاربر، این برنامه با هیچ برنامهٔ وب دیگری تفاوتی ندارد.
در این پیکربندی نیازی به وباسمبلی نیست و بلیزر روی زماناجرای ASP.NET Core در سرور اجرا میشود.
بازنمایی درونخطی صفحهٔ 28 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 29 از ۱۲۱بازگشت به فهرست
شکل ۹: این نمودار، اجرای بلیزر در سمت سرور و تعامل آن با مرورگر از طریق اتصال SignalR را نشان میدهد.
با وجود تفاوت چشمگیر در نحوهٔ عملکرد بلیزر در سمت سرور، مدل واقعی برنامه تقریباً یکسان باقی میماند. در قالب پروژهٔ سمت سرور، تفاوتهای اندکی در کد نمونهٔ ارائهشده وجود دارد. قالب، مانند برنامهٔ Blazor WebAssembly، فقط یک پروژه دارد.
در برنامهٔ Blazor Server، فایل _Host.cshtml نقطهٔ ورود سمت کاربر به برنامه است. برخلاف برنامهٔ Blazor WebAssembly، فایل _Host.cshtml برای راهاندازی برنامه از Razor Pages استفاده میکند. ازاینرو نحو Razor را در صفحه و بهشکل Tag Helper زیر میبینیم: <component …>.
Tag Helper مربوط به component، رندر یک مؤلفهٔ Razor را از درون یک Razor Page فراخوانی میکند. مؤلفهٔ ریشهٔ برنامه از App.razor در همین نقطه فراخوانی میشود. هنگامی که برنامه برای عملیات سمت سرور پیکربندی شود، فایل جاوااسکریپتی blazor.server.js جایگزین blazor.webassembly.js میشود. blazor.server.js کاربر SignalR را راهاندازی میکند و ارتباط WebSocket با سرور را برقرار میسازد.
بازنمایی درونخطی صفحهٔ 29 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 30 از ۱۲۱بازگشت به فهرست
<body>
<app>
<component type="typeof(App)"
render-mode="ServerPrerendered" />
</app>
…
<script src="_framework/blazor.server.js"></script>
</body>
چون تمام برنامه در سمت سرور اجرا میشود، نمونهٔ FetchData در این نوع پروژه از دادههای یک سرویس محلی استفاده میکند. دستور @inject در این نمونه بهجای HttpClient - که در قالب Full-Stack دیدیم - نمونهای از WeatherForecastService را فراهم میکند. WeatherForecastService در نمونه، کلاسی ساده برای تولید دادههای تصادفی است؛ اما در یک سناریوی واقعی، این سرویس میتواند Context پایگاه دادهٔ Entity Framework، مخزن (Repository) یا منبع دیگری از داده باشد.
@using BlazorBookExamples.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
// omitted for brevity
@code {
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService
.GetForecastAsync(DateTime.Now);
}
}
WeatherForecastService و سرویسهای دیگر در متد ConfigureServices فایل Startup.cs به ظرف تزریق وابستگی اضافه میشوند.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
Server
پروژهٔ سروری که قالب ارائه میکند، یک میزبان سادهٔ ASP.NET Core است. در فایل Startup.cs این پروژه، میانافزار UseEndpoints فراخوانی میشود و هاب SignalR بلیزر را به مسیر صحیح نگاشت میکند.
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env)
{
بازنمایی درونخطی صفحهٔ 30 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 31 از ۱۲۱بازگشت به فهرست
}
…
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
مزایا و معایب
از آنجا که برای راهاندازی کاربر فقط مقدار کمی جاوااسکریپت لازم است و هیچ اسمبلی .NET به کاربر منتقل نمیشود، برنامهٔ Blazor Server-Side از نظر ترافیک شبکه کارآمد است. حتی هنگام اجرا نیز ترافیک شبکه سبک باقی میماند، زیرا ارتباط میان «کاربر سبک» مرورگر و سرور در قالب بستههای دودویی کوچک انجام میشود. بااینحال، چون WebSocket به کار میرود، کاربر و سرور باید همواره به یکدیگر متصل باشند. بنابراین برنامههای Blazor Server-Side در حالت آفلاین کار نمیکنند.
جمعبندی انواع پروژهٔ بلیزر
هر نوع پروژه مجموعهای مشابه از نمونهها، از جمله مؤلفههای Counter و Fetch Data، دارد. نمونهٔ Fetch Data از پروژهای به پروژهٔ دیگر متفاوت است تا ویژگیهای خاص همان نوع پروژه را نشان دهد.
با قالبهای پروژهٔ Blazor WebAssembly، Full-Stack و Blazor Server، توسعهدهندگان میتوانند نقطهٔ شروعی را انتخاب کنند که بیشترین سازگاری را با نیازهای برنامهشان دارد. قالب Blazor WebAssembly بر اجرای کامل فایلهای ایستا در مرورگر تمرکز دارد؛ درحالیکه قالب Full-Stack میزبانی ASP.NET Core و Web API را نیز در بر میگیرد. قالب Blazor Server چارچوب بلیزر را روی سرور اجرا میکند و بهجای وباسمبلی به SignalR متکی است؛ در نتیجه، در ازای عملکرد مناسب، وابستگی دائمی به اتصال شبکه ایجاد میشود.
بازنمایی درونخطی صفحهٔ 31 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 32 از ۱۲۱بازگشت به فهرست
مبانی مؤلفهها در بلیزر
در فصل پیش، انواع پروژهای را که برای ساخت یک پروژهٔ جدید استفاده میشوند بررسی کردیم. در این فصل با شیوهٔ نوشتن مؤلفهها بیشتر آشنا میشویم.
همانطور که در فصل «آشنایی با بلیزر» بهاختصار گفتیم، چارچوب بلیزر از مدل مؤلفهٔ Razor استفاده میکند. در این فصل، هنگام ساخت مؤلفهای برای نمایش پیشبینی آبوهوا، دربارهٔ مؤلفههای Razor بیشتر میآموزیم. در این فرایند با مبانی ساخت مؤلفه، از جمله دستورها، پارامترها، محتوای فرزند/قالبها، مسیریابی و مدیریت رویداد آشنا خواهیم شد. برای سادگی، از منابعی استفاده میکنیم که قالب جدید Blazor Server app در اختیارمان قرار میدهد.
دستورها
دستورهای کامپایلر (Compiler Directives) برای ارائهٔ فرمان به Razor استفاده میشوند؛ فرمانهایی که معمولاً شیوهٔ تجزیهٔ مؤلفه را تغییر میدهند یا قابلیت متفاوتی را فعال میکنند. بیایید صفحهای جدید برای نمایش پیشبینی هفتگی آبوهوا بسازیم. در چارچوب بلیزر، صفحهها مؤلفههایی هستند که با دستور @page تزئین میشوند. پس از دستور @page یک مقدار رشتهای قرار میگیرد که مسیر صفحهٔ مؤلفه را تعریف میکند و URLای میسازد که میتوان در مرورگر به آن رفت؛ برای نمونه: @page "/my-url".
هرچند دستور @page مسیریابی را فعال میکند، توانایی ما برای استفاده از مؤلفه بهعنوان یک بلوک سازندهٔ پایه در دیگر مؤلفهها یا «صفحهها» را تغییر نمیدهد.
بازنمایی درونخطی صفحهٔ 32 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 33 از ۱۲۱بازگشت به فهرست
کار را با افزودن یک مؤلفهٔ Razor جدید به نام WeeklyForecast به برنامه آغاز میکنیم. در پروژه، پوشهٔ Pages را انتخاب و روی آن راستکلیک کنید؛ سپس Add > New Item را انتخاب کنید و مطابق شکل ۱۰، گزینهٔ Razor Component را برگزینید. نام مؤلفه را WeeklyForecast.razor بگذارید و برای پایان کار روی Create کلیک کنید.
شکل ۱۰: ساخت یک مؤلفهٔ Razor جدید با پنجرهٔ Add New Item.
مؤلفه با یک قالب اولیهٔ بسیار ساده ساخته میشود. این قالب را نگه میداریم و دستور @page با مقدار "/weeklyforecast" را در ابتدای فایل قرار میدهیم. بهترین رویه این است که همهٔ دستورها در بالای فایل قرار گیرند، مگر آنکه کاربرد خاصی جای دیگری را ضروری کند. برای نمونه، بلوک @code نیز یک دستور است که معمولاً در پایین مؤلفه قرار میگیرد؛ بااینحال جابهجایی آن خطایی ایجاد نمیکند.
@page "/weeklyforecast"
<h3>WeeklyForecast</h3>
@code {
}
اکنون میتوانیم برنامه را اجرا و تغییرات را در مرورگر مشاهده کنیم. پس از اجرای برنامه، /weeklyforecast را به نشانی اضافه کنید تا به صفحهٔ تازهساختهشده بروید. نتیجه باید شبیه شکل ۱۱ باشد.
بازنمایی درونخطی صفحهٔ 33 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 34 از ۱۲۱بازگشت به فهرست
شکل ۱۱: مؤلفهٔ WeeklyForecast که برای تعیین مسیر از دستور @page استفاده میکند.
از این صفحه برای ساخت نمونهٔ اولیهٔ محصول نهایی استفاده میکنیم. این کار کمک میکند تشخیص دهیم کدام بخشها را میتوان به اجزای قابلاستفادهٔ مجدد تبدیل کرد. اطلاعات آبوهوا را با HTML و کلاسهای CSS مربوط به Bootstrap و Open Iconic نمایش خواهیم داد.
درون صفحهٔ WeeklyForecast نخستین عنصر نمونهٔ رابط کاربری را اضافه میکنیم: دادهٔ پیشبینی یک روز. فعلاً از مقادیر ثابت استفاده میکنیم که بعداً با دادههای واقعی جایگزین خواهند شد. نشانهگذاری درون یک عنصر div قرار دارد که با کلاس card از Bootstrap ظاهر مناسبی پیدا میکند. درون بدنهٔ کارت، یک عنصر span با کلاسهای Open Iconic یعنی oi oi-rain قرار میدهیم تا آیکونی متناسب با وضعیت آبوهوا نمایش دهد.
بازنمایی درونخطی صفحهٔ 34 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 35 از ۱۲۱بازگشت به فهرست
@page "/weeklyforecast"
<h3>WeeklyForecast</h3>
<div class="card bg-light" style="width: 18rem;">
<div class="card-body text-center">
<span class="h1 oi oi-rain"></span>
<h1 class="card-title">17 C°</h1>
<p class="card-text">
Rainy weather expected Monday
</p>
</div>
</div>
…
تغییرات را ذخیره و صفحهٔ WeeklyForecast را دوباره بارگذاری میکنیم؛ اکنون کارت آبوهوا نمایش داده میشود. صفحهٔ رندرشده باید شبیه شکل ۱۲ باشد.
شکل ۱۲: نمونهٔ اولیهٔ صفحهٔ پیشبینی هفتگی که یک پیشبینی آبوهوا را نمایش میدهد.
صفحه کمکم شکل میگیرد؛ اما ما در حال ساخت پیشبینی هفتگی هستیم و درحالحاضر فقط یک روز داریم. صفحه را طوری تغییر میدهیم که بتواند دادهٔ پیشبینی پنج روز را نمایش دهد. چون کارتها تکرار خواهند شد، بخش نمایش را در یک ظرف Flexbox قرار میدهیم. از آنجا که Bootstrap به کار میبریم، میتوانیم از کلاس کمکی d-flex استفاده کنیم.
درون ظرف d-flex باید نمایش آبوهوا را تکرار کنیم. برای این کار از Razor و حلقهٔ foreach کمک میگیریم. برای ساخت یک حلقهٔ ساده بدون دادهٔ واقعی، در صفحهٔ بعد از Enumerable.Range استفاده میکنیم.
بازنمایی درونخطی صفحهٔ 35 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 36 از ۱۲۱بازگشت به فهرست
متد Enumerable.Range برای ما یک دنباله تولید میکند. حالا که چند کارت نمایش داده میشود، ویژگی عرض ثابت style="width: 18rem;" را حذف میکنیم تا عنصر در صورت نیاز گسترش یابد. برای ایجاد فاصلهٔ سفید میان کارتها، کلاس CSS m-2 یا margin 2 افزوده میشود.
…
<div class="d-flex">
@foreach (var item in Enumerable.Range(1, 5))
{
<div class="card bg-light m-2">
<div class="card-body text-center">
<span class="h1 oi oi-rain"></span>
<h1 class="card-title">17 C°</h1>
<p class="card-text">
Rainy weather expected Monday
</p>
</div>
</div>
}
</div>
…
نتیجه، پیشبینی پنجروزهای است که مطابق شکل ۱۳ در عرض صفحه تکرار میشود.
شکل ۱۳: صفحهٔ آبوهوای هفتگی که یک کارت پیشبینی ثابت را پنج بار در عرض صفحه تکرار میکند.
بازنمایی درونخطی صفحهٔ 36 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 37 از ۱۲۱بازگشت به فهرست
اکنون احتمالاً الگوی تکرار هر روز آبوهوا را میبینید. این بخش تکرارشونده میتواند بهسادگی یک مؤلفهٔ قابلاستفادهٔ مجدد باشد که اطلاعات یک روز را کپسوله میکند. بیایید این بخش را از صفحهٔ WeeklyForecast جدا کنیم و در فایل مؤلفهٔ مستقلی قرار دهیم.
در پوشهٔ /Shared پروژه، مؤلفهای جدید به نام WeatherDay میسازیم. محتوای اولیهٔ مؤلفهٔ تازه را حذف میکنیم و HTML درون حلقهٔ foreach از مؤلفهٔ WeeklyForecast را به مؤلفهٔ WeatherDay انتقال میدهیم. همین عملیات سادهٔ برش و چسباندن برای ساخت یک مؤلفهٔ پایهٔ Razor کافی است.
پس از انتقال نشانهگذاری به مؤلفهٔ WeatherDay، میتوانیم در WeeklyForecast آن را با نامش به کار ببریم. یک مؤلفهٔ WeatherDay به بدنهٔ حلقهٔ foreach اضافه میکنیم.
/Shared/WeatherDay.razor
<div class="card bg-light m-2">
<div class="card-body text-center">
<span class="h1 oi oi-rain"></span>
<h1 class="card-title">17 C°</h1>
<p class="card-text">
Rainy weather expected Monday
</p>
</div>
</div>
/Pages/WeeklyForecast.razor
…
<div class="d-flex">
@foreach (var item in Enumerable.Range(1, 5))
{
<WeatherDay></WeatherDay>
}
</div>
…
با اجرای دوبارهٔ صفحه، نتیجه باید کاملاً مشابه اجرای قبلی و همان شکل ۱۳ باشد. خروجی تغییر نکرده است، زیرا فقط مسئولیت رندر کارت را از WeeklyForecast به WeatherDay منتقل کردهایم.
پارامترها
اکنون برخی مقادیر ثابت را با پارامترهای مؤلفه جایگزین میکنیم. سادهترین راه درک پارامترهای مؤلفه در مدل Razor Component این است که آنها را ویژگیهای عمومیای بدانیم که با صفت ویژهٔ [Parameter] تزئین شدهاند.
بازنمایی درونخطی صفحهٔ 37 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 38 از ۱۲۱بازگشت به فهرست
در ادامه با صفتهای پارامتر دیگری نیز آشنا میشویم، اما صفت استاندارد [Parameter] برای بیشتر سناریوها مناسب است.
حالا که HTML مؤلفه درون WeatherDay جدا شده است، میتوانیم با پذیرش داده از طریق پارامترها، آن را پویاتر کنیم. مؤلفهٔ آبوهوا باید خلاصهٔ وضعیت هوا - مانند Rainy، Sunny یا Cloudy - را همراه با آیکون متناظر نمایش دهد. دما و روز هفته نیز نشان داده میشوند.
برای شروع چند پارامتر میسازیم و همزمان با حل چالشها قابلیتهای دقیقتری اضافه میکنیم. یک بلوک @code ایجاد میکنیم، سه ویژگی را به آن میافزاییم و هرکدام را با [Parameter] تزئین میکنیم.
@code {
[Parameter]
public string Summary { get; set; }
[Parameter]
public int TemperatureC { get; set; }
[Parameter]
public DayOfWeek DayOfWeek { get; set; }
}
اتصال دادهٔ یکطرفه
برای نمایش مقدار پارامتر از اتصال دادهٔ یکطرفه (One-Way Data Binding) استفاده میکنیم. این روش مقدار را در همان نقطهای که در نشانهگذاری قرار دارد مینویسد. در Razor کافی است نام ویژگی را با علامت @ آغاز کنیم؛ مانند @Summary.
برای هر پارامتر، متن ثابت نشانهگذاری را با مقدار متصل به داده جایگزین میکنیم. هنگامی که Razor خروجی نهایی را تولید میکند، همهٔ مقادیر بهطور خودکار به رشته تبدیل میشوند. لازم نیست مقداری مانند TemperatureC را با وجود نوع int بودن، دستی تبدیل کنیم؛ هرچند در صورت نیاز میتوانیم قالب نمایش را سفارشی کنیم.
<div class="card bg-light m-2">
<div class="card-body text-center">
<span class="h1 oi oi-rain"></span>
<h1 class="card-title">@TemperatureC C°</h1>
<p class="card-text">
@Summary weather expected @DayOfWeek
</p>
</div>
</div>
بازنمایی درونخطی صفحهٔ 38 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 39 از ۱۲۱بازگشت به فهرست
اکنون که مؤلفه پارامترهای متصل به داده دارد، به صفحهٔ WeeklyForecast بازمیگردیم و نمونهٔ مؤلفه را بهروزرسانی میکنیم تا از قابلیت جدید استفاده کند. مؤلفهٔ WeatherDay را در صفحه پیدا میکنیم و چند مقدار ثابت برای پارامترهای آن قرار میدهیم.
@foreach (var item in Enumerable.Range(1, 5))
{
<WeatherDay
TemperatureC="20"
Summary="Cloudy"
DayOfWeek="DayOfWeek.Friday">
</WeatherDay>
}
حالا دادهای پویا به صفحهٔ WeeklyForecast اضافه میکنیم. یک WeatherForecastService را به صفحه تزریق و از آن برای تولید داده استفاده خواهیم کرد. این سرویس در قالب پروژهٔ جدید وجود دارد و در پوشهٔ /Data قرار گرفته است. فایل WeatherForecastService.cs را باز کنید و شیوهٔ کار آن را ببینید. مجموعهای به نام Summaries وجود دارد که برای پرکردن ویژگی Summary استفاده میشود. متد GetForecastAsync بر اساس تاریخ آغاز، پنج مقدار تصادفی WeatherForecast تولید میکند.
فهرست Summaries را ساده میکنیم تا فقط شامل Cloudy، Rainy و Sunny باشد؛ این سه مقدار با سه آیکون موجود در پروژه متناظرند.
private static readonly string[] Summaries = new[]
{
//"Freezing", "Bracing", "Chilly", "Cool", "Mild",
//"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
"Cloudy", "Rainy", "Sunny"
};
در WeatherDay از یک ویژگی فقطخواندنی استفاده میکنیم تا بر اساس فیلد Summary آیکون مناسب تعیین شود. یک عبارت سهتایی کوتاه، رشتهٔ Summary را به نام آیکون متناظر تبدیل میکند.
string IconCssClass =>
Summary == "Cloudy" ? "cloud" :
Summary == "Rainy" ? "rain" :
"sun";
اکنون میتوان از ویژگی IconCssClass برای تغییر مقدار CSS آیکون مؤلفه استفاده کرد.
<span class="h1 oi oi-@IconCssClass"></span>
بازنمایی درونخطی صفحهٔ 39 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 40 از ۱۲۱بازگشت به فهرست
تزریق یک سرویس
اکنون مؤلفهٔ WeatherDay میتواند به دادهٔ پویا متصل شود. با دستور @inject، سرویس WeatherForecastService را به صفحه تزریق میکنیم. این وابستگی در دسترس است، زیرا قالب از قبل WeatherForecastService را در Startup.cs ثبت کرده است. با نگاه به متد ConfigureServices میتوانیم نحوهٔ پیکربندی تزریق وابستگی (Dependency Injection یا DI) را ببینیم. روشهای مختلف DI را در فصل دیگری که به این موضوع اختصاص دارد بررسی خواهیم کرد.
services.AddSingleton<WeatherForecastService>();
در صفحهٔ WeeklyForecast، زیر دستور @page، دستور @inject را اضافه میکنیم و هم نوعی را که باید Resolve شود و هم نام متغیری را که نمونهٔ آن نوع دریافت میکند مشخص میکنیم؛ برای نمونه: @inject T myTInstance. دستور @using نیز WeatherForecastService را در دامنه قرار میدهد.
@page "/weeklyforecast"
@using Data;
@inject WeatherForecastService WeatherService
متد چرخهٔ عمر OnInitializedAsync
اکنون که WeatherForecastService در دسترس است، میتوانیم متد GetForecastAsync را فراخوانی کنیم که آرایهای از اشیای WeatherForecast میسازد. داده باید هنگام نخستین مقداردهی مؤلفه بارگذاری شود. مؤلفههای Razor مقداردهی اولیه را از طریق متدهای چرخهٔ عمر OnInitializedAsync و OnInitialized مدیریت میکنند. این متدها بهطور خودکار از کلاس ComponentBase - پایهٔ همهٔ مؤلفههای Razor - به ارث میرسند.
بهترین رویه این است که هرجا ممکن باشد از کد ناهمگام استفاده کنیم، زیرا تجربهٔ کاربری کلی را بهتر میکند. افزون بر این، WeatherForecastService نیز قابلیت ناهمگام دارد؛ بنابراین برای دریافت داده همزمان با مقداردهی مؤلفه از OnInitializedAsync استفاده میکنیم.
یک فیلد برای نگهداری دادهای که در صفحه نمایش داده میشود اضافه میکنیم. آرایهای خالی از WeatherForecast به نام forecasts میسازیم. پس از آن، متد OnInitializedAsync را Override میکنیم و با انتظار برای فراخوانی GetForecastAsync، فیلد forecasts را مقداردهی میکنیم.
بازنمایی درونخطی صفحهٔ 40 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 41 از ۱۲۱بازگشت به فهرست
WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await WeatherService
.GetForecastAsync(DateTime.Now);
}
اکنون که از دادهٔ WeatherService استفاده میکنیم، باید حلقهٔ ثابت نما را با حلقهای جایگزین کنیم که آرایهٔ forecasts را پیمایش میکند. درون حلقهٔ جدید، به عضو جاری ارجاع میدهیم و مقدار ویژگی متناظر را روی پارامتر مؤلفه قرار میدهیم. کامپایلر .NET هوشمند است؛ بنابراین فقط برای مقادیر رشتهای لازم است از علامت @ استفاده کنیم تا رشتهٔ ثابت از مقدار ویژگی تشخیص داده شود.
@*foreach (var item in Enumerable.Range(1, 5))*@
@foreach (var forecast in forecasts)
{
<WeatherDay
TemperatureC="forecast.TemperatureC"
Summary="@forecast.Summary"
DayOfWeek="forecast.Date.DayOfWeek">
</WeatherDay>
}
چون دیگر با دادهٔ ثابت کار نمیکنیم، باید احتمال مقدار null را نیز در نظر بگیریم. مؤلفه یک بار پیش از اجرای OnInitializedAsync و بار دیگر پس از آن رندر میشود؛ بنابراین اگر هنگامی رندر شود که آرایهٔ forecasts هنوز null است، ورود به حلقهٔ foreach باعث پرتاب NullReferenceException خواهد شد.
برای جلوگیری از این وضعیت، از شرط if/else استفاده میکنیم تا هنگام نبود داده پیامی نمایش داده شود و استثنا رخ ندهد.
@if (forecasts == null)
{
<span>No Data</span>
}
else
{
foreach (var forecast in forecasts)
{
<WeatherDay
TemperatureC="forecast.TemperatureC"
Summary="@forecast.Summary"
DayOfWeek="forecast.Date.DayOfWeek">
</WeatherDay>
}
}
بازنمایی درونخطی صفحهٔ 41 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 42 از ۱۲۱بازگشت به فهرست
اکنون با رفتن به مسیر weeklyforecast میتوانیم داده را بهصورت پویا رندر کنیم؛ همانطور که در شکل ۱۴ دیده میشود.
شکل ۱۴: صفحهٔ آبوهوای هفتگی، کارت پیشبینی را برای پنج عضو در عرض صفحه نمایش میدهد.
مؤلفههای WeeklyForecast و WeatherDay در حال کاملشدناند. میتوانیم بر اساس مجموعهای از داده که هنگام مقداردهی اولیهٔ مؤلفه بارگذاری میشود، مؤلفهها را بهصورت پویا پر کنیم. داده از WeeklyForecast و از طریق اتصال دادهٔ یکطرفه مستقیماً به هر مؤلفهٔ WeatherDay جریان مییابد. این یکی از روشهای رایج نمایش داده است و هنگامی که رابط کاربری ثابتی میسازیم بهخوبی کار میکند.
اکنون مؤلفهٔ WeatherDay را گسترش میدهیم و با فراهمکردن یک ناحیهٔ قالب، امکان توسعهٔ بیشتر رابط کاربری آن را ایجاد میکنیم.
محتوای فرزند و قالبها
مؤلفههای قالبپذیر (Templated Components) مؤلفههایی هستند که یک یا چند قالب رابط کاربری را بهعنوان پارامتر میپذیرند. از قالبها میتوان برای سفارشیکردن بخشی از خروجی رندرشدهٔ مؤلفه استفاده کرد. برای نمایش شیوهٔ پیادهسازی قالب، کار را با همان WeatherDay ادامه میدهیم.
بازنمایی درونخطی صفحهٔ 42 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 43 از ۱۲۱بازگشت به فهرست
برای انعطافپذیرترکردن WeatherDay، ناحیهای برای قالب اضافه میکنیم که در شکل ۱۵ با کادر قرمز نشان داده شده است. این بخش به توسعهدهندگان اجازه میدهد هر HTML، کد Razor یا مؤلفهای را بهعنوان محتوای فرزند وارد کنند.
شکل ۱۵: یک کادر قرمز محل ایجاد ناحیهٔ قالب مؤلفه را مشخص میکند.
برای افزودن قالب به WeatherDay از کلاس RenderFragment استفاده میکنیم. RenderFragment نمایانگر بخشی از محتوای رابط کاربری است و بهصورت نمایندهای پیادهسازی میشود که محتوا را در RenderTreeBuilder - DOM مجازی بلیزر - مینویسد. در ظاهر ممکن است تصور شود محتوای فرزند بهصورت HTML خام نوشته میشود؛ اما RenderFragment کاملاً با معماری مؤلفهای بلیزر سازگار است.
در ظاهر ممکن است تصور شود محتوای فرزند بهصورت HTML خام نوشته میشود؛ اما RenderFragment از معماری مؤلفهای بلیزر پیروی میکند.
بازنمایی درونخطی صفحهٔ 43 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 44 از ۱۲۱بازگشت به فهرست
یک RenderFragment به مؤلفهٔ WeatherDay اضافه میکنیم. این عضو دقیقاً مانند هر پارامتر دیگر مؤلفه و با یک ویژگی و صفت [Parameter] افزوده میشود. در این نمونه نام ویژگی را CustomMessage میگذاریم.
@code {
[Parameter]
public RenderFragment CustomMessage { get; set; }
…
[Parameter]
public string Summary { get; set; }
}
برای استفاده از پارامتر، در محل دلخواهِ نشانهگذاری مؤلفه به RenderFragment ارجاع میدهیم. بین عناصر h1 و p، عبارت @CustomMessage قرار میگیرد و قالب در همین نقطه رندر میشود.
<div class="card bg-light m-2">
<div class="card-body text-center">
<span class="h1 oi oi-@IconCssClass"></span>
<h1 class="card-title">@TemperatureC C°</h1>
@CustomMessage
<p class="card-text">
@Summary weather expected @DayOfWeek
</p>
</div>
</div>
هنگام استفاده از WeatherDay، قالب با مشخصکردن نام آن در قالب یک تگ HTML یعنی <CustomMessage> اعمال میشود. درون این تگ میتوان از نشانهگذاری Razor، HTML، مؤلفههای دیگر یا هر ترکیبی از این موارد استفاده کرد.
این قابلیت را با یک شرط ساده به نمونه اضافه میکنیم: اگر پیشبینی Rainy بود، هشدار ویژهای نمایش داده شود. درون CustomMessage مقداری کد Razor و یک عنصر ساده با کلاس CSS بوتاسترپ alert alert-danger قرار میدهیم.
<WeatherDay
TemperatureC="forecast.TemperatureC"
Summary="@forecast.Summary"
DayOfWeek="forecast.Date.DayOfWeek">
<CustomMessage>
@if (forecast.Summary == "Rainy")
{
<div class="alert alert-danger">
Tornado Warning!
</div>
}
</CustomMessage>
بازنمایی درونخطی صفحهٔ 44 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 45 از ۱۲۱بازگشت به فهرست
</WeatherDay>
وقتی برنامه را اجرا و صفحهٔ weeklyforecast را مشاهده کنیم، اکنون مطابق شکل ۱۶ کادر هشدار Tornado Warning! درون مؤلفهٔ WeatherDay نمایش داده میشود.
شکل ۱۶: پیشبینی هفتگی با هشدار گردباد که در ناحیهٔ قالب مؤلفهٔ WeatherDay نمایش داده شده است.
تا اینجا، مؤلفههای WeeklyForecast و WeatherDay بیشتر قابلیتهایی را که معمولاً در معماری مؤلفهای میبینیم دارند. بااینحال، یکی از جنبههای مهم توسعهٔ رابط کاربری هنوز بررسی نشده است: تعاملپذیری. در ادامه با فراهمکردن امکان انتخاب یک عضو در پیشبینی هفتگی، نحوهٔ مدیریت رویدادها در بلیزر را بررسی میکنیم.
مدیریت رویداد
وقتی در C# به رویدادها و نمایندهها فکر میکنیم، احتمالاً کلیدواژههای event و delegate یا انواع نمایندهٔ Action و Func به ذهنمان میآیند. این گزینهها هنگام کار با اسمبلیهای استاندارد C# از نظر فنی معتبرند. اما کار با رابط کاربری برنامهٔ بلیزر و مؤلفههای Razor، الگوی معماری متفاوت و نمایندهٔ ویژهای به نام EventCallback را معرفی میکند.
EventCallback نوعی نماینده برای در معرض قراردادن رویدادها میان مؤلفهها است. یک مؤلفهٔ والد میتواند متد Callback خود را به EventCallback مؤلفهٔ فرزند اختصاص دهد.
بازنمایی درونخطی صفحهٔ 45 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 46 از ۱۲۱بازگشت به فهرست
وقتی EventCallback به این روش استفاده شود، مؤلفهٔ والد و فرزند هنگام رخدادن رویدادها بهطور خودکار دوباره رندر میشوند. استفاده از event، delegate، Action یا Func ممکن است باعث شود رابط کاربری مطابق انتظار بهروزرسانی نشود. به همین دلیل، در مؤلفههای Razor باید همیشه از EventCallback استفاده شود.
EventCallback نوعی نماینده است که برای ارائهٔ رویدادها میان مؤلفهها استفاده میشود.
برای نمایش نحوهٔ استفاده از EventCallback، کار را با مؤلفههای WeeklyForecast و WeatherDay ادامه میدهیم. قابلیتی اضافه میکنیم تا کاربر بتواند با کلیک روی یک روز، آن WeatherDay را در پیشبینی هفتگی انتخاب کند.
ابتدا پارامتر جدیدی به نام OnSelected به WeatherDay میافزاییم. این پارامتر باید یک EventCallback<DayOfWeek> باشد؛ یعنی رویداد، مقداری از نوع DayOfWeek را بهعنوان آرگومان بازمیگرداند. از این مقدار میتوانیم برای تشخیص عضوی که OnSelected را فعال کرده استفاده کنیم.
[Parameter]
public EventCallback<DayOfWeek> OnSelected { get; set; }
زیر پارامتر OnSelected، یک مدیریتکنندهٔ رویداد ساخته میشود که مسئول فعالکردن نمایندهٔ رویداد OnSelected در مؤلفهٔ والد است. برای فراخوانی OnSelected، متد InvokeAsync را با DayOfWeek جاری بهعنوان آرگومان فراخوانی میکنیم.
void HandleOnSelected()
{
OnSelected.InvokeAsync(this.DayOfWeek);
}
در ادامهٔ منطق WeatherDay، ویژگی Selected اضافه میشود تا مشخص کند مؤلفه در وضعیت انتخابشده قرار دارد یا نه. این ویژگی هم برای اتصال مقدار انتخابشده به مؤلفه استفاده میشود و هم برای تعیین CSS متناظر رابط کاربری.
با ویژگی خصوصی SelectedCss، بر اساس مقدار Selected کلاس CSS مناسب تعیین میشود. اگر عضو انتخاب شده باشد، کلاسهای bg-primary text-white مؤلفه را برجسته و رنگ متن را معکوس میکنند؛ در غیر این صورت مقدار پیشفرض bg-light خواهد بود.
[Parameter]
public bool Selected { get; set; }
بازنمایی درونخطی صفحهٔ 46 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 47 از ۱۲۱بازگشت به فهرست
private string SelectedCss => Selected
? "bg-primary text-white"
: "bg-light";
اکنون در نشانهگذاری مؤلفه میتوانیم هنگام کلیک کاربر، نمایندهٔ رویداد HandleOnSelected را فعال کنیم. به بیرونیترین عنصر div یک مدیریتکنندهٔ onclick متصل و آن را به متد HandleOnSelected نسبت میدهیم. همچنین div را تغییر میدهیم تا از مقدار SelectedCss استفاده کند و ظاهر رابط کاربری را تغییر دهد.
<div class="card m-2 @SelectedCss"
@onclick="HandleOnSelected">
<div class="card-body text-center">
…
</div>
</div>
اکنون WeatherDay قابل انتخاب است و با چند تغییر در کلاس WeatherForecast و مؤلفهٔ WeeklyForecast میتوانیم قابلیت جدید را به کار بگیریم. چون WeatherForecast داده و وضعیت نما را فراهم میکند، ویژگی Selected را به آن میافزاییم تا بتوانیم در مؤلفهها به آن متصل شویم.
public class WeatherForecast
{
public bool Selected { get; set; }
…
نشانهگذاری WeeklyForecast برای پیوند همهٔ اجزا چند بهروزرسانی دریافت میکند. در محل استفاده از WeatherDay، ویژگیهای جدید اعمال میشوند. متد OnSelected نمایندهٔ HandleItemSelected را میگیرد و مقدار Selected به ویژگی Selected عضو forecast متصل میشود.
<WeatherDay
TemperatureC="forecast.TemperatureC"
Summary="@forecast.Summary"
DayOfWeek="forecast.Date.DayOfWeek"
OnSelected="HandleItemSelected"
Selected="forecast.Selected">
پس از تکمیل تغییرات، میتوانیم برنامه را اجرا کنیم، به صفحهٔ weeklyforecast برویم و مطابق شکل ۱۷ اعضا را از نما انتخاب کنیم.
بازنمایی درونخطی صفحهٔ 47 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 48 از ۱۲۱بازگشت به فهرست
شکل ۱۷: عضو دوم فهرست پس از کلیک کاربر انتخاب شده است.
میتوانیم با افزودن هر نوع منطق سفارشی به متد HandleItemSelected این مفهوم را بیشتر گسترش دهیم. تصور کنید هنگام انتخاب یک عضو چه قابلیتهایی میتوان افزود؛ برای نمونه نمایش پیشبینی ساعتی، فهرستکردن کسبوکارهای تعطیل یا پیشنهاد فعالیتهای مناسب برای آن وضعیت هوا. این مفهوم در بسیاری از برنامههای تجاری که دادههایشان رابطهٔ والد-فرزند دارند قابل استفاده است.
جمعبندی مبانی مؤلفههای بلیزر
در سراسر این فصل، مبانی ساخت مؤلفه مانند دستورها، پارامترها، محتوای فرزند/قالبها، مسیریابی و مدیریت رویداد را آموختیم. هر نمونه، فرایند ساخت یک مؤلفه را مرحلهبهمرحله نشان داد و همزمان موضوعاتی را معرفی کرد که در بخشهای بعدی کتاب با جزئیات بیشتری بررسی خواهند شد. هدف این فصل ارائهٔ دانشی بود که هنگام استفاده از چارچوب بلیزر فوراً ما را به بهرهوری برساند.
بازنمایی درونخطی صفحهٔ 48 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 49 از ۱۲۱بازگشت به فهرست
رویکرد کدِ پشتصحنه
در فصل پیش دربارهٔ معماری مؤلفه آموختیم. در این فصل با استفاده از کلاسهای partial، نشانهگذاری مؤلفه را از منطق آن جدا میکنیم.
در این فصل میآموزیم چه زمانی استفاده از رویکرد کدِ پشتصحنه (Code-Behind) در توسعهٔ بلیزر سودمند است، چه مزایایی دارد و هنگام بازآرایی مؤلفههای موجود چه انتظاری باید داشته باشیم.
در بلیزر، معماری پیشفرض مؤلفه به این صورت است که تمام نشانهگذاری و منطق در یک فایل .razor ترکیب میشوند. وقتی مؤلفهها سادهاند، این رویکرد خوب کار میکند؛ اما با افزایش پیچیدگی، مدیریت تمام بخشهای مؤلفه دشوار میشود. رویکرد Code-Behind اجازه میدهد نشانهگذاری و منطق در فایلهای جداگانه قرار گیرند. در این فصل بررسی میکنیم چه زمانی این روش مفید است، چه سودی میبریم و در بازآرایی مؤلفههای موجود با چه مواردی روبهرو خواهیم شد.
رویکرد تکفایلی
کار را با مرور سریع رویکرد تکفایلی آغاز میکنیم. مؤلفهٔ FetchData از قالب پیشفرض بلیزر - که در شکل ۱۸ نمایش داده شده - نمونهٔ ما خواهد بود. مؤلفهٔ FetchData با پیمایش دادهای که یک سرویس فراهم میکند، جدولی HTML از پیشبینی آبوهوا نمایش میدهد. مؤلفه از دستورها، HTML/Razor و بلوک @code استفاده میکند؛ همگی اجزای معمول یک مؤلفهاند.
بازنمایی درونخطی صفحهٔ 49 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 50 از ۱۲۱بازگشت به فهرست
شکل ۱۸: مؤلفهٔ FetchData با رویکرد تکفایلی.
نشانهگذاری این مؤلفه نسبتاً طولانی است. با بیش از ۳۰ خط، خود نشانهگذاری طول فایل را افزایش میدهد و برای یافتن منطق مؤلفه که در بلوک @code قرار دارد مجبور میشویم به پایین پیمایش کنیم. در شکل ۱۹ میبینیم پیش از رسیدن به منطق مؤلفه، نزدیک به ۴۰ خط نشانهگذاری وجود دارد.
بازنمایی درونخطی صفحهٔ 50 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 51 از ۱۲۱بازگشت به فهرست
شکل ۱۹: بخش نشانهگذاری مؤلفهٔ FetchData در ۳۷ خط امتداد دارد.
به خاطر داشته باشید این مؤلفه هنوز در واقع بسیار ساده است. با افزایش پیچیدگی، هر بخش نیز بزرگتر خواهد شد؛ یعنی در نهایت دستورها، عبارتهای using، نشانهگذاری و منطق بیشتری در بلوک کد خواهیم داشت. هرچند داشتن یک فایل همهکاره ممکن است راحت به نظر برسد، بهدلیل پیمایش مداوم میان نشانهگذاری و منطق، نگهداری آن بهتدریج خستهکننده میشود.
مؤلفهٔ FetchData را با استفاده از فایل Code-Behind بازآرایی میکنیم تا ببینیم چگونه تجربهٔ کلی توسعهدهنده بهتر میشود.
رویکرد Code-Behind
Code-Behind اصطلاح رایجی برای تکنیکی است که در آن یک فایل کد جداگانه، تمام منطق صفحه، نما یا مؤلفهٔ متناظر را در بر میگیرد. ساخت Code-Behind در بلیزر چند مرحله دارد، اما خوشبختانه خود چارچوب از آن پشتیبانی میکند و راهاندازی بسیار ساده است. برای تکمیل Code-Behind باید یک کلاس بسازیم و سپس آن را به نشانهگذاری پیوند دهیم. پس از شکلگیری این ساختار، میتوانیم منطق موجود را به فایل جدید انتقال دهیم.
ساخت و پیوند Code-Behind
بازنمایی درونخطی صفحهٔ 51 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 52 از ۱۲۱بازگشت به فهرست
ابتدا به یک فایل کلاس نیاز داریم که Code-Behind را نمایندگی کند. این فایل یک کلاس partial استاندارد .NET است؛ بااینحال، نامگذاری اهمیت زیادی دارد. هنگام ساخت فایل Code-Behind باید هم نام فایل و هم نام کلاس را با دقت انتخاب کنیم، زیرا میتوانند تأثیر زیادی بر تجربهٔ توسعه داشته باشند.
از نام فایل آغاز میکنیم. هنگام افزودن فایل باید از قرارداد رایج زیر پیروی کنیم:
[componentName].razor.cs
Visual Studio پسوند .razor.cs را میشناسد و فایل را مطابق شکل ۲۰ بهدرستی زیر فایل مؤلفه در پنجرهٔ File Explorer قرار میدهد. چون مؤلفه از قبل نام یک کلاس را اشغال کرده است، باید کلاس جدید را با کلیدواژهٔ partial تعریف کنیم.
public partial class FetchData { }
شکل ۲۰: Visual Studio فایلهایی با نام یکسان و الگوی پسوند .razor.cs را بهصورت تودرتو نمایش میدهد.
اکنون میتوانیم تمام کد بلوک @code و چند دستور دیگر را به Code-Behind منتقل کنیم.
انتقال @code به Code-Behind
بیشتر منطق موجود در بلوک @code بدون هیچ تغییری مستقیماً به Code-Behind کپی میشود؛ بااینحال چند بهروزرسانی کوچک لازم است.
هنگام انتقال کد، اگر Code-Behind بهطور خودکار یک فضای نام را دریافت نکند، ممکن است به عبارتهای using بیشتری نیاز باشد. ابزارهای استاندارد در اینجا بسیار کمککنندهاند و میانبر [Ctrl]+[.] بهسرعت فضاهای نام گمشده را اضافه میکند.
بازنمایی درونخطی صفحهٔ 52 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 53 از ۱۲۱بازگشت به فهرست
کافی است هر موردی را که زیر آن خط قرمز کشیده شده انتخاب کنید و کلیدهای [Ctrl]+[.] را فشار دهید. اگر مشکل از فضای نام گمشده باشد، اصلاح پیشنهادی را بپذیرید تا تغییر اعمال شود.
// added by [ctrl] + [.]
using BlazorBookExamples.WeatherComponent.Data;
using Microsoft.AspNetCore.Components;
پس از انتقال همهٔ کدها، یک مرحلهٔ نهایی باقی میماند: تزریق وابستگی (DI).
تزریق وابستگی در Code-Behind بلیزر
تزریق وابستگی در نشانهگذاری Razor با دستور @inject مدیریت میشود. در کلاسهای Code-Behind نیز این قابلیت وجود دارد، اما نحو آن کاملاً متفاوت است و در نگاه نخست شاید آشکار نباشد. برای استفاده از تزریق وابستگی در فایل Code-Behind، صفت [Inject] را روی ویژگیای از نوع موردنظر قرار میدهیم؛ برای نمونه: [Inject] MyType MyProp { get; set; }.
[Inject]
WeatherForecastService ForecastService { get; set; }
با افزودن صفت [Inject]، مؤلفه اکنون میتواند سرویس را Resolve کند و دیگر به دستور مربوطه در نشانهگذاری Razor نیاز نداریم. میتوانیم نشانهگذاری را پاکسازی کنیم و دستورهای @inject و @using و همچنین بلوک @code را حذف نماییم.
نکات پایانی دربارهٔ Code-Behind
در آغاز، دستورها، نشانهگذاری و منطق مؤلفه همگی در یک فایل قرار داشتند. رویکرد تکفایلی بهسبب سادگی مزایایی دارد و برای مؤلفههای کوچک عالی است، اما در مقیاس بزرگ خوب عمل نمیکند. وقتی مؤلفه به نشانهگذاری یا منطق پیچیده نیاز پیدا کند، بهسادگی در فایل بزرگ گم میشویم یا بهدلیل دو شیوهٔ متفاوت بیان کد دچار سردرگمی میشویم. افزون بر این، دسترسی به برخی از ارزشمندترین ابزارهای بهرهوری Visual Studio را از دست میدهیم.
تبدیل یک مؤلفهٔ موجود به Code-Behind بسیار آسان است. فقط چند تغییر مربوط به عبارتهای using و تزریق وابستگی لازم است.
بازنمایی درونخطی صفحهٔ 53 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 54 از ۱۲۱بازگشت به فهرست
در بیشتر موارد، Visual Studio حتی ما را برای انجام اصلاحات درست و رسیدن به کامپایل موفق راهنمایی میکند.
شکل ۲۱: نمای کنارهم مؤلفهٔ FetchData؛ نشانهگذاری در سمت چپ و منطق در سمت راست.
پس از تکمیل انتقال، از نقشهای روشن و جداگانه برای نشانهگذاری و منطق و همچنین پیمایش بسیار کمتر بهرهمند میشویم. اگر لازم باشد کل مؤلفه را یکجا ببینیم، میتوانیم دو پنجره را مانند شکل ۲۱ کنار هم قرار دهیم و بر کار جاری تمرکز کنیم.
بازنمایی درونخطی صفحهٔ 54 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 55 از ۱۲۱بازگشت به فهرست
اتصال داده
فصلهای پیش ما را با نحو Razor آشنا کردند. در این فصل، شیوهٔ کار با اتصال داده، نحوهٔ تعریف اتصال و زمان فعالشدن رویدادهای اتصال را بررسی میکنیم.
اتصال داده (Data Binding) جریان داده میان یک یا چند مؤلفه و متغیر است. اتصال یکطرفه هنگامی مفید است که باید داده را در رابط کاربری نمایش دهیم؛ بهویژه برای سناریوهای فقطخواندنی. اتصال دادهٔ دوطرفه معمولاً برای واداشتن چند مؤلفه به بهروزرسانی وضعیت خود بر اساس مؤلفههای دیگر سامانه استفاده میشود. بنابراین، اتصال دوطرفه بیشتر همراه با مؤلفههای ورودی به کار میرود؛ جایی که مقدار یک ورودی مستقیماً از مقدار ورودی دیگری اثر میپذیرد.
برای درک بهتر نحوهٔ مدیریت اتصال داده در بلیزر، نمونهای ساده راهاندازی میکنیم. ابزاری برای تبدیل اینچ و سانتیمتر میسازیم که از دو ورودی HTML برای تبدیل در هر دو جهت استفاده میکند. نمونه را در چند مرحله میسازیم و هر مرحله یکی از مفاهیم اصلی اتصال داده را آشکار میکند. شکل ۲۲ نمونهٔ نهایی را نشان میدهد که در مراحل بعد بررسی خواهیم کرد.
بازنمایی درونخطی صفحهٔ 55 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 56 از ۱۲۱بازگشت به فهرست
شکل ۲۲: در این نمونه، هرکدام از ورودیها میتوانند مقداری را به اینچ یا سانتیمتر تبدیل کنند. دو ورودی با اتصال دادهٔ دوطرفه همگام نگه داشته میشوند؛ برای نمونه: 1 in = 2.54 cm.
برای این مجموعه نمونهها، مؤلفهای جدید میسازیم. زیر پوشهٔ Pages مؤلفهای به نام Converter اضافه و مسیر صفحه را نیز متناسب با آن تنظیم کنید؛ مانند @page "/converter".
نخستین آزمایش، مشاهدهٔ رفتار پیشفرض دستور @ هنگام استفاده روی یک عنصر HTML است. کار را با افزودن یک ورودی HTML به مؤلفه و تنظیم مقدار آن روی ۱ و نوع آن روی number آغاز میکنیم.
<input value="1" type="number" />
اجرای برنامه در این مرحله صفحهای چندان هیجانانگیز تولید نمیکند. صفحه ورودیای دارد که مقدار پیشفرض آن ۱ است و اجازه میدهد هر مقدار عددی تعیین کنیم. هرچند عملکرد زیادی ندارد، زمینهٔ لازم برای نمونهٔ بعدی را فراهم میکند.
اتصال یکطرفه
از اتصال دادهٔ یکطرفه برای تنظیم مقادیر عددی مؤلفه استفاده میکنیم. یک عنصر HTML دوم، یعنی تگ پاراگراف p، اضافه میکنیم که متن «مقدار اینچ برابر است با: ۱» را نمایش دهد.
<input value="1" type="number" />
<p>The value of inches is: 1</p>
پس از نشانهگذاری، بلوک @code افزوده میشود تا منطق مؤلفه را در آن بنویسیم. درون بلوک کد، فیلدی از نوع double با نام inches اضافه میکنیم و مقدار پیشفرض آن را ۱ قرار میدهیم.
@code {
بازنمایی درونخطی صفحهٔ 56 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 57 از ۱۲۱بازگشت به فهرست
double inches = 1; // default value
}
حالا که فیلد inches در دسترس است، به بخش نشانهگذاری بازمیگردیم و مقدار ثابت ۱ را هم در ورودی و هم در متن پاراگراف با آن جایگزین میکنیم.
<input value="@inches" type="number" />
<p>The value of inches is: @inches</p>
@code {
double inches = 1; // default value
}
لحظهای تصور کنید این مؤلفه چگونه رفتار خواهد کرد: وضعیت اولیه چگونه است و وقتی عدد تازهای در ورودی وارد شود چه اتفاقی میافتد؟ در ابتدا میتوان تشخیص داد که ورودی مقدار ۱ را دارد و متن میگوید «مقدار اینچ ۱ است».
آنچه ممکن است غافلگیرکننده باشد، رفتار برنامه پس از ورود عدد جدید است. وقتی مقدار ورودی تغییر کند، فقط مقدار خود ورودی عوض میشود و مقدار فیلد inches تغییر نمیکند. هنگام نوشتن value="@inches"، ویژگی value به عدد جاری در inches تنظیم میشود؛ درست مانند آنکه در نمونهٔ قبلی آن را دستی روی ۱ گذاشته باشیم. به بیان دیگر، value بهعنوان یک ارجاع زنده به فیلد inches تنظیم نشده است. این خروجی فقطخواندنی، اتصال دادهٔ یکطرفه محسوب میشود.
هنگام نوشتن value="@inches"، مقدار value به عدد جاری در inches تنظیم میشود؛ درست مانند آنکه آن را دستی روی ۱ قرار داده باشیم. این مقدار یک ارجاع زنده به فیلد inches نیست.
رویدادهای اتصال
برای آنکه هنگام تغییر مقدار ورودی توسط کاربر، هم فیلد inches و هم متن خروجی بهدرستی بهروزرسانی شوند، باید از یک رویداد استفاده کنیم. با مدیریت رویداد onchange ورودی، میتوانیم منطق کسبوکار لازم برای بهروزرسانی فیلد inches را اجرا کنیم.
ابتدا مدیریتکنندهای به نام UpdateValue میسازیم. این متد یک ChangeEventArgs میگیرد که مقدار تعیینشده توسط کاربر را در خود دارد. سپس میتوانیم با آرگومان رویداد، فیلد inches را روی مقدار جدید قرار دهیم. چون inches از نوع double است، باید محافظتهای لازم را اضافه و آرگومان رویداد را بهدرستی Parse کنیم.
بازنمایی درونخطی صفحهٔ 57 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 58 از ۱۲۱بازگشت به فهرست
void UpdateValue(ChangeEventArgs e)
{
double val = 0; // Failing to parse will set to 0
double.TryParse(e.Value.ToString(), out val);
inches = val;
}
اکنون در نشانهگذاری، نمایندهٔ UpdateValue را به رویداد onchange ورودی اختصاص میدهیم.
<input value="@inches"
@onchange="UpdateValue"
type="number" />
همهچیز برای همگام نگهداشتن مقدار ورودی، فیلد inches و متن نمایش آماده است. وقتی کاربر مقدار ورودی را تغییر دهد، متد UpdateValue فراخوانی و فیلد پشتیبان بهروزرسانی میشود. از آنجا که onchange یک EventCallback است، رابط کاربری دوباره رندر میشود و متن خروجی نیز مطابق انتظار تغییر میکند.
اتصال دوطرفه
استفاده از ویژگی value ورودی و مدیریت دستی رویداد onchange بسیار مفید است، اما راه سادهتری نیز وجود دارد. نمونه را با اتصال دوطرفه و دستور @bind ادامه میدهیم. نکتهٔ جالب این است که پیادهسازی ما تا اینجا بسیار شبیه نحوهٔ کار داخلی @bind است.
وقتی @bind بهصورت پیشفرض روی یک ورودی فراخوانی شود، ویژگی value تنظیم و بهروزرسانی میشود. افزون بر این، onchange رویداد پیشفرضی است که اتصال داده را فعال میکند. بلیزر همچنین کارهای اضافی لازم برای تبدیل خودکار نوعها را انجام میدهد.
در مؤلفهٔ ورودی نمونه، میتوانیم ویژگی value را با @bind جایگزین کنیم. چون @bind رویداد onchange و تبدیلهای نوع لازم را نیز مدیریت میکند، دستور @onchange و متد UpdateValue قابل حذفاند. نتیجه این است که هر سه مقدار - فیلد inches، مقدار ورودی و نمایش inches - همگام میشوند؛ همانطور که در شکل ۲۳ دیده میشود.
<input type="number" @bind="inches" />
<p>The value of inches is: @inches</p>
@code {
double inches = 1; // default value
}
بازنمایی درونخطی صفحهٔ 58 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 59 از ۱۲۱بازگشت به فهرست
شکل ۲۳: با تغییر مقدار ورودی، فیلد پشتیبان بهروزرسانی میشود و متن «The value of inches» دوباره رندر میگردد.
اکنون که یک نمونهٔ سادهٔ اتصال دوطرفه داریم، آن را با افزودن ورودی دوم برای سانتیمتر گسترش میدهیم. ورودی سانتیمتر تقریباً با ورودی اینچ یکسان است. فیلدی به نام centimeters با مقدار اولیهٔ ۲٫۵۴ میسازیم و یک ورودی عددی متصل به آن اضافه میکنیم.
<input type="number" @bind="inches" />
<input type="number" @bind="centimeters" />
@code {
double inches = 1; // default value
double centimeters = 2.54; // default value
}
هر ورودی هنوز بهطور مستقل به فیلد متناظر خود متصل است. باید مقداری منطق کسبوکار برای انجام تبدیل واقعی اضافه کنیم. چون @bind مدیریتکنندهٔ رویداد را درون خود پنهان میکند، از ویژگیهای C# برای انجام کار استفاده خواهیم کرد.
ویژگیای به نام Inches اضافه میکنیم و اتصال را از فیلد inches به این ویژگی تغییر میدهیم. ویژگی Inches هنگام خواندن، فقط فیلد پشتیبان را برمیگرداند. هنگام تنظیم مقدار Inches، تبدیل با ضرب مقدار در ۲٫۵۴ انجام میشود و هر دو فیلد centimeters و inches بهروزرسانی میشوند. اتصال داده تضمین میکند تغییرات ایجادشده توسط setter در رابط کاربری منعکس شوند.
double inches = 1; // default value
public double Inches
{
get => inches;
set
{
centimeters = value * 2.54;
inches = value;
}
}
بازنمایی درونخطی صفحهٔ 59 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 60 از ۱۲۱بازگشت به فهرست
همین منطق را برای سانتیمتر تکرار میکنیم: ویژگی دیگری میافزاییم و هنگام تنظیم مقدار، تبدیل لازم برای فیلد inches را اعمال میکنیم. عناصر HTML دیگری مانند برچسبها نیز برای تکمیل رابط کاربری افزوده میشوند.
@page "/converter"
<h1>Convert Inch to/from Centimeter</h1>
<label>Inches</label>
<input type="number" @bind="Inches" />
<span> = </span>
<input type="number" @bind="Centimeters" />
<label>Centimeters</label>
@code {
double inches = 1; // default value
double centimeters = 2.54; // default value
public double Inches
{
get => inches;
set
{
centimeters = value * 2.54;
inches = value;
}
}
public double Centimeters
{
get => centimeters;
set
{
inches = value / 2.54;
centimeters = value;
}
}
}
اکنون ابزار تبدیل نمایشدادهشده در شکل ۲۲ کامل است. ورودیها بهطور کامل به داده متصلاند و وقتی کاربر هرکدام از مقادیر را تغییر دهد، همهٔ مقدارها همگام باقی میمانند.
اتصال دوطرفهٔ پیشرفته
بازنمایی درونخطی صفحهٔ 60 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 61 از ۱۲۱بازگشت به فهرست
مؤلفهٔ تبدیل کامل شده و اکنون با تغییر هرکدام از ورودیها بهروزرسانی میشود. به هدف رسیدهایم، اما هنوز جای بهبود وجود دارد. درحالحاضر، پیش از آنکه اتصال داده انجام شود باید رویداد onchange فعال گردد؛ یعنی بهروزرسانی زمانی رخ میدهد که عنصر تمرکز خود را از دست بدهد، مثلاً وقتی روی جای دیگری کلیک کنیم یا با کلید Tab از ورودی خارج شویم.
بهجای رفتار پیشفرض onchange، نمونه را طوری تغییر میدهیم که به oninput متصل شود؛ رویدادی که بلافاصله هنگام تایپ رخ میدهد. برای تغییر رویداد پیشفرض اتصال، از دستور @bind:event استفاده و نام رویداد "oninput" را مشخص میکنیم.
<input type="number"
@bind="Inches"
@bind:event="oninput" />
<input type="number"
@bind="Centimeters"
@bind:event="oninput" />
اکنون رابط کاربری همزمان با تایپ کاربر یا هر تغییر دیگری در مقدار ورودی بهروزرسانی میشود.
دستور اتصال انعطافپذیر است و میتوان آن را برای ویژگیهای مشخص نیز پیکربندی کرد. برای نمونه، میتوان ویژگی پیشفرض و رویداد متناظر را با نحو زیر تعیین کرد:
@bind-propertyName @bind-propertyName:event="eventName"
نمونهٔ زیر همان اثر قبلی را دارد، اما نشان میدهد چگونه بهطور صریح به ویژگی value متصل شویم.
<input type="number"
@bind-value="Inches"
@bind-value:event="oninput" />
<input type="number"
@bind-value="Centimeters"
@bind-value:event="oninput" />
جمعبندی اتصال داده
اتصال داده در بلیزر نهفقط راحت، بلکه انعطافپذیر است. چه از اتصال یکطرفه استفاده کنیم و چه دوطرفه، کنترل کاملی بر صفت اتصال و رویدادی داریم که بهروزرسانی را فعال میکند. این سادگی، نیاز به نوشتن دستی کدهای تکراری را کاهش میدهد. هنگامی که اتصال دوطرفه همراه با getter و setterهای ویژگیهای C# استفاده شود، میتوانیم شیوهٔ مدیریت مقادیر متصلشده را با دقت تنظیم کنیم.
بازنمایی درونخطی صفحهٔ 61 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 62 از ۱۲۱بازگشت به فهرست
توضیح RenderTree بلیزر
بلیزر بر پایهٔ انتزاعی از DOM به نام RenderTree ساخته شده است. در این فصل میآموزیم انتزاع DOM دقیقاً چیست، RenderTree برای چه کاری استفاده میشود و چرا توسعهدهندگان بلیزر باید آن را بشناسند.
انتزاع مدل شیء سند (Document Object Model یا DOM) ممکن است ترسناک و پیچیده به نظر برسد، اما در برنامههای وب مدرن به امری معمول تبدیل شده است. دلیل اصلی این است که بهروزرسانی محتوای رندرشده در مرورگر از نظر محاسباتی پرهزینه است. انتزاعهای DOM میان برنامه و مرورگر واسطه میشوند تا مقدار بخشی از صفحه که دوباره رندر میشود کاهش یابد.
برای درک واقعی تأثیر RenderTree بلیزر بر برنامه، ابتدا باید مبانی را مرور کنیم.
با یک تعریف کوتاه از DOM آغاز میکنیم. مدل شیء سند یا DOM یک رابط مستقل از پلتفرم و زبان است که سند XML یا HTML را بهصورت ساختار درختی در نظر میگیرد. در ساختار درختی DOM، هر گره یک شیء است که بخشی از سند را تشکیل میدهد؛ بنابراین DOM ساختار سندی با یک درخت منطقی است.
DOM رابطی مستقل از پلتفرم و زبان است که سند XML یا HTML را بهصورت ساختار درختی در نظر میگیرد.
بازنمایی درونخطی صفحهٔ 62 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 63 از ۱۲۱بازگشت به فهرست
هنگامی که برنامهٔ وب در مرورگر بارگذاری میشود، یک DOM جاوااسکریپتی ساخته میشود. این درخت از اشیا بهعنوان رابط میان جاوااسکریپت و سند واقعی در مرورگر عمل میکند. هنگام ساخت برنامههای وب پویا یا برنامههای تکصفحهای (SPA) با جاوااسکریپت، از سطح API مربوط به DOM استفاده میکنیم.
استفاده از DOM برای ساخت، بهروزرسانی و حذف عناصر HTML، تغییر CSS و دیگر ویژگیها «دستکاری DOM» (DOM Manipulation) نامیده میشود. افزون بر دستکاری DOM، میتوانیم با کمک آن رویدادها را ایجاد و مدیریت کنیم.
در نمونهٔ کد زیر، یک صفحهٔ وب پایه با دو عنصر h1 و p داریم. هنگامی که مرورگر سند را بارگذاری میکند، DOMای ساخته میشود که عناصر HTML را نمایندگی میکند. در شکل ۲۴، نمایی از DOM را بهصورت گرههای یک درخت میبینیم.
<!DOCTYPE html>
<html>
<body>
<h1>Hello World</h1>
<p id="beta">This is a sample document.</p>
</body>
</html>
شکل ۲۴: یک سند HTML بهصورت درختی از گرهها بارگذاری میشود؛ هر شیء نمایندهٔ عنصری در DOM است.
بازنمایی درونخطی صفحهٔ 63 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 64 از ۱۲۱بازگشت به فهرست
با جاوااسکریپت میتوانیم با ارجاع صریح به اشیای درخت در DOM حرکت کنیم. از گرهٔ ریشهٔ document آغاز میکنیم و در فرزندان اشیا پیش میرویم تا به شیء یا ویژگی موردنظر برسیم. برای نمونه، میتوانیم فرزند دوم شاخهٔ body را با document.body.children[1] بگیریم و سپس مقدار innerText را بهعنوان ویژگی بخوانیم.
document.body.children[1].innerText
"This is a sample document."
راه سادهتر برای دریافت همان عنصر، استفاده از تابعی است که DOM را بر اساس یک پرسوجوی مشخص جستوجو میکند. چند متد کمکی برای جستوجوی DOM با انتخابگرهای مختلف وجود دارد. برای نمونه، عنصر p را با شناسهٔ beta و تابع getElementById دریافت میکنیم.
document.getElementById("beta").innerText
"This is a sample document."
در طول تاریخ وب، چارچوبها کار با DOM را سادهتر کردهاند. jQuery چارچوبی با API گسترده برای دستکاری DOM است. در نمونهٔ زیر دوباره متن عنصر p را دریافت میکنیم. با متد $ در jQuery میتوانیم بهآسانی عنصر را با صفت id پیدا و به متن آن دسترسی پیدا کنیم.
//jQuery
$("#beta").text()
"This is a sample document."
نقطهٔ قوت jQuery متدهای کمکیای است که مقدار کد لازم برای یافتن و دستکاری اشیا را کاهش میدهند. بااینحال، بزرگترین ضعف این رویکرد، مدیریت ناکارآمد بهروزرسانیها بهسبب تغییر مستقیم عناصر DOM است. چون دستکاری مستقیم DOM از نظر محاسباتی پرهزینه است، باید با احتیاط انجام شود.
چون دستکاری مستقیم DOM از نظر محاسباتی پرهزینه است، باید با احتیاط انجام شود.
در بیشتر برنامهها معمول است که چند عملیات برای بهروزرسانی DOM انجام شود. با رویکرد معمول جاوااسکریپت یا jQuery ممکن است یک گره را از درخت حذف و محتوای جدیدی جایگزین آن کنیم. هنگام این نوع بهروزرسانی، عناصر و فرزندانشان اغلب حتی وقتی نیازی به تغییر ندارند حذف و دوباره ساخته میشوند.
بازنمایی درونخطی صفحهٔ 64 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 65 از ۱۲۱بازگشت به فهرست
در نمونهٔ زیر، چند عنصر مشابه با انتخابگر عام n-elements حذف میشوند. سپس عناصر دوباره جایگزین میشوند؛ حتی اگر فقط نیازمند اصلاح بوده باشند. همانطور که در شکل ۲۵ میبینیم، عناصر زیادی حذف و جایگزین میشوند، درحالیکه تنها دو مورد به بهروزرسانی نیاز داشتند.
// 1 = initial state
$("n-elements").remove() // 2-3
$("blue").append(modifiedElement1) // 4
$("green").append(modifiedElement2) // 4
$("orange").append(modifiedElement3) // 4
شکل ۲۵: ۱) وضعیت اولیه؛ ۲) عناصر مشابه برای حذف انتخاب میشوند؛ ۳) عناصر و فرزندانشان از DOM حذف میشوند؛ ۴) همهٔ عناصر جایگزین میشوند، درحالیکه فقط برخی تغییر کردهاند.
در یک برنامهٔ بلیزر، مسئول ایجاد مستقیم تغییر در DOM نیستیم. بلیزر بهجای آن، یک لایهٔ انتزاعی میان DOM و کدی که برای برنامه مینویسیم قرار میدهد. انتزاع DOM در بلیزر RenderTree نام دارد و نسخهای سبک از وضعیت DOM است.
بازنمایی درونخطی صفحهٔ 65 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 66 از ۱۲۱بازگشت به فهرست
RenderTree را میتوان کارآمدتر از DOM بهروزرسانی کرد و چند تغییر را در یک بهروزرسانی واحد DOM ادغام نمود. برای بیشینهکردن کارایی، RenderTree از الگوریتم مقایسهٔ تفاوتها (Diffing Algorithm) استفاده میکند تا فقط عناصر ضروری در DOM مرورگر تغییر کنند.
اگر در یک محدودهٔ کاری واحد چند بهروزرسانی روی عناصر برنامهٔ بلیزر انجام دهیم، DOM فقط تغییرات حاصل از تفاوت نهایی را دریافت میکند. هنگام انجام کار، نسخهٔ جدیدی از RenderTree بر اساس تغییرات ناشی از کد یا اتصال داده ساخته میشود. وقتی مؤلفه آمادهٔ رندر دوباره باشد، وضعیت جاری با وضعیت جدید مقایسه و یک Diff تولید میشود. در زمان بهروزرسانی، فقط مقادیر متفاوت روی DOM اعمال میشوند.
برای دیدن اینکه RenderTree چگونه میتواند بهروزرسانیهای DOM را کاهش دهد، دقیقتر نگاه میکنیم. در شکل ۲۶، وضعیت اولیه شامل سه عنصر سبز، آبی و نارنجی است که قرار است تغییر کنند.
شکل ۲۶: وضعیت اولیهٔ RenderTree در سمت چپ و DOM در سمت راست. عناصر دارای مقادیر سبز، آبی و نارنجی توسط کد تحت تأثیر قرار خواهند گرفت.
در شکل ۲۷، چند مرحلهٔ کار درون یک چرخهٔ واحد انجام میشود. اعضا حذف و جایگزین میشوند، اما نتیجه فقط مقدار سبز و آبی را با هم عوض میکند. پس از پایان چرخهٔ عمر، تفاوتها با یکدیگر تطبیق داده میشوند.
بازنمایی درونخطی صفحهٔ 66 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 67 از ۱۲۱بازگشت به فهرست
شکل ۲۷: ۱) RenderTree جاری؛ ۲ تا ۴) برخی عناصر حذف، جایگزین و بهروزرسانی میشوند؛ ۵) وضعیت جاری و جدید برای یافتن تفاوت مقایسه میشوند.
شکل ۲۸: تفاوت RenderTree برای بهروزرسانی فقط عناصری استفاده میشود که در طول عملیات تغییر کردهاند.
ساخت RenderTree
در یک برنامهٔ بلیزر، مؤلفههای Razor با پسوند .razor در واقع کاملاً متفاوت از نشانهگذاری سنتی Razor Pages یا Viewها با پسوند .cshtml پردازش میشوند. Razor در زمینهٔ MVC یا Razor Pages فرایندی یکطرفه است که در سمت سرور به HTML رندر میشود.
بازنمایی درونخطی صفحهٔ 67 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 68 از ۱۲۱بازگشت به فهرست
یک مؤلفه در بلیزر رویکرد متفاوتی دارد: نشانهگذاری آن برای تولید یک کلاس C# استفاده میشود که RenderTree را میسازد. برای دیدن نحوهٔ ایجاد RenderTree، این فرایند را دقیقتر بررسی میکنیم.
هنگامی که یک مؤلفهٔ Razor ساخته میشود، فایل .razor به پروژه افزوده میشود و محتوای آن برای تولید یک کلاس C# به کار میرود. کلاس تولیدشده از ComponentBase ارث میبرد؛ کلاسی که متد BuildRenderTree مؤلفه را در بر دارد، همانطور که در شکل ۲۹ نشان داده شده است.
BuildRenderTree متدی است که یک شیء RenderTreeBuilder دریافت میکند و با ترجمهٔ نشانهگذاری به اشیای RenderTree، مؤلفه را به درخت اضافه مینماید.
شکل ۲۹: نمودار کلاس ComponentBase با متد BuildRenderTree که برجسته شده است.
بازنمایی درونخطی صفحهٔ 68 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 69 از ۱۲۱بازگشت به فهرست
با استفاده از نمونهٔ مؤلفهٔ Counter موجود در قالب .NET میتوانیم ببینیم کد مؤلفه چگونه به یک کلاس تولیدشده تبدیل میشود. در مؤلفهٔ Counter، اجزای مهم زیر را میتوانیم در RenderTree نهایی شناسایی کنیم:
- دستور مسیریابی صفحه
- عنصر
h1 بهعنوان یک عنصر پایهٔ HTML
- عنصر
p و currentCount بهعنوان ترکیبی از محتوای ثابت و فیلد متصل به داده
- دکمه با مدیریتکنندهٔ رویداد
onclick به نام IncrementCount
- بلوک کد شامل کد C#
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary"
@onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
کد نمونهٔ Counter برای تولید کلاسی با متد دقیق BuildRenderTree استفاده میشود که اشیای درخت را توصیف میکند. با بررسی کلاس تولیدشده میبینیم اجزای اصلی چگونه به کد خالص C# ترجمه شدهاند:
- دستور صفحه به یک صفت روی کلاس تبدیل میشود.
AddMarkupContent محتوای HTML مانند عنصر h1 را تعریف میکند.
- عناصر ترکیبی مانند
p و currentCount به گرههای جداگانه با انواع محتوای مشخص، یعنی OpenElement و AddContent، تبدیل میشوند.
- دکمه اشیای صفت مربوط به CSS و مدیریتکنندهٔ رویداد
onclick را در بر میگیرد.
- کد درون بلوک کد بهعنوان C# ارزیابی میشود.
Counter یک کلاس عمومی است که از ComponentBase ارث میبرد.
[Route("/counter")]
public class Counter : ComponentBase
{
بازنمایی درونخطی صفحهٔ 69 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 70 از ۱۲۱بازگشت به فهرست
private int currentCount = 0;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddMarkupContent(0, "<h1>Counter</h1>\r\n\r\n");
builder.OpenElement(1, "p");
builder.AddContent(2, "Current count: ");
builder.AddContent(3, this.currentCount);
builder.CloseElement();
builder.AddMarkupContent(4, "\r\n\r\n");
builder.OpenElement(5, "button");
builder.AddAttribute(6, "class", "btn btn-primary");
builder.AddAttribute<MouseEventArgs>(
7,
"onclick",
EventCallback.Factory.Create<MouseEventArgs>(
this,
new Action(this, Counter.IncrementCount)));
builder.AddContent(8, "Click me");
builder.CloseElement();
}
private void IncrementCount()
{
this.currentCount++;
}
}
میبینیم نشانهگذاری و کد چگونه به قطعهای بسیار ساختیافته از منطق تبدیل میشوند. همهٔ بخشهای مؤلفه در RenderTree نمایندگی میشوند تا بتوان آنها را بهطور کارآمد به DOM منتقل کرد.
هر عضو در RenderTree یک شمارهٔ توالی نیز دارد؛ مانند AddContent(num, value). شمارههای توالی برای کمک به الگوریتم Diff و افزایش کارایی استفاده میشوند. یک عدد صحیح خام، شاخصی فوری در اختیار سامانه قرار میدهد تا با بررسی ترتیب و وجود یا نبود شمارهٔ توالی، تشخیص دهد تغییری رخ داده است یا نه. برای نمونه، با مقایسهٔ دنبالهٔ اشیای 1,2,3 با 1,3 میتوان نتیجه گرفت که عضو ۲ از DOM حذف شده است.
RenderTree ابزار قدرتمندی است که ابزارهای هوشمند آن را از ما پنهان کردهاند. همانطور که در نمونههای قبلی دیدیم، مؤلفههای ما فقط کلاسهای استاندارد C# هستند. میتوان این کلاسها را با ارثبری از ComponentBase و نوشتن دستی متد RenderTreeBuilder ساخت؛ اما این کار توصیه نمیشود و رویهای نامناسب است.
RenderTreeهای دستی هنگامی مشکلساز میشوند که شمارهٔ توالی عددی خطی و ثابت نباشد. الگوریتم Diff به پیشبینیپذیری کامل نیاز دارد؛ در غیر این صورت ممکن است مؤلفه بیدلیل دوباره رندر شود و کارایی آن از بین برود.
بازنمایی درونخطی صفحهٔ 70 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 71 از ۱۲۱بازگشت به فهرست
RenderTreeهایی که دستی نوشته میشوند، اگر شمارهٔ توالی آنها عددی خطی و ثابت نباشد میتوانند مشکلساز شوند.
بهینهسازی رندر مؤلفه
وقتی در بلیزر با فهرستی از عناصر یا مؤلفهها کار میکنیم، باید رفتار فهرست و هدف استفاده از مؤلفهها را در نظر بگیریم. در نهایت، الگوریتم Diff بلیزر باید تصمیم بگیرد کدام عناصر یا مؤلفهها حفظ شوند و اشیای RenderTree چگونه به آنها نگاشت شوند. معمولاً میتوان جزئیات این الگوریتم را نادیده گرفت، اما در مواردی ممکن است بخواهیم فرایند را کنترل کنیم:
- فهرستی که رندر میشود - برای نمونه در یک بلوک
@foreach - و هر عضو آن شناسهای یکتا دارد.
- فهرستی با عناصر فرزند که ممکن است با درج، حذف یا تغییر ترتیب اعضا تغییر کند.
- مواردی که رندر دوباره به تفاوت رفتاری قابل مشاهدهای مانند ازدسترفتن تمرکز عنصر منجر میشود.
فرایند نگاشت RenderTree را میتوان با صفت دستور @key کنترل کرد. با افزودن @key به الگوریتم Diff میگوییم عناصر یا مؤلفههای مرتبط با مقدار کلید را حفظ کند. نمونهای را بررسی میکنیم که به @key نیاز دارد و هر سه معیار بالا را برآورده میسازد.
یک فهرست نامرتب ul ساخته میشود. درون هر عضو li، یک h1 مقدار کلاس Color را نمایش میدهد و یک input نیز بهشکل چکباکس وجود دارد. برای شبیهسازی عملیاتی مانند مرتبسازی، درج یا حذف اعضا، دکمهای برای معکوسکردن فهرست اضافه میکنیم. دکمه از تابع درونخطی items = items.Reverse() استفاده میکند تا با کلیک، آرایهٔ اعضا را معکوس کند.
<ul class="list-group">
@foreach (var item in items)
{
<li class="list-group-item">
<h1>@item.Value</h1>
<input type="checkbox" />
</li>
}
</ul>
<button @onclick="_ => items = items.Reverse()">Reverse</button>
بازنمایی درونخطی صفحهٔ 71 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 72 از ۱۲۱بازگشت به فهرست
@code {
// class Color {int Id, string Value}
IEnumerable<Color> items = new Color[] {
new Item {Id = 0, Value = "Green" },
new Item {Id = 1, Value = "Blue" },
new Item {Id = 2, Value = "Orange" },
new Item {Id = 3, Value = "Purple" }
};
}
وقتی برنامه را اجرا کنیم، فهرست با یک چکباکس برای هر عضو رندر میشود. اگر چکباکس عضو «Green» را انتخاب و سپس فهرست را معکوس کنیم، چکباکس انتخابشده همچنان در بالای فهرست باقی میماند و اکنون در عضو «Purple» قرار گرفته است. دلیل این رفتار آن است که الگوریتم Diff فقط متن هر عنصر h1 را بهروزرسانی کرده است. وضعیت اولیه و معکوسشده در شکل ۳۰ نمایش داده شدهاند؛ توجه کنید جای چکباکس تغییر نکرده است.
شکل ۳۰: خطای رندر قابل مشاهده است؛ هنگام معکوسشدن آرایه، چکباکس جابهجا نمیشود و DOM زمینهٔ رابطهٔ عنصر را از دست میدهد.
میتوانیم از دستور @key برای ارائهٔ اطلاعات بیشتر به RenderTree استفاده کنیم. @key مشخص میکند هر عضو فهرست چگونه با فرزندانش ارتباط دارد. با این اطلاعات اضافی، الگوریتم Diff میتواند ساختار عنصر را حفظ کند. در نمونه، Id عضو را به @key اختصاص میدهیم و برنامه را دوباره اجرا میکنیم.
@foreach (var item in items)
{
<li @key="item.Id" class="list-group-item">
<h1>@item.Value</h1>
<input type="checkbox" />
بازنمایی درونخطی صفحهٔ 72 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 73 از ۱۲۱بازگشت به فهرست
</li>
}
با اعمال دستور @key، RenderTree اعضای فهرست و عناصر فرزند مرتبط با آنها را ایجاد، جابهجا یا حذف میکند. اگر چکباکس عضو «Green» را انتخاب و فهرست را معکوس کنیم، چکباکس انتخابشده نیز جابهجا میشود؛ زیرا RenderTree کل گروه عناصر li را درون فهرست منتقل میکند. این رفتار در شکل ۳۱ دیده میشود.
شکل ۳۱: با استفاده از صفت کلید، عناصر رابطهٔ خود را حفظ میکنند و هنگام بهروزرسانی DOM، چکباکس در ظرف مناسب باقی میماند.
در این نمونه، سناریویی ایدهآل داشتیم که معیارهای نیاز به @key را برآورده میکرد و توانستیم خطاهای بصری ناشی از رندر دوبارهٔ فهرست را برطرف کنیم. بااینحال، کاربردها همیشه تا این اندازه آشکار نیستند؛ بنابراین باید با دقت پیامدهای اعمال @key را بررسی کنیم.
وقتی @key استفاده نشود، بلیزر تا حد امکان نمونههای عناصر و مؤلفههای فرزند را حفظ میکند. مزیت @key این است که بهجای آنکه الگوریتم Diff نگاشت را انتخاب کند، ما کنترل میکنیم نمونههای مدل چگونه به نمونههای حفظشدهٔ مؤلفه نگاشت شوند. استفاده از @key هزینهٔ اندکی برای عملیات Diff دارد؛ اما اگر عناصر بهدرستی توسط RenderTree حفظ شوند، میتواند در مجموع سودمند باشد.
جمعبندی
هرچند RenderTree با استفاده از نحو Razor در فایلهای .razor از ما پنهان شده است، درک تأثیر آن بر شیوهٔ نوشتن کد اهمیت دارد. همانطور که در نمونه دیدیم، شناخت RenderTree و نحوهٔ کار آن هنگام نوشتن مؤلفههایی که یک سلسلهمراتب را مدیریت میکنند ضروری است.
بازنمایی درونخطی صفحهٔ 73 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 74 از ۱۲۱بازگشت به فهرست
صفت @key هنگام کار با مجموعهها و سلسلهمراتب اهمیت ویژهای دارد؛ زیرا کمک میکند RenderTree بهینه شود و از خطاهای رندر جلوگیری گردد.
RenderTree موضوعی است که هنگام بررسی فصل بعد دربارهٔ تعامل با جاوااسکریپت نیز باید در نظر بگیریم، زیرا جاوااسکریپت میتواند بر DOM اثر بگذارد.
بازنمایی درونخطی صفحهٔ 74 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 75 از ۱۲۱بازگشت به فهرست
قابلیت تعامل با جاوااسکریپت
در فصل پیش آموختیم مؤلفهها چگونه رندر میشوند. در این فصل میبینیم جاوااسکریپت چگونه میتواند با برنامههای بلیزر ارتباط برقرار کند.
بلیزر دارای API تعامل با جاوااسکریپت (JavaScript Interoperability یا Interop) است. از طریق JavaScript Interop، یک برنامهٔ بلیزر میتواند توابع جاوااسکریپت را از C# فراخوانی کند. در جهت عکس، متدهای C# نیز میتوانند از کد جاوااسکریپت فراخوانی شوند. بنابراین میتوان Callbackهای رفتوبرگشتی داشت که به هر دو پلتفرم اجازه میدهند با هم کار کنند.
تعامل با جاوااسکریپت برای توسعهٔ بلیزر یک ضرورت است. از آنجا که بلیزر از طریق وباسمبلی دسترسی کامل به DOM ندارد، بسیاری از APIهای DOM که معمولاً به آنها نیاز داریم با دسترسی مستقیم به جاوااسکریپت پشتیبانی میشوند. کاربردهای این تعامل شامل فراخوانی APIهای DOM مانند GeoLocation، MatchMedia، Web Storage یا localStorage، کوکیهای مرورگر و دستکاری مستقیم DOM است.
هرچند دسترسی و دستکاری مستقیم DOM از طریق Interop ممکن است، باید تأثیر احتمالی آن بر RenderTree بلیزر را در نظر گرفت.
API تعامل با جاوااسکریپت چهار بخش اصلی دارد:
IJSRuntime: لایهٔ انتزاعی زماناجرای جاوااسکریپت که متد InvokeAsync را فراهم میکند.
InvokeAsync: برای فراخوانی یک متد جاوااسکریپت و بازگرداندن مقدار استفاده میشود.
InvokeVoidAsync: متد توسعهای برای فراخوانی متد جاوااسکریپتی که مقدار بازگشتی ندارد.
بازنمایی درونخطی صفحهٔ 75 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 76 از ۱۲۱بازگشت به فهرست
JSInvokable: صفتی برای مشخصکردن متد .NET که میتواند از جاوااسکریپت فراخوانی شود.
برای درک بهتر زمان استفاده از JavaScript Interop و متدهای لازم، چند نمونه را بررسی میکنیم تا با Interop تعامل داشته باشیم و ببینیم چرا جاوااسکریپت برای یک برنامهٔ بلیزر ضروری است.
فراخوانی جاوااسکریپت از .NET
در این نمونه میآموزیم چگونه جاوااسکریپت را از .NET فراخوانی کنیم. هدف، ساخت قابلیتی است که مطابق شکل ۳۲ به برنامه اجازه دهد میان پوستهٔ روشن و تاریک جابهجا شود.
شکل ۳۲: یک دکمهٔ تغییر وضعیت به کاربران اجازه میدهد میان پوستهٔ روشن و تاریک برنامه جابهجا شوند.
چون برنامههای بلیزر از فناوریهای استاندارد وب ساخته شدهاند، بهترین راه پیادهسازی این قابلیت، تغییر CSS برنامه است. برای تغییر کامل پوسته باید میان دو فایل CSS مجزا که تمام مقادیر هر پوسته را دارند جابهجا شویم: light.css و dark.css. فرض میکنیم این فایلها از قبل مطابق شکل ۳۳ فراهم شدهاند. خود CSS موضوع بحث ما نیست؛ فقط باید بدانیم با اعمال هر پوسته، برنامه از پالت روشن یا تاریک استفاده خواهد کرد.
بازنمایی درونخطی صفحهٔ 76 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 77 از ۱۲۱بازگشت به فهرست
شکل ۳۳: پروژه، پوستهٔ روشن و تاریک را بهصورت فایلهای CSS جداگانه در پوشهٔ wwwroot در بر دارد.
برنامههای بلیزر از فایل _Host.cshtml یا index.html برای میزبانی صفحهٔ SPA سمت کاربر استفاده میکنند. بخش پویای برنامه درون عنصر app صفحه قرار دارد. مؤلفهها و کد نمیتوانند بر عناصر بیرون از app اثر بگذارند؛ از جمله عناصر head صفحه.
<head>
<!-- Blazor has no scope here -->
<meta charset="utf-8" />
<base href="~/" />
...
<link href="css/light.css" rel="stylesheet" />
<!-- /Blazor has no scope here -->
</head>
<body>
<app>
<!-- Blazor (RenderTree) exist here -->
<component type="typeof(App)"
render-mode="ServerPrerendered" />
<!-- /Blazor (RenderTree) exist here -->
</app>
</body>
برای آنکه برنامه بتواند میان پوستهٔ روشن و تاریک جابهجا شود، باید عنصر link درون head را جایگزین کنیم. چون بلیزر در دامنهٔ این عنصر نیست، برای اصلاح head به JavaScript Interop متکی خواهیم بود. از آنجا که جاوااسکریپت ما روی عناصری بیرون از دامنهٔ بلیزر کار میکند، احتمال تداخل با RenderTree وجود ندارد.
پیش از نوشتن کد Interop، مؤلفهای میسازیم که در نهایت پوستهٔ برنامه را تغییر دهد. نمونهٔ مؤلفهٔ انتخاب پوسته در شکل ۳۲ دیده میشود. مؤلفهٔ جدید ThemeChooser دکمهای به کاربر ارائه میکند. فشردن دکمه، پرچم بولی isDark را تغییر میدهد و متن را از Light به Dark یا برعکس تبدیل میکند. مقدار متنی پوسته با ویژگی SetThemeName نمایندگی میشود. وقتی مؤلفه نمایش داده شود، متن آن چنین خواهد بود: Click here to switch themes [Dark Mode].
بازنمایی درونخطی صفحهٔ 77 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 78 از ۱۲۱بازگشت به فهرست
در این مرحله، مؤلفه توان محدودی دارد و فقط پیام متنی را تغییر میدهد، نه پوستهٔ واقعی برنامه را.
<div class="alert alert-info my-2">
Click here to switch themes
<TelerikButton OnClick="SwapTheme"
Primary="true"
Icon="@IconName.InvertColors">
@SetThemeName Mode
</TelerikButton>
</div>
@code {
bool isDark;
string SetThemeName => isDark ? "Dark" : "Light";
async Task SwapTheme()
{
isDark = !isDark;
}
}
سپس فایل جاوااسکریپتی themeChooser.js را به برنامه اضافه میکنیم که کد لازم برای تغییر عنصر link برنامه را دارد. مطابق شکل ۳۴، فایل themeChooser.js در پوشهٔ wwwroot برنامه قرار میگیرد.
شکل ۳۴: فایل themeChooser.js که قابلیت Interop را فراهم میکند در پوشهٔ wwwroot ذخیره شده است.
برای آنکه برنامهٔ بلیزر بتواند به تابع جاوااسکریپت دسترسی پیدا کند، تابع باید در دامنهٔ سراسری window در دسترس باشد. برای این کار فضای نام themeChooser را به window اضافه و تابع setTheme را زیر آن تعریف میکنیم. نامگذاری اهمیت دارد، زیرا بعداً باید این توابع را از کد بلیزر فراخوانی کنیم و IntelliSense نمیتواند اشتباههای نامگذاری را اصلاح کند.
بازنمایی درونخطی صفحهٔ 78 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 79 از ۱۲۱بازگشت به فهرست
window.themeChooser = {
setTheme: function (themeName) { … theme swap code }
}
در تابع setTheme، یک عنصر link جدید با استفاده از پارامتر themeName ساخته میشود. هنگام فراخوانی تابع، themeName از کد C# ارسال خواهد شد. این نام با مسیر فایل CSS و پسوند .css ترکیب و در صفت href لینک قرار میگیرد. در پایان، عنصر head در DOM پیدا میشود، لینک اصلی حذف و لینک جدید جایگزین آن میشود. مرورگر تغییر را تشخیص میدهد و صفحه را بهطور خودکار با منبع CSS جدید بهروزرسانی میکند.
window.themeChooser = {
setTheme: function (themeName) {
// Build the new css link
let newLink = document.createElement("link");
newLink.setAttribute("id", "theme");
newLink.setAttribute("rel", "stylesheet");
newLink.setAttribute("type", "text/css");
newLink.setAttribute("href", `css/${themeName}.css`);
// Remove and replace the theme
let head = document.getElementsByTagName("head")[0];
head.querySelector("#theme").remove();
head.appendChild(newLink);
}
}
سپس فایل themeChooser.js را بهعنوان منبع ایستا به برنامه اضافه میکنیم. در فایل میزبان برنامه، فقط با یک تگ script به آن ارجاع میدهیم.
<head>
…
<link id="theme" href="css/light.css" rel="stylesheet" />
<script src="js/themeChooser.js"></script>
</head>
اکنون برنامه همهٔ اجزای لازم برای استفاده از کد themeChooser.js را دارد. به مؤلفهٔ بلیزر بازمیگردیم و کدی برای فراخوانی تابع setTheme از طریق JavaScript Interop اضافه میکنیم.
برای فراخوانی جاوااسکریپت از .NET از انتزاع IJSRuntime استفاده میشود. نمونهٔ IJSRuntime با تزریق وابستگی ساخته میشود و در پیکربندی پیشفرض سرویسهای بلیزر وجود دارد. از این نمونه میتوان متدهای InvokeAsync<T> و InvokeVoidAsync را فراخوانی کرد. در اینجا از InvokeVoidAsync استفاده میکنیم، زیرا تابع setTheme مقدار بازگشتی ندارد.
بازنمایی درونخطی صفحهٔ 79 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 80 از ۱۲۱بازگشت به فهرست
متد SwapTheme با فراخوانی await js.InvokeVoidAsync بهروزرسانی میشود. پارامتر نخست، فضای نام و نام تابع جاوااسکریپت را مشخص میکند. نام پوسته بهعنوان پارامتر دوم ارسال و به تابع setTheme در جاوااسکریپت منتقل میشود. در این نمونه فقط یک رشتهٔ ساده ارسال میکنیم، اما هر شیء قابل سریالسازی به JSON قابل استفاده است.
@inject IJSRuntime js
<div class="alert alert-info my-2">
Click here to switch themes
<TelerikButton OnClick="SwapTheme"
Primary="true"
Icon="@IconName.InvertColors">
@SetThemeName Mode
</TelerikButton>
</div>
@code {
bool isDark;
string SetThemeName => isDark ? "Dark" : "Light";
async Task SwapTheme()
{
isDark = !isDark;
await js.InvokeVoidAsync(
"themeChooser.setTheme",
SetThemeName);
}
}
این قابلیت اکنون کامل است و برنامه میتواند مطابق شکل ۳۵ با یک کلیک میان پوستهٔ روشن و تاریک جابهجا شود.
شکل ۳۵: مؤلفهٔ انتخاب پوسته با استفاده از JavaScript Interop، پوستهٔ برنامه را از روشن به تاریک تغییر میدهد.
بازنمایی درونخطی صفحهٔ 80 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 81 از ۱۲۱بازگشت به فهرست
در نمونهٔ قبلی دیدیم چگونه با JavaScript Interop توابع جاوااسکریپت را از درون C# اجرا کنیم. توانستیم آرگومانهایی به جاوااسکریپت بفرستیم و از مقادیر آنها برای تغییر پوستهٔ برنامه استفاده کنیم. در نمونهٔ بعدی میبینیم چگونه با ارسال مقدار از جاوااسکریپت به C# و اجرای کد C# از جاوااسکریپت، عملیات رفتوبرگشتی انجام دهیم.
فراخوانی .NET از جاوااسکریپت
در این نمونه میآموزیم چگونه مقادیر را از جاوااسکریپت به .NET بفرستیم و .NET را از جاوااسکریپت فراخوانی کنیم. هدف، ساخت قابلیتی است که مطابق شکل ۳۸ به برنامه اجازه دهد از API مکانیابی وب استفاده کند.
مکانیابی (Geolocation) گزینهٔ مناسبی برای JavaScript Interop است، زیرا بلیزر API مستقیمی برای آن ندارد. برای دسترسی به Geolocation API باید توابع جاوااسکریپت را فراخوانی کنیم تا API اجرا شود و سپس نتایج از طریق Callback به .NET بازگردند.
پیش از آغاز، مختصراً نحوهٔ کار Geolocation وب را مرور میکنیم. رابط Geolocation یک مشخصات W3C است که بیشتر مرورگرهای مدرن آن را از طریق ویژگی فقطخواندنی Navigator.geolocation پیادهسازی میکنند. این ویژگی شیئی از نوع Geolocation بازمیگرداند که با مجموعهای از توابع به مکان دستگاه دسترسی میدهد.
در این نمونه از تابع getCurrentPosition استفاده میکنیم تا شیء GeolocationCoordinates شامل عرض و طول جغرافیایی دستگاه کاربر بازگردانده شود. این قابلیت به برنامه اجازه میدهد بر اساس موقعیت کاربر نتایج سفارشی ارائه کند. در طول نمونه، کلاسهایی برای نمایش این اشیا در .NET اضافه میکنیم تا هنگام انتقال مقدارها میان جاوااسکریپت و C#، سریالسازی و بازسریالسازی سادهتر شود.
کار را با نمونهای ساده آغاز میکنیم که مرورگر را بررسی میکند تا مشخص شود Geolocation API پشتیبانی میشود یا نه. وجود navigator.geolocation را بررسی و نتیجه را بهصورت مقدار بولی بازمیگردانیم. ابتدا فضای نام blazorGeolocation را به window اضافه میکنیم. درون آن تابع hasGeolocationFeature را میسازیم که بررسی را انجام و مقدار true/false بازمیگرداند. این قطعهٔ کوتاه جاوااسکریپت نخستین کد Interop ما برای تعامل با Geolocation API است.
window.blazorGeolocation = {
hasGeolocaitonFeature: function () {
return navigator.geolocation ? true : false;
}
};
بازنمایی درونخطی صفحهٔ 81 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 82 از ۱۲۱بازگشت به فهرست
فایل را با نام blazorGeolocation.js ذخیره و در بخش head فایل _Host.cshtml یا index.html برنامه به آن ارجاع میدهیم.
<head>
…
<script src="~/js/geoLocation.js"></script>
</head>
نیاز بعدی، ساخت همتای .NET است که هم تابع hasGeolocationFeature را فراخوانی کند و هم نتیجه را بازگرداند. کلاس جدیدی به نام Geolocation میسازیم که Geolocation API را در .NET نمایندگی میکند. سازندهٔ این کلاس به یک نمونهٔ IJSRuntime نیاز دارد که درون کلاس استفاده خواهد شد.
public class Geolocation
{
private readonly IJSRuntime js;
public Geolocation(IJSRuntime js)
{
this.js = js;
}
}
در نمونهٔ قبلی IJSRuntime را بررسی کردیم و با InvokeVoidAsync یک متد جاوااسکریپت را از C# فعال نمودیم. این بار از متد InvokeAsync<T> استفاده میکنیم که علاوه بر فعالکردن تابع جاوااسکریپت، انتظار دریافت نتیجهای از نوع T را دارد. در اینجا T همان مقدار بولی بازگشتی از hasGeoloactionFeature است.
متدی در کلاس Geolocation میسازیم که فراخوانی Interop را مدیریت و یک ValueTask<bool> بازگرداند. ValueTask تضمین میکند فراخوانی HasGeolocationFeature مقدار bool یا Task<bool> برگرداند.
public class Geolocation
{
…
public async ValueTask<bool> HasGeolocationFeature() =>
await js.InvokeAsync<bool>(
"blazorGeolocation.hasGeolocaitonFeature");
}
اکنون میتوانیم Interop را در یک مؤلفه آزمایش کنیم. باید IJSRuntime را به مؤلفه تزریق کنیم تا به شیء Geolocation داده شود. سپس فیلد بولی hasGeolocation را برای نگهداری نتیجهٔ فراخوانی میسازیم.
برای اطمینان از بارگذاری کامل صفحه و دسترسپذیری APIهای جاوااسکریپت، از متد چرخهٔ عمر OnAfterRenderAsync مؤلفه استفاده خواهیم کرد.
بازنمایی درونخطی صفحهٔ 82 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 83 از ۱۲۱بازگشت به فهرست
در OnAfterRenderAsync یک کلاس Geolocation جدید ساخته، متد HasGeolocationFeature فراخوانی و نتیجه در hasGeolocation ذخیره میشود. برای پایان عملیات، StateHasChanged را فراخوانی میکنیم تا رابط کاربری دوباره رندر و پیام Browser has Geolocation نمایش داده شود.
@inject IJSRuntime js
@using GeoLocation
@page "/"
<h1>Hello, world!</h1>
@if (hasGeoLocation)
{
<p>Browser has Geolocation</p>
}
@code {
bool hasGeoLocation;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var geo = new Geolocation(js);
hasGeoLocation = await geo.HasGeolocationFeature();
StateHasChanged();
}
}
}
هنگام بارگذاری برنامه، مرورگر تشخیص میدهد کد به قابلیت Geolocation دسترسی دارد و مطابق شکل ۳۶ فوراً از کاربر مجوز میخواهد. همزمان صفحه نیز مطابق شکل ۳۷ در دسترسبودن Geolocation را نشان میدهد.
بازنمایی درونخطی صفحهٔ 83 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 84 از ۱۲۱بازگشت به فهرست
شکل ۳۶: هنگامی که برای نخستین بار از جاوااسکریپت به Geolocation ارجاع داده شود، مرورگر از کاربر اجازهٔ استفاده از موقعیت مکانی را درخواست میکند.
شکل ۳۷: با بارگذاری صفحه، پیام Browser has Geolocation نمایش داده میشود و نشان میدهد جاوااسکریپت مقداری بازگردانده است.
تا اینجا Interop فقط یک نوع بولی ساده بازمیگرداند که در دسترسبودن قابلیت Geolocation را مشخص میکند. در مراحل بعد متدی اضافه میکنیم که نوع پیچیدهای شامل مختصات کاربر را از جاوااسکریپت دریافت کند. برای ارسال و دریافت نوعهای پیچیده میان C# و جاوااسکریپت، اشیا باید قابل سریالسازی به JSON باشند.
از تابع getCurrentPosition در Geolocation API استفاده خواهیم کرد. این تابع انتظار یک Callback دارد که شیء Position را بازمیگرداند.
Position {
coords: {
latitude: 38.2099893,
longitude: -85.21792610000001,
…
},
بازنمایی درونخطی صفحهٔ 84 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 85 از ۱۲۱بازگشت به فهرست
timestamp: 1583430690849
}
شیء Position همچنین یک Timestamp جاوااسکریپتی دارد که باید به مقداری سازگار با .NET تبدیل شود تا در C# بهدرستی سریالسازی گردد. برای این کار تابع کمکیای میسازیم که Position را میگیرد و Timestamp را به تاریخ جاوااسکریپت نگاشت میکند. این کار تضمین میکند هنگام ارسال به .NET، مقدار بهصورت Date سریالسازی شود.
window.blazorGeolocation = {
toSerializeable: function (e) {
return {
"coords": {
"latitude": e.coords.latitude,
"longitude": e.coords.longitude
},
"timestamp": new Date(e.timestamp)
};
},
hasGeolocaitonFeature: function () {
return navigator.geolocation ? true : false;
}
};
برای آنکه مقادیر در C# قابل سریالسازی باشند، چند کلاس برای دریافت مقادیر Callback جاوااسکریپت نیاز داریم. نام اشیا و ویژگیهایشان باید با همتای جاوااسکریپتی منطبق باشد.
getCurrentPosition(PositionCallback successCallback,
optional PositionErrorCallback errorCallback,
optional PositionOptions options);
PositionCallback = void (Position position);
PositionErrorCallback = void (PositionError positionError);
مشخصات دقیق رابط Geolocation در وبسایت W3C در دسترس است: www.w3.org/TR/geolocation-API/
public class PositionOptions
{
public bool EnableHighAccuracy { get; set; } = false;
public int Timeout { get; set; }
public int MaximumAge { get; set; } = 0;
}
public class Coords
{
public double Latitude { get; set; }
بازنمایی درونخطی صفحهٔ 85 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 86 از ۱۲۱بازگشت به فهرست
public double Longitude { get; set; }
}
public class Position
{
public Coords Coords { get; set; }
public DateTime Timestamp { get; set; }
}
علاوه بر Callback موفق، میتوانیم با یک Enum از PositionError نیز پشتیبانی کنیم.
public enum PositionError
{
PERMISSION_DENIED = 1,
POSITION_UNAVAILABLE,
TIMEOUT
}
اکنون که بخش .NET اشیای Geolocation کامل شده است، میتوانیم APIای در .NET بسازیم که خود را با پیادهسازی جاوااسکریپتی تابع getCurrentPosition تطبیق دهد.
ابتدا توابع Callbackای را میسازیم که جاوااسکریپت هنگام موفقیت یا شکست getCurrentPosition فراخوانی خواهد کرد. دو Action به نامهای OnGetPosition و OnGetPositionError ساخته میشوند تا Callbackها را در .NET مدیریت کنند.
برای آنکه جاوااسکریپت بتواند این Callbackها را فراخوانی کند، توابع متناظر RaiseOnGetPosition و RaiseOnGetPostionError با صفت JSInvokable ساخته میشوند. JSInvokable صفت ویژهای است که اجازه میدهد توابع از طریق JavaScript Interop فراخوانی شوند. این الگو، API جاوااسکریپت را با واگذاری نتیجه به OnGetPosition و OnGetPositionError تطبیق میدهد.
public class GeoLocation
{
…
private Action<Position> OnGetPosition;
[JSInvokable]
public void RaiseOnGetPosition(Position p) =>
OnGetPosition?.Invoke(p);
private Action<PositionError> OnGetPositionError;
[JSInvokable]
public void RaiseOnGetPositionError(PositionError err) =>
OnGetPositionError?.Invoke(err);
…
بازنمایی درونخطی صفحهٔ 86 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 87 از ۱۲۱بازگشت به فهرست
سپس متد .NET به نام GetCurrentPosition را اضافه میکنیم که تقریباً با همتای جاوااسکریپتی خود یکسان است. این متد Actionهای onSuccess و onError و همچنین PositionOptions اختیاری را که ممکن است به API جاوااسکریپت بفرستیم دریافت میکند.
درون متد، Actionهای OnGetPosition و OnGetPositionError تنظیم میشوند. در پایان، متد جاوااسکریپتی blazorGeolocation.getCurrentPosition فراخوانی و یک DotNetObjectReference همراه با PositionOptions برای آن ارسال میشود. DotNetObjectReference پوشش ویژهای است که بهجای مقدار قابل سریالسازی JSON، ارجاع یک شیء را به جاوااسکریپت میفرستد.
…
public async ValueTask GetCurrentPosition(
Action<Position> onSuccess,
Action<PositionError> onError,
PositionOptions options = null)
{
OnGetPosition = onSuccess;
OnGetPositionError = onError;
await js.InvokeVoidAsync(
"blazorGeolocation.getCurrentPosition",
DotNetObjectReference.Create(this),
options);
}
}
با کاملشدن API .NET، بخش جاوااسکریپت را با تکمیل تابع blazorGeolocation.getCurrentPosition پایان میدهیم. پیشتر فراخوانی این تابع را در متد C# یعنی GetCurrentPosition بهصورت تابعی با آرگومانهای DotNetObjectReference و PositionOptions تعریف کردیم.
در جاوااسکریپت، تابع getCurrentPosition یک Callback داخلی onSuccess دارد که از طریق DotNetObjectReference یا geolocationRef نتیجه را به .NET بازمیگرداند. تابع invokeMethodAsync روی geolocationRef، متد RaiseOnGetPosition در .NET را همراه با نتیجهٔ سریالشده فراخوانی میکند.
همین الگو برای تابع onError نیز اجرا میشود: invokeMethodAsync متد RaiseOnGetPositionError را در .NET فراخوانی و کد خطا را ارسال میکند. عبارت پایانی تابع getCurrentPosition با فراخوانی مستقیم Geolocation API فرایند را کامل میکند.
window.blazorGeolocation = {
…
getCurrentPosition: function (geolocationRef, options) {
function onSuccess(result) {
return geolocationRef.invokeMethodAsync(
'RaiseOnGetPosition',
blazorGeolocation.toSerializeable(result));
};
function onError(er) {
بازنمایی درونخطی صفحهٔ 87 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 88 از ۱۲۱بازگشت به فهرست
return geolocationRef
.invokeMethodAsync(
'RaiseOnGetPositionError',
er.code);
};
navigator.geolocation
.getCurrentPosition(onSuccess, onError, options);
}
};
اکنون که کد Interop کامل است، مؤلفه را اصلاح و قابلیت جدید را اضافه میکنیم. دو فیلد جدید برای نگهداری مقادیر Position و PositionError افزوده میشوند. متد OnSuccess برای مدیریت Callback موفق ساخته میشود؛ این متد مقدار Position را تنظیم و با StateHasChanged مؤلفه را دوباره رندر میکند. متد OnError نیز هنگام رخدادن خطا مقدار error را تنظیم میکند.
در متد OnAfterRenderAsync یک PositionOptions جدید ساخته و GetCurrentPosition با Callbackهای OnSuccess و OnError و گزینهها فراخوانی میشود.
@inject IJSRuntime js
@using GeoLocation
@page "/"
<h1>Hello, world!</h1>
@if (hasGeoLocation)
{
<p>Browser has GeoLocation</p>
}
@if (position != null)
{
<p>
Current position: @position.Coords.Latitude Lat,
@position.Coords.Longitude Long
</p>
<p>Positioin reported at: @position.Timestamp</p>
}
@switch (error)
{
case PositionError.PERMISSION_DENIED:
<p>GeoLocation Permisson Denied</p>
break;
case PositionError.POSITION_UNAVAILABLE:
<p>GeoLocation Position Unavailable</p>
break;
case PositionError.TIMEOUT:
<p>GeoLocation Timeout</p>
break;
default:
break;
}
بازنمایی درونخطی صفحهٔ 88 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 89 از ۱۲۱بازگشت به فهرست
@code {
Position position;
PositionError error;
bool hasGeoLocation;
void OnSuccess(Position p)
{
position = p;
StateHasChanged();
}
void OnError(PositionError e)
{
error = e;
StateHasChanged();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var geo = new GeoLocation(js);
hasGeoLocation = await geo.HasGeolocationFeature();
StateHasChanged();
var options = new PositionOptions() {
EnableHighAccuracy = false,
Timeout = 200,
MaximumAge = 0
};
await geo.GetCurrentPosition(
OnSuccess,
OnError,
options);
}
}
}
هنگامی که مؤلفه رندر میشود، Geolocation API را از طریق Interop فراخوانی و متدهای Callback داخلی را با OnSuccess و OnError تنظیم میکند. در جاوااسکریپت، Geolocation API ارجاعی به شیء .NET دریافت میکند و از آن برای انتقال Callbackهای خود استفاده مینماید؛ این کار با فراخوانی RaiseOnGetPosition و بازگرداندن Position انجام میشود.
این فرایند باعث رندر دوبارهٔ مؤلفه میشود و نشانهگذاری Razor موقعیت را مطابق شکل ۳۸ نمایش میدهد.
بازنمایی درونخطی صفحهٔ 89 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 90 از ۱۲۱بازگشت به فهرست
شکل ۳۸: برنامه، موقعیت کاربر را بهشکل مختصات جغرافیایی دریافتشده از JavaScript Interop نمایش میدهد.
جمعبندی
همانطور که در نمونه دیدیم، ارتباط رفتوبرگشتی کامل با جاوااسکریپت از بلیزر ممکن است. این ابزار هنگام دسترسی به عناصر بیرون از RenderTree یا استفاده از APIهای مرورگر که در بلیزر در دسترس نیستند بسیار مفید است.
JavaScript Interop میتواند مقادیر ساده و پیچیدهٔ قابل سریالسازی به JSON را منتقل کند؛ بنابراین ارتباط با جاوااسکریپت تقریباً بهاندازهٔ کار با HttpClient سرراست است.
بازنمایی درونخطی صفحهٔ 90 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 91 از ۱۲۱بازگشت به فهرست
ابزارهای فرانتاند
در فصل پیش آموختیم چگونه با جاوااسکریپت کار کنیم. در این فصل، منابع ایستای وب و ابزارهای مرتبط را بررسی میکنیم.
مانند هر برنامهٔ وب، یک برنامهٔ بلیزر نیز به وابستگیهای فرانتاند نیاز دارد. در فضای کنونی توسعهٔ فرانتاند، بیشتر وابستگیها با ابزارهای جاوااسکریپتی مانند npm و Webpack دریافت و ساخته میشوند. این ابزارها قدرتمندند، اما ممکن است استفاده از آنها دشوار باشد، منحنی یادگیری پلتفرم را افزایش دهند و در زمینهٔ جاوااسکریپت عمل کنند.
بیشتر وابستگیهای فرانتاند به این ابزارهای پیچیده نیاز ندارند و برای دستیابی به هدف نیز مجبور نیستیم مستقیماً از آنها استفاده کنیم. زیستبوم .NET و Visual Studio ابزارهایی برای مدیریت وابستگیهای فرانتاند و انجام کارهایی مانند کامپایل Sass به CSS در اختیار دارند.
مدیریت وابستگیها با LibMan
Library Manager یا LibMan ابزاری سبک برای دریافت وابستگیهای فرانتاند است. LibMan کتابخانهها و چارچوبهای محبوب را از منابع آنلاین مانند CDNJS و unpkg دریافت میکند. وابستگیهای دریافتشده با LibMan در محل دلخواه درون پروژهٔ بلیزر قرار میگیرند.
برای آشنایی با کاربرد LibMan، منبع Sass با پسوند .scss چارچوب Bootstrap را بهعنوان وابستگی دریافت میکنیم. این نمونه نشان میدهد LibMan چگونه وابستگیها را در سطحی دقیق مدیریت میکند. برای افزودن Bootstrap از Visual Studio، روی پروژهٔ بلیزر راستکلیک میکنیم.
بازنمایی درونخطی صفحهٔ 91 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 92 از ۱۲۱بازگشت به فهرست
سپس مطابق شکل ۳۹ از زیرمنو مسیر Add > Client-Side Library را انتخاب میکنیم.
شکل ۳۹: پنجرهٔ LibMan از طریق منوی زمینهای پروژه باز میشود.
پنجرهٔ Add Client-Side Library ظاهر میشود و میتوانیم نحوهٔ دریافت وابستگی توسط LibMan را پیکربندی کنیم. در پنجرهٔ شکل ۴۰، منبع بسته را unpkg انتخاب میکنیم. unpkg در این سناریو مفید است، زیرا مخزن کامل وابستگی، از جمله کد منبع، را در بر دارد.
برای دریافت Bootstrap از unpkg، نام و نسخهٔ کتابخانه را مشخص میکنیم. مقدار bootstrap@latest جدیدترین نسخهٔ وابستگی را در اختیارمان قرار میدهد. سپس فایلهای مشخصی را از کتابخانه انتخاب میکنیم؛ در اینجا پوشهٔ scss و محتوای آن هدف ماست. با انتخاب فقط پوشهٔ scss، تنها فایلهای لازم را دریافت میکنیم و از افزودهشدن فایلهای نامرتبط به پروژه جلوگیری میشود.
در پایان، محل مقصدی برای فایلهای LibMan تعیین میکنیم؛ در این نمونه پوشهٔ Themes/Bootstrap. با کلیک روی Install، LibMan با تولید فایل libman.json مقداردهی اولیه میشود و وابستگیها را دریافت میکند.
بازنمایی درونخطی صفحهٔ 92 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 93 از ۱۲۱بازگشت به فهرست
شکل ۴۰: پنجرهٔ LibMan فایل libman.json را پیکربندی و مقداردهی اولیه میکند؛ این فایل مشخص میسازد کدام وابستگیهای فرانتاند باید دریافت شوند.
هرچند در نمونه از LibMan در محیط Visual Studio استفاده کردیم، این ابزار از خط فرمان نیز قابل اجرا است.
dotnet tool install -g Microsoft.Web.LibraryManager.Cli
بازنمایی درونخطی صفحهٔ 93 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 94 از ۱۲۱بازگشت به فهرست
کامپایل با BuildWebCompiler
اکنون که وابستگی SCSS مربوط به Bootstrap نصب شده است، باید کد منبع را به یک فایل CSS کامپایل کنیم. بار دیگر بهجای ابزارهای جاوااسکریپتی از زیرساخت موجود .NET استفاده میکنیم.
برای کامپایل SCSS از WebCompiler بهره میبریم؛ ابزاری ساده برای کامپایل منابع وب مانند SCSS، TypeScript و موارد دیگر. برای افزودن WebCompiler به پروژه، بستهٔ NuGet با نام BuildWebCompiler را مطابق شکل ۴۱ اضافه میکنیم.
شکل ۴۱: بستهٔ NuGet با نام BuildWebCompiler که برای کامپایل منابع وب استفاده میشود، در پنجرهٔ NuGet Explorer نمایش داده شده است.
علاوه بر بستهٔ NuGet، افزونهای اختیاری برای Visual Studio وجود دارد که منوهای زمینهای منابع وب را اضافه میکند. این افزونه میانبرهایی برای کامپایل منابع و نصب بستهٔ BuildWebCompiler ارائه میدهد.
پس از آمادهشدن ابزارها، فایل compilerconfig.json را تعریف و outputFile و inputFile کتابخانهٔ Bootstrap را مشخص میکنیم. WebCompiler را طوری پیکربندی میکنیم که فایل منبع bootstrap.scss را پیدا و خروجی کامپایلشده را در پوشهٔ wwwroot برنامه قرار دهد تا قابل ارائه به کاربر باشد.
[
{
"outputFile": "wwwroot/css/bootstrap/bootstrap.css",
"inputFile": "Themes/Bootstrap/scss/bootstrap.scss"
}
]
وقتی فایل پیکربندی ذخیره شود، فرایند کامپایل آغاز و فایل خروجی تولید میشود. این فرایند هنگام Build پروژه از Visual Studio یا CLI نیز فعال خواهد شد.
ابزارهایی مانند LibMan و WebCompiler مستقیم سراغ اصل کار میروند و با پیکربندی و سربار بسیار کم عمل میکنند. هرچند بلیزر فناوری تازهای در این حوزه است، توسعهٔ وب برای زیستبوم .NET موضوع جدیدی نیست.
بازنمایی درونخطی صفحهٔ 94 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 95 از ۱۲۱بازگشت به فهرست
ابزارهای موجودی مانند این نمونهها با بلیزر کاربردهای تازهای پیدا میکنند و زیستبوم جدیدی در حال شکلگیری است.
بازنمایی درونخطی صفحهٔ 95 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 96 از ۱۲۱بازگشت به فهرست
کتابخانههای کلاسی Razor
در فصل پیش، منابع ایستای وبِ خارج از .NET را بررسی کردیم. در این فصل بر ساخت و مصرف وابستگیهای .NET از طریق کتابخانههای کلاسی Razor تمرکز میکنیم.
کتابخانههای کلاسی Razor (Razor Class Libraries یا RCL) بستههایی هستند که میتوانند هر ترکیبی از مؤلفهها، صفحهها، کد Interop و منابع ایستای پشتیبان مانند JavaScript، CSS، تصویر و فونت را در بر گیرند. هدف این است که مؤلفههای کاملاً عملیاتی بتوانند بهصورت آماده، از طریق فایلهای DLL یا بستههای NuGet، تحویل داده شوند.
وجود سازوکاری مطمئن برای اشتراک منابع، محیطی پربازده برای ساخت برنامههای بلیزر ایجاد میکند.
برای درک محکم RCLها، یک RCL جدید میسازیم و سپس آن را در یک برنامهٔ بلیزر مصرف میکنیم. این فرایند هم دیدگاه سازندهٔ RCL و هم نحوهٔ استفاده از آن را نشان خواهد داد.
ساخت یک Razor Class Library جدید
برای ساخت RCL جدید، از قالب .NET Razor Class Library استفاده میکنیم. کار را با برنامهای تازه آغاز و یک RCL به پروژه اضافه میکنیم. پنجرهٔ پروژهٔ جدید در صفحهٔ بعد باز میشود.
بازنمایی درونخطی صفحهٔ 96 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 97 از ۱۲۱بازگشت به فهرست
با راستکلیک روی پروژه و انتخاب Add > New Project مطابق شکل ۴۲، پنجرهٔ پروژهٔ جدید باز میشود.
شکل ۴۲: افزودن پروژهای جدید به راهحل موجود از طریق منوی زمینهای Add.
در پنجرهٔ Add a new project، قالب Razor Class Library را مطابق شکل ۴۳ از فهرست قالبها انتخاب میکنیم. پس از انتخاب قالب، به صفحهٔ پیکربندی بعدی در شکل ۴۴ میرویم.
این مرحله مهم است، زیرا نوع پروژهٔ Razor Component را از Razor Pages متمایز میکند. اگر کادر Support pages and views انتخاب شود، قالب یک پروژهٔ Razor Pages/Views ایجاد میکند. چون هدف ما پشتیبانی از چارچوب بلیزر است، کادر را مطابق شکل ۴۴ بدون علامت باقی میگذاریم.
بازنمایی درونخطی صفحهٔ 97 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 98 از ۱۲۱بازگشت به فهرست
شکل ۴۳: قالب Razor Class Library در پنجرهٔ Add a new project انتخاب شده است.
بازنمایی درونخطی صفحهٔ 98 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 99 از ۱۲۱بازگشت به فهرست
شکل ۴۴: Razor Class Library با گزینهٔ Support pages and views غیرفعال یا بدون علامت.
پروژهٔ جدید همراه با مجموعهای از نمونههای پیشفرض به راهحل اضافه میشود. در پروژه، پوشهٔ wwwroot وجود دارد که با قرارداد پروژههای بلیزر مطابقت دارد. پوشهٔ wwwroot در RCL محل نگهداری منابع ایستای وب است تا مصرفکنندهٔ نهایی بتواند از آنها استفاده کند.
فایلهای wwwroot هنگام افزودهشدن RCL به یک برنامه، در مسیری ویژه قرار میگیرند. این موضوع را در بخش بعد و هنگام مصرف RCL بررسی خواهیم کرد.
درون پوشهٔ wwwroot تصویری به نام background.png وجود دارد. در کنار آن، فایل CSS با نام styles.css قرار گرفته است. این دو فایل با هم منابع بصری مؤلفهٔ نمونهٔ Component1 را تشکیل میدهند که خود آن نیز در قالب وجود دارد.
همچنین فایل جاوااسکریپتی exampleJsInterop.js در wwwroot قرار دارد. exampleJsInterop همراه با کلاس متناظر ExampleJsInterop در ریشهٔ پروژه استفاده میشود. این فایلها در درخت پروژهٔ شکل ۴۴ دیده میشوند.
بازنمایی درونخطی صفحهٔ 99 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 100 از ۱۲۱بازگشت به فهرست
شکل ۴۴: فایلهای موجود در قالب Razor Class Library. پوشهٔ wwwroot منابع ایستایی را در بر دارد که از Component1 و ExampleJsInterop پشتیبانی میکنند.
پروژهٔ پیشفرض یک مؤلفهٔ نمونه به نام Component1 دارد. این مؤلفه در ریشهٔ پروژه و در فایل Component1.cs قرار گرفته است. خود مؤلفه یک عنصر سادهٔ div را با ظاهری شبیه بنر نمایش میدهد.
<div class="my-component">
This Blazor component is defined in the
<strong>RazorClassLibrary1</strong> package.
</div>
هرچند مؤلفهٔ این نمونه در ریشه قرار دارد، میتوان هر تعداد پوشه و مؤلفه برای سفارشیسازی پروژه اضافه کرد. مؤلفههای افزودهشده همان قواعد برنامهٔ معمول بلیزر را دنبال میکنند؛ مسیر و نام فایل با فضای نام و کلاس متناظر خواهند بود. بنابراین میتوانیم مؤلفهها را با تغییر کد کم یا حتی بدون تغییر، از برنامهٔ بلیزر به RCL منتقل کنیم.
اکنون که پروژهٔ RCL به راهحل اضافه شده است، میتوانیم مؤلفهها، کد و منابع کتابخانه را مصرف کنیم.
مصرف Razor Class Library
Razor Class Library را میتوان بهصورت فایل DLL، بستهٔ NuGet یا از طریق پروژه افزود. بسته به نوع منابع موجود در RCL، چند مرحله برای تکمیل فرایند لازم است. در این نمونه با پروژهٔ RCLای ادامه میدهیم که با قالب پروژه ساخته شد. میآموزیم چگونه مؤلفه، منابع ایستا و JavaScript Interop را از RCL اضافه کنیم.
برای افزودن RCL از طریق پروژه به یک برنامهٔ بلیزر، به Project Reference نیاز داریم. در پروژهٔ برنامهٔ بلیزر روی گره Dependencies راستکلیک و مطابق شکل ۴۵ گزینهٔ Add References را انتخاب میکنیم.
بازنمایی درونخطی صفحهٔ 100 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 101 از ۱۲۱بازگشت به فهرست
شکل ۴۵: پنجرهٔ Add Reference با راستکلیک روی گره Dependencies در پروژهٔ برنامهٔ بلیزر در دسترس است.
همانطور که در شکل ۴۶ دیده میشود، در پنجرهٔ Reference Manager پروژهٔ RCL با نام RazorClassLibrary1 انتخاب و به برنامهٔ بلیزر اضافه میشود.
شکل ۴۶: Razor Class Library انتخابشده در پنجرهٔ Reference Manager.
بازنمایی درونخطی صفحهٔ 101 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 102 از ۱۲۱بازگشت به فهرست
ارجاع پروژه تمام وابستگیهای .NET موردنیاز پروژه را از RCL فراهم میکند؛ درست مانند آنکه یک بستهٔ NuGet به پروژه اضافه کرده باشیم. پس از رسیدگی به وابستگیهای .NET، هنوز باید منابع ایستای موجود در RCL را دستی ثبت کنیم.
در بلیزر و RCLها، منابع ایستا در پوشهٔ wwwroot پروژه ذخیره میشوند؛ اما منابع RCL در مسیر متفاوتی Resolve میشوند. هنگام ارجاع به منابع ایستای CSS و JavaScript از یک RCL، مسیر wwwroot به الگوی زیر تبدیل میشود:
_content/<namespace>/
با این الگو میتوانیم بهآسانی منابع را در بخش head فایل _Host.cshtml یا index.html برنامه ارجاع دهیم. فایلهای CSS و JavaScript متعلق به RCL را در برنامهٔ بلیزر اضافه میکنیم.
<link href="_content/RazorClassLibrary1/styles.css"
rel="stylesheet" />
<script src="_content/RazorClassLibrary1/exampleJsInterop.js">
</script>
</head>
پس از آمادهشدن همهٔ ارجاعها، میتوانیم مؤلفهٔ RCL را در برنامهٔ بلیزر به کار ببریم. فایل index.razor را باز و یک عبارت using اضافه میکنیم تا مؤلفهٔ RCL در دامنه قرار گیرد. سپس Component1 را مستقیماً به صفحه میافزاییم.
@page "/"
@using RazorClassLibrary1
<h1>Hello, @message!</h1>
Welcome to your new app.
<Component1></Component1>
اکنون برنامهٔ بلیزر را اجرا میکنیم تا نتیجه را ببینیم. هنگام اجرا، Component1 نمایش داده میشود و استایل CSS تعریفشده در مسیر _content مطابق شکل ۴۷ اعمال میگردد.
شکل ۴۷: Component1 از Razor Class Library ارجاعدادهشده، در برنامهٔ در حال اجرا رندر شده است.
بازنمایی درونخطی صفحهٔ 102 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 103 از ۱۲۱بازگشت به فهرست
همانطور که پیشتر گفتیم، RCLها میتوانند بیش از مؤلفهها را در خود جای دهند. در سناریوی ما، JavaScript Interop نیز در کتابخانهٔ ارجاعدادهشده وجود دارد. فایل جاوااسکریپت در مرحلهٔ قبلی ارجاع داده شد و اکنون فقط باید API مربوط به C# را اجرا کنیم.
نمونهٔ RCL با استفاده از تابع داخلی prompt در DOM یک کادر ورودی نمایش میدهد. دوباره صفحهٔ index را تغییر میدهیم و این بار JavaScript Interop موجود را فراخوانی میکنیم تا به prompt دسترسی داشته باشیم.
در بالای مؤلفه، دستور inject برای دریافت ارجاعی به نمونهٔ IJSRuntime استفاده میشود. در بلوک کد، فیلدی به نام message برای نگهداری خروجی prompt تعریف میکنیم. مقدار پیشفرض message برابر world است؛ بنابراین هنگام مقداردهی اولیهٔ صفحه، متن Hello, world نمایش داده میشود.
سپس متد ShowPrompt ساخته میشود که با رویداد کلیک دکمه فراخوانی خواهد شد. درون این متد، ExampleJsInterop.Prompt را با نمونهٔ IJSRuntime و پیام Say hello: فراخوانی میکنیم.
@inject IJSRuntime js
@page "/"
@using RazorClassLibrary1
…
@code {
string message = "world";
async Task ShowPrompt()
{
message = await ExampleJsInterop.Prompt(
js,
"Say hello:");
}
}
فرایند را با بهروزرسانی نشانهگذاری مؤلفه کامل میکنیم. پیام اصلی Hello, World با Hello, @message جایگزین میشود تا فیلد message بخشی از عنوان باشد. در پایان، دکمهای اضافه میکنیم که رویداد onclick آن به متد ShowPrompt متصل است.
<h1>Hello, @message!</h1>
Welcome to your new app.
<Component1></Component1>
<button @onclick="ShowPrompt">Show Prompt</button>
هنگام اجرای برنامه، عنوان اولیه Hello, world خواهد بود. زیر عنوان، Component1 از RCL ارجاعشده نمایش داده میشود. وقتی کاربر روی دکمهٔ [Show Prompt] کلیک کند، prompt داخلی مرورگر با استفاده از جاوااسکریپت موجود در RCL نمایش داده میشود. در شکل ۴۸، برنامه مقدار اولیه را همراه با prompt مرورگر نشان میدهد.
بازنمایی درونخطی صفحهٔ 103 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 104 از ۱۲۱بازگشت به فهرست
پیام prompt برابر Say hello: است. با واردکردن پیام و کلیک روی OK، مقدار به برنامه بازمیگردد و صفحه با نتیجه بهروزرسانی میشود. شکل ۴۹ نتیجهٔ Hello, Blazor را پس از واردکردن Blazor نشان میدهد.
شکل ۴۸: Interop مربوط به Prompt در RazorClassLibrary1 اجرا و prompt داخلی مرورگر نمایش داده میشود.
شکل ۴۹: وضعیت نهایی صفحه پس از ورود مقدار Blazor، پیام Hello, Blazor را در عنوان نشان میدهد.
همانطور که در نمونهٔ قالب دیدیم، RCLها اشتراک بخشهای قابلاستفادهٔ مجددِ رابط کاربری و کد را آسان میکنند. با RCL میتوانیم کتابخانههای مشترکی بسازیم که در چند پروژه استفاده شوند. افزون بر این، RCLها را میتوان بهصورت بستههای NuGet بستهبندی و از طریق NuGet.org یا سرورهای خصوصی NuGet توزیع کرد.
بازنمایی درونخطی صفحهٔ 104 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 105 از ۱۲۱بازگشت به فهرست
اکنون که میدانیم Razor Class Library چیست، کاربرد آن را در مقیاس بزرگتر بررسی میکنیم. در ادامه به Telerik UI for Blazor میپردازیم؛ کتابخانهای با تعداد زیادی مؤلفهٔ غنی رابط کاربری و ابزارهای لازم برای ساخت برنامههای سازمانی بزرگمقیاس.
بازنمایی درونخطی صفحهٔ 105 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 106 از ۱۲۱بازگشت به فهرست
معرفی Telerik UI for Blazor
Progress Software و برند Telerik سابقهای طولانی در پشتیبانی از جامعهٔ .NET با محصولاتی مانند Telerik UI for ASP.NET AJAX، UI for ASP.NET MVC و UI for ASP.NET Core دارند. این پشتیبانی با انتشار Telerik UI for Blazor ادامه پیدا میکند.
Telerik UI for Blazor محصولی کاملاً مستقل و اصیل است و محصولات موجود jQuery/JavaScript را در C# بستهبندی نمیکند تا وانمود کند محصول جدیدی ساخته شده است. مدل برنامهنویسی مبتنی بر Wrapper، انتزاعی نشتکننده است که جزئیاتش به لایهٔ API .NET بازمیگردد و احتمالاً با RenderTree تداخل میکند.
Telerik UI for Blazor از صفر ساخته شده است؛ مؤلفهها تا حد امکان با .NET نوشته شدهاند و فقط در مواقع ضروری به JavaScript Interop وابستهاند. این رویکرد بومی .NET یک سرمایهگذاری بلندمدت است که یکپارچگی روان با چارچوب بلیزر را ممکن میکند.
بازنمایی درونخطی صفحهٔ 106 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 107 از ۱۲۱بازگشت به فهرست
شکل ۵۰: برنامهٔ Telerik UI for Blazor با مؤلفههای Data Grid و Chart. نسخهٔ نمایشی در نشانی قابل مشاهدهٔ demos.telerik.com/blazor-financial-portfolio ارائه شده است.
Telerik UI for Blazor محصولی کاملاً مستقل است و محصولات موجود jQuery/JavaScript را در C# بستهبندی نمیکند تا آن را محصولی جدید جلوه دهد.
Telerik UI for Blazor یک مجموعهٔ کامل محصول است که یک Razor Class Library با بیش از ۳۰ مؤلفهٔ رابط کاربری را در بر دارد؛ از جمله موارد دیدهشده در شکل ۵۰: Data Grid، Scheduler، Chart، Window، DropDown و موارد بسیار دیگر.
افزون بر این، مؤلفهها از قابلیتهای حرفهای مانند اتصال داده، دسترسپذیری و جهانیسازی/بومیسازی پشتیبانی میکنند.
Telerik UI for Blazor فقط مجموعهای از مؤلفههای رابط کاربری نیست. محصول شامل پشتیبانی حرفهای، پوستهها و سازندهٔ پوسته، قالبها و ابزارهای پردازش سند برای کار با فایلهای Office نیز میشود.
نصب
بازنمایی درونخطی صفحهٔ 107 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 108 از ۱۲۱بازگشت به فهرست
نصب Telerik UI for Blazor فقط چند مرحلهٔ ساده دارد. ابتدا به نسخهٔ آزمایشی رایگان ۳۰روزه از Telerik.com نیاز داریم. پس از ساخت حساب، مرورگر نصبکنندهٔ محصول را بهطور خودکار دریافت میکند. نصبکننده منبع بستهٔ NuGet مربوط به Telerik UI for Blazor، فایلهای دودویی، یکپارچگی Visual Studio و نمونهها را به سامانه اضافه میکند.
پس از پایان نصب میتوانیم یک پروژهٔ موجود را دستی ارتقا دهیم یا پروژهای جدید را با یکی از قالبهای متعدد محصول آغاز کنیم. چون در حال بررسی نصب هستیم، پیش از پرداختن به قالبهای پروژهٔ جدید، مسیر ارتقای پروژهٔ موجود را ادامه میدهیم.
از آموختههای فصل قبل دربارهٔ Razor Class Library استفاده میکنیم تا Telerik UI for Blazor را به برنامهٔ موجود اضافه کنیم. مانند هر RCL دیگری، باید به کتابخانه ارجاع دهیم. در این نمونه از Feed خصوصی NuGet برای دریافت بستهٔ Telerik UI for Blazor استفاده میکنیم.
NuGet Package Manager را در Visual Studio مطابق شکل ۵۲ باز میکنیم، منبع بسته را انتخاب و عبارت Telerik را جستوجو میکنیم تا بسته نمایش داده شود. Feed خصوصی باید از قبل توسط نصبکنندهٔ محصول افزوده شده باشد. در صورت نیاز، میتوان با پیروی از راهنمای مستندات محصول، منبع بسته را دستی افزود.
شکل ۵۲: Razor Class Library مربوط به Telerik UI for Blazor که از NuGet Package Manager نصب میشود.
پس از افزودن ارجاع NuGet، باید منابع ایستای کتابخانه را ارجاع دهیم. در فایل _Host.cshtml یا index.html پروژه، فایلهای CSS و JavaScript Interop کتابخانه را اضافه میکنیم.
<head>
<link rel="stylesheet" href=
"_content/telerik.ui.for.blazor.trial/css/kendo-theme-default/all.css" />
<script src="_content/telerik.ui.for.blazor.trial/js/telerik-blazor.js"
defer></script>
<!-- For commercial licenses use
بازنمایی درونخطی صفحهٔ 108 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 109 از ۱۲۱بازگشت به فهرست
ref="_content/telerik.ui.for.blazor/-resource-"
-->
</head>
در بخش head میتوانیم با تغییر مسیر فایل CSS، پوستهٔ برنامه را نیز تغییر دهیم. در بخشی از مسیر که نام /css/kendo-theme-default قرار دارد، میتوانیم مطابق شکل ۵۳ یکی از گزینههای default، bootstrap یا material را انتخاب کنیم. با همین تغییر، همهٔ مؤلفههای برنامه میتوانند از پوستهٔ Google Material Design استفاده کنند.
<link rel="stylesheet" href=
"_content/Telerik.UI.for.Blazor/css/kendo-theme-material/all.css" />
Telerik UI for Blazor همچنین به چند سرویس از طریق تزریق وابستگی نیاز دارد. این سرویسها بهآسانی با فراخوانی متد AddTelerikBlazor از نقطهٔ ورود پروژه ثبت میشوند: فایل Startup.cs برای Blazor Server یا Program.cs برای WebAssembly.
// Blazor Server only, Startup.cs
services.AddTelerikBlazor();
// Blazor WebAssembly only, Program.cs
builder.Services.AddTelerikBlazor();
چون کتابخانه مؤلفههای زیادی دارد و احتمالاً در سراسر پروژه از آنها استفاده میکنیم، آنها را به دامنهٔ سراسری اضافه میکنیم. افزودن Telerik.Blazor و Telerik.Blazor.Components به _Imports.razor تضمین میکند در همهٔ بخشهای برنامه به کتابخانه دسترسی داشته باشیم.
@using Telerik.Blazor
@using Telerik.Blazor.Components
در پایان، TelerikRootComponent را به MainLayout برنامه اضافه میکنیم. Telerik UI for Blazor قابلیتهای پیشرفتهای مانند پنجرههای Modal، ToolTip و Animation دارد که باید در عنصر ریشهٔ برنامه ثبت شوند؛ TelerikRootComponent این کار را انجام میدهد.
@inherits LayoutComponentBase
<TelerikRootComponent>
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
@Body
بازنمایی درونخطی صفحهٔ 109 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 110 از ۱۲۱بازگشت به فهرست
</div>
</TelerikRootComponent>
نصب کامل است و اکنون آمادهایم از Telerik UI for Blazor در برنامه استفاده کنیم. برای آشنایی با Telerik Grid، مؤلفهٔ موجود FetchData را تغییر میدهیم و HTML ایستای آن را با یک مؤلفهٔ کامل جایگزین میکنیم.
Data Grid بلیزر
Telerik UI for Blazor Data Grid فهرست بلندی از قابلیتهای حرفهای دارد. این Data Grid از اتصال داده، انتخاب تکی/چندگانه، فیلتر متناسب با زمینه، مرتبسازی، صفحهبندی، پوستهها، قالب ردیف/ستون و چند حالت ویرایش پشتیبانی میکند.
برای مشاهدهٔ این قابلیتها، جدول دستنویس نمونهٔ FetchData در شکل ۵۴ را با Telerik Data Grid جایگزین میکنیم.
شکل ۵۴: جدول دادهٔ HTML بدون تغییر که در نمونهٔ FetchData وجود دارد.
کد FetchData را در پوشهٔ /Pages پیدا کنید. کل عنصر table را با مؤلفهٔ TelerikGrid جایگزین میکنیم. بررسی null نیز قابل حذف است، زیرا Telerik Grid این عملیات را بهصورت داخلی انجام میدهد.
<!-- all code below can be removed -->
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
بازنمایی درونخطی صفحهٔ 110 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 111 از ۱۲۱بازگشت به فهرست
else
{
}
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
مؤلفهٔ TelerikGrid ویژگی Data را به forecasts متصل میکند که آرایهای از اشیای WeatherForecast است. ویژگیهای Pageable، FilterMode، Groupable، Reorderable و Sortable نیز برای Grid فعال شدهاند.
درون TelerikGrid برای هر فیلدی که میخواهیم در Grid نمایش داده شود، یک مؤلفهٔ فرزند تعریف میکنیم. چون همهٔ این موارد کد C# هستند، میتوانیم ویژگی Field را با عملگر nameof در C# تنظیم کنیم و از ایمنی نوع بهره ببریم.
عنوان ستونها با ویژگی Title قابل سفارشیسازی است. اگر Title تنظیم نشود، عنوان بهطور خودکار نام ویژگی را میگیرد. علاوه بر این، میتوان از قالبها برای نمایش فرمتهای سفارشی، تصویرها و حتی مؤلفههای دیگر رابط کاربری استفاده کرد. در اینجا یک قالب برای فرمتکردن فیلد Date به کار رفته است.
<TelerikGrid Data=forecasts
Sortable=true
Pageable=true
Groupable=true
FilterMode=GridFilterMode.FilterRow
Reorderable=true>
<GridColumns>
<GridColumn Field="@nameof(WeatherForecast.Date)">
<Template>
@((context as WeatherForecast)
.Date.ToShortDateString())
</Template>
</GridColumn>
<GridColumn Field="@nameof(WeatherForecast.TemperatureF)"
Title="Temp (F)" />
<GridColumn Field="@nameof(WeatherForecast.TemperatureC)"
Title="Temp (C)" />
<GridColumn Field="@nameof(WeatherForecast.Summary)" />
</GridColumns>
</TelerikGrid>
بازنمایی درونخطی صفحهٔ 111 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 112 از ۱۲۱بازگشت به فهرست
پس از جایگزینی HTML با مؤلفهٔ Telerik Grid، برنامه را دوباره اجرا میکنیم و تغییرات را مطابق شکل ۵۵ میبینیم. اکنون یک Grid کامل و سازگار با الزامات دسترسپذیری داریم.
شکل ۵۵: Telerik UI for Blazor Data Grid؛ یک مؤلفهٔ Data Grid کامل.
افزودن مؤلفهها به پروژه نسبتاً آسان است. تنها با چند مرحله توانستیم یک مؤلفهٔ استاندارد HTML را به چیزی بسیار تعاملیتر و غنی از قابلیت تبدیل کنیم.
Telerik UI for Blazor همچنین قالبهای پروژهای دارد که ساخت پروژههای جدید را سادهتر میکنند. با یکپارچگی Visual Studio میتوانیم فرایند را باز هم کوتاهتر کنیم.
یکپارچگی Visual Studio
Telerik UI for Blazor در زمان نگارش چهار قالب برای آغاز سریع پروژههای جدید دارد. هنگام اجرای نصبکننده، قالبها به Visual Studio افزوده میشوند. میتوان آنها را با مسیر File > New Telerik Project در پنجرهٔ پروژهٔ جدید پیدا کرد.
دو قالب خالی وجود دارد: یکی برای Server و دیگری برای WebAssembly یا Client App. این قالبها برای کاربران پیشرفتهای مناسباند که پروژهای میخواهند که همهٔ منابع Telerik از قبل در آن ارجاع داده شده باشند، اما نمونههای اضافی و مزاحمی برای حذفکردن نداشته باشد.
برای کاربرانی که راهنمایی بیشتری میخواهند، قالبهای Server و WebAssembly نیز وجود دارند که نمونههای عملی از تجربههای کاربری محبوب، شامل Grid، Menu، Chart و Form، ارائه میکنند.
بازنمایی درونخطی صفحهٔ 112 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 113 از ۱۲۱بازگشت به فهرست
این قالبها در پنجرهٔ Create New Project شکل ۵۶ دیده میشوند.
شکل ۵۶: پنجرهٔ پروژهٔ جدید Telerik UI for Blazor چهار قالب برای شروع کار دارد.
قالب CRUD, Form, Chart - Server App را انتخاب و بهسرعت مرور میکنیم. در این قالب، تمام منابع Telerik UI for Blazor از قبل ارجاع داده شدهاند؛ بنابراین هیچ مرحلهٔ راهاندازی اضافی وجود ندارد. میتوانیم بلافاصله برنامه را اجرا و صفحههای نمایشی مؤلفهها و سناریوهای محبوب رابط کاربری را مشاهده کنیم.
صفحهٔ Home مروری بر محتوای برنامه و پیوندهایی به منابع مختلف دارد. در بخش ۱ شکل ۵۷، یک مؤلفهٔ Telerik Menu را میبینیم که از مؤلفهٔ داخلی NavLink یکپارچه با بلیزر استفاده میکند. با منو میتوانیم به دیگر صفحههای نمونهٔ پروژه برویم یا منابع Telerik UI for Blazor را بررسی کنیم.
بخش ۲ شکل ۵۷ یک مؤلفهٔ Telerik Window است که اطلاعات مفیدی دربارهٔ نمونهها ارائه میکند و همزمان شیوهٔ استفاده از Window را نشان میدهد.
بازنمایی درونخطی صفحهٔ 113 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 114 از ۱۲۱بازگشت به فهرست
بخش ۳ شکل ۵۷ نمونهها را همراه با فایل .razor متناظرشان فهرست میکند و این موارد تعاملی نیز هستند. اگر به دکمهٔ بخش ۴ شکل ۵۷ برویم، میتوانیم یک مؤلفهٔ Animation را فعال کنیم که شیوهٔ ساخت اعلان Toast در برنامه را نشان میدهد.
شکل ۵۷: ۱) مؤلفهٔ Telerik Menu؛ ۲) نمونهٔ Telerik Window؛ ۳) فهرست تعاملی نمونهها و محل فایلهایشان؛ ۴) نمونهٔ Telerik Animation.
با رفتن به نمونهٔ Grid، یک مؤلفهٔ Telerik Grid کاملاً پیکربندیشده میبینیم. این نمونه عملیات ایجاد، بهروزرسانی و حذف یا CUD را در بر دارد و نقطهٔ شروعی برای هر برنامهٔ ورود داده فراهم میکند؛ همانطور که در شکل ۵۸ دیده میشود.
شکل ۵۸: Telerik Grid با تجربهٔ کامل ویرایش که آمادهٔ سازگارشدن با سناریوی برنامهٔ ماست.
گزینهٔ بعدی منو نمونهٔ Chart در شکل ۵۹ است. نمودار Telerik با راهنما، برچسبها، عنوان و چند سری دادهٔ متصل بهطور کامل پیکربندی شده است. این نمونه، نقطهٔ شروع مناسبی برای برنامهٔ بعدی ما با سبک داشبورد خواهد بود.
بازنمایی درونخطی صفحهٔ 114 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 115 از ۱۲۱بازگشت به فهرست
شکل ۵۹: مؤلفهٔ Telerik Chart با دو مؤلفهٔ Line Series.
آخرین نمونه در شکل ۶۰، Form رایج است. این فرم بسیاری از مؤلفههای ورودی موجود در Telerik UI for Blazor را نشان میدهد. همهٔ مؤلفههای ورودی بهشکل یکپارچه با اعتبارسنجی داخلی فرمهای بلیزر کار میکنند.
شکل ۶۰: نمونهٔ فرم با چند مؤلفهٔ ورودی Telerik و اعتبارسنجی فرم.
آزمایش Telerik UI for Blazor
Telerik UI for Blazor طراحی شده است تا توسعهدهندگان را سریع به مرحلهٔ اجرا برساند. با مؤلفههای بومی و کامل رابط کاربری بلیزر، قالبهای پروژه و پوستهها، میتوانیم در زمان کمتر بهرهوری بسیار بیشتری داشته باشیم. Telerik UI for Blazor بهمدت ۳۰ روز بهصورت رایگان در دسترس است.
بازنمایی درونخطی صفحهٔ 115 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 116 از ۱۲۱بازگشت به فهرست
نسخهٔ آزمایشی، دسترسی کامل به کل محصول و همهٔ قابلیتهای آن را فراهم میکند. برای شروع، به وبسایت Telerik در نشانی قابل مشاهدهٔ www.telerik.com/blazor-ui بروید و روی Try Now کلیک کنید. وبسایت Telerik همچنین دارای نمونههای تعاملی، مستندات و ویدئوهای شروع کار است.
بازنمایی درونخطی صفحهٔ 116 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 117 از ۱۲۱بازگشت به فهرست
نتیجهگیری
در سراسر کتاب، بلیزر، چارچوب جدید برنامهٔ تکصفحهای مایکروسافت، را بررسی کردیم. وباسمبلی امکان توسعهٔ برنامههای غیرجاوااسکریپتی را فراهم کرده است. وباسمبلی با استفاده از زماناجرای Mono، .NET را به مرورگر میآورد و نسل جدیدی از برنامههای .NET را ممکن میکند که در مرورگر اجرا میشوند.
علاوه بر زماناجرای کامل سمت کاربر با وباسمبلی، بلیزر مدل سمت سروری مبتنی بر SignalR نیز ارائه میکند. با این دو مدل، میتوانیم شیوهٔ تحویل برنامه را انتخاب و تجربه را بر اساس نیازها تنظیم کنیم.
دیدیم مدل مؤلفهٔ Razor تا چه اندازه ساده است و چگونه میتوان رابطهای کاربری گوناگون ساخت. مدل مؤلفه منحنی یادگیری پایینی دارد و با قالبها انعطافپذیرتر میشود. نحو Razor برای توسعهدهندگان .NET آشناست و پایهای محکم برای توسعهٔ مؤلفه ایجاد میکند.
با نحو اتصال داده در مؤلفههای Razor، حتی گردشکارهای دشوار نیز بهسرعت قابل حلاند. نمونههایی دیدیم که از کنترل کامل بر اتصال مقدار و نحو رویداد سفارشی استفاده میکردند. با این تکنیکها میتوانیم با کد بسیار کم یا بدون کد اضافه مشخص کنیم چه مقادیری و در چه زمانی متصل شوند.
برنامههای وب مدرن عناصر تعاملی فراوانی دارند و این موضوع ایدههای تازهای برای بهروز نگهداشتن DOM با اطلاعات جدید ایجاد کرده است. رندر DOM عملیاتی پرهزینه است و بلیزر با RenderTree برای کارایی تلاش میکند. الگوریتم هوشمند Diff در RenderTree تضمین میکند برنامههای بلیزر سریع رندر شوند و فقط کمترین تغییرات ضروری را اعمال کنند.
آموختیم چگونه با استفادهٔ درست از دستور @key از این قابلیت بهطور کامل بهره ببریم و هنگامی که مؤلفهها سلسلهمراتبی از عناصر مرتبط تولید میکنند، راهنمایی لازم را به چارچوب بدهیم.
بلیزر از طریق لایهٔ JavaScript Interop میان توسعهٔ C# و جاوااسکریپت پل میزند. این ابزار برای مهاجرت کدهای موجود جاوااسکریپت و استفاده از قابلیتهایی که خارج از دسترس بلیزرند ایدهآل است. در نمونهها دیدیم چگونه جاوااسکریپت را از .NET فراخوانی کنیم.
بازنمایی درونخطی صفحهٔ 117 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 118 از ۱۲۱بازگشت به فهرست
همچنین مقادیر را به محیط جاوااسکریپت فرستادیم و نمونهای از ارتباط دوطرفه با Callbackها و Geolocation API را بررسی کردیم.
همزمان با فاصلهگرفتن توسعهدهندگان از جاوااسکریپت، .NET میتواند برخی وابستگیهای JavaScript و CSS فرانتاند را به ابزارهای .NET منتقل کند. با LibMan دیدیم چگونه از سرویسهای جایگزین بسته برای واردکردن کد منبع CSS یا SCSS چارچوب Bootstrap استفاده کنیم. با BuildWebCompiler توانستیم منابع را بدون نیاز به npm یا Webpack کامپایل کنیم.
ساخت و مصرف وابستگیهای .NET از طریق Razor Class Library ممکن است. با قالب RCL، ساخت پروژههای جدید ساده است و ساختاری مشابه برنامههای بلیزر دارند. مصرف کتابخانهها نیز بهآسانی ارجاع به پروژهٔ RCL یا نصب یک بستهٔ NuGet است.
RCLها میتوانند مؤلفههای بسیار و منابع ایستا را در بر داشته باشند؛ همانطور که در کتابخانهٔ بومی Telerik UI for Blazor دیدیم.
در هر فصل و نمونه، ویژگی متفاوتی از بلیزر را بررسی کردیم. هر ایده به شکلی ارائه شد که بتوانید فوراً شروع کنید و به بهرهوری برسید. این کتاب فقط آغاز راه است. بلیزر موضوعی عمیق با قابلیتهای متنوعی است که فرصت پرداختن به همهٔ آنها را نداشتیم.
برای ادامهٔ مسیر یادگیری بلیزر، وبلاگ Telerik را در نشانی قابل مشاهدهٔ www.Telerik.com/Blogs دنبال کنید و نویسنده، Ed Charbeneau، را با شناسهٔ @EdCharbeneau در Twitter بیابید.
بازنمایی درونخطی صفحهٔ 118 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 119 از ۱۲۱بازگشت به فهرست
واژهنامه
WebAssembly (wasm) - قالب دستورالعمل دودویی برای یک ماشین مجازی پشتهمحور است. Wasm بهعنوان مقصدی قابل حمل برای کامپایل طراحی شده است.
Razor Syntax - نحو نشانهگذاری قالب مبتنی بر زبان برنامهنویسی C# است.
Razor Page - صفحهای با پسوند .cshtml در چارچوب ASP.NET Razor Pages که نشانهگذاری و منطق را در بر دارد. با بلیزر سازگار است، اما RenderTree تولید نمیکند.
Razor View - نمایی با پسوند .cshtml در چارچوب ASP.NET MVC. از کد Razor سند HTML تولید میکند، اما RenderTree نمیسازد.
Razor Component (.razor) - مدل مؤلفه در بلیزر که نحو Razor و کد C# را کپسوله و RenderTree تولید میکند.
Blazor Component - مراجعه شود به: Razor Component.
Blazor WebAssembly - برنامهٔ بلیزر که با استفاده از وباسمبلی در مرورگر اجرا میشود. این برنامه به سرور نیاز ندارد.
Blazor Server - برنامهٔ بلیزر که روی سرور اجرا میشود و با اتصال WebSocket در SignalR با مرورگر کاربر ارتباط دارد.
Blazor Client-Side - مراجعه شود به: Blazor WebAssembly.
Blazor Server-Side - مراجعه شود به: Blazor Server.
Dependency Injection (DI) - الگوی طراحی نرمافزار برای ایجاد کد با اتصال سست است. این مفهوم شامل ظرفی است که اشیا را ثبت و نمونهسازی میکند و آنها را بهصورت مقدارهای سراسری در برنامه در اختیار میگذارد.
MSBuild - Microsoft Build Engine که بیشتر با نام MSBuild شناخته میشود؛ مجموعهابزار Build آزاد و متنباز برای کد مدیریتشده و کد بومی C++ که بخشی از .NET Framework بوده است.
Interop - مراجعه شود به: Interoperability.
Interoperability - توانایی سامانههای رایانهای یا نرمافزارها برای تبادل اطلاعات و استفاده از آن.
JavaScript Interop - لایهٔ تعامل که به برنامههای بلیزر اجازه میدهد توابع جاوااسکریپت را فراخوانی کنند یا برعکس.
Mono Runtime - زماناجرای چندسکویی .NET که برای قابلیت حمل طراحی شده است. این زماناجرا برنامههای بلیزر، Xamarin و Unity را پشتیبانی میکند.
بازنمایی درونخطی صفحهٔ 119 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 120 از ۱۲۱بازگشت به فهرست
npm - Node Package Manager، مدیر بسته برای زبان برنامهنویسی JavaScript.
Webpack - بستهبند ماژول متنباز جاوااسکریپت که عمدتاً برای JavaScript استفاده میشود، اما میتواند منابع فرانتاند مانند HTML، CSS و تصویر را نیز تبدیل کند.
Razor Class Library (RCL) - کتابخانهٔ تخصصی .NET برای مؤلفههای Razor و منابع پشتیبان آنها. RCL را میتوان برای Razor View و Razor Page نیز استفاده کرد.
BuildWebCompiler - بستهٔ NuGet برای کامپایل منابع وب.
Library Manager (LibMan) - ابزار سبک دریافت وابستگیهای فرانتاند.
Code-Behind - الگوی نرمافزاریای که در آن نشانهگذاری یا کد نما، توسط فایلی جداگانه شامل منطق پشتیبانی میشود. فایل منطق در این الگو Code-Behind نام دارد.
Single-Page Application (SPA) - برنامه یا وبسایتی که بهجای روش پیشفرض مرورگر برای بارگذاری صفحههای کاملاً جدید، با بازنویسی پویای صفحهٔ جاری و دادههای تازهٔ سرور با مرورگر تعامل میکند.
Geolocation - شناسایی یا تخمین موقعیت جغرافیایی واقعی یک شیء.
IJSRuntime - نمونهای از زماناجرای جاوااسکریپت را نمایندگی میکند که فراخوانیها میتوانند به آن ارسال شوند.
Document Object Model (DOM) - رابطی چندسکویی و مستقل از زبان است که سند XML یا HTML را بهصورت ساختار درختی در نظر میگیرد.
RenderTree - نسخهای از DOM که تغییرات را میتوان سریع در آن اعمال کرد؛ گرهها را میتوان بدون پیامد رندر دوبارهٔ کامل صفحه ایجاد، بهروزرسانی یا حذف کرد.
Data Binding - تکنیکی عمومی که منبع دادهٔ ارائهدهنده و مصرفکننده را به هم متصل و آنها را همگام میکند.
Directive - سازهای زبانی که مشخص میکند کامپایلر ورودی خود را چگونه پردازش کند.
Microsoft MVP - جایزهٔ Microsoft Most Valuable Professional که مایکروسافت آن را به «متخصصان فناوریای که با اشتیاق دانش خود را با جامعه به اشتراک میگذارند» اعطا میکند.
بازنمایی درونخطی صفحهٔ 120 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
ترجمهٔ صفحهٔ 121 از ۱۲۱بازگشت به فهرست
بیشتر بدانید
دربارهٔ Progress
Progress با نماد بورسی NASDAQ: PRGS، پلتفرمی پیشرو برای توسعه و استقرار برنامههای راهبردی کسبوکار ارائه میکند. ما به مشتریان و شرکا امکان میدهیم تجربههای دیجیتال مدرن و اثرگذار را با کسری از تلاش، زمان و هزینه ارائه کنند.
Progress ابزارهای قدرتمندی برای ساخت آسان تجربههای کاربری سازگار در انواع دستگاه یا نقطهٔ تماس، انعطاف یک پلتفرم توسعهٔ برنامهٔ Cloud-Native برای ارائهٔ برنامههای مدرن، فناوری پیشروی اتصال داده، مدیریت محتوای وب، قواعد کسبوکار، انتقال امن فایل، پایش شبکه و همچنین یادگیری ماشین برندهٔ جایزه ارائه میکند که قابلیتهای شناختی را به بخشی از هر برنامه تبدیل میسازد.
بیش از ۱۷۰۰ فروشندهٔ مستقل نرمافزار، ۱۰۰٬۰۰۰ مشتری سازمانی و دو میلیون توسعهدهنده برای توانبخشی به برنامههای خود به Progress متکیاند. اطلاعات Progress در نشانی قابل مشاهدهٔ www.progress.com یا شمارهٔ +1-800-477-6473 ارائه شده است.
© ۲۰۲۰ Progress Software Corporation و/یا شرکتهای تابعه یا وابستهٔ آن. تمامی حقوق محفوظ است.
بازنمایی درونخطی صفحهٔ 121 فایل اصلی برای حفظ کامل شکلها، نمودارها، اسکرینشاتها و چیدمان مرجع.
کنترل پوشش نهایی
۱۲۱ صفحهٔ منبع و ۱۲۱ بخش ترجمه پردازش شدهاند. ۶۰ شکل شمارهگذاریشده، نمونههای جدولی، کدها، واژهنامه و صفحات پایانی با بازنمایی کامل صفحهٔ اصلی حفظ شدهاند.
تصاویر خارجی: صفر - CSS خارجی: صفر - JavaScript خارجی: صفر - فونت خارجی: صفر.