بسته

Ngrx چیست و چه زمانی از یک storage استفاده کنیم؟

این پست بخشی از مجموعه معماری Angular است که در آن مشکلات و راه حل های رایج طراحی را در سطح View Layer و لایه Service پوشش می دهیم.

 

لایه سرویس Angular – معماری Storage یا ذخیره سازی

با Angular، طراحی و توسعه لایه View برنامه ما ساده تر از همیشه است.

اما لایه سرویس (همچنین به عنوان لایه داده شناخته می شود) که در واقع قلب کاربردی برنامه است گزینه های زیادی را ارائه می دهد:

  • چگونه باید لایه سرویس را ساختار دهیم؟
  • آیا باید از store استفاده کنیم؟
  • آیا باید از Redux استفاده کنیم؟
  • آیا باید از RxJs های ساده استفاده کنیم؟
  • NgRx چطور؟

 

یکی از مواردی که در دنیای Angular بسیار محبوب است راه حل های store است.

آنها از دنیای React سرچشمه گرفتند و از طریق منحنی پذیرش فناوری معمول رفتند: پذیرش انبوه، درک اینکه این راه حل نهایی برای همه چیز نیست، و سپس در استفاده از آن در موقعیت های خاص، اما نه در موقعیت های دیگر.

 

چرا store ها در React اینقدر محبوب هستند؟

چرا store ها در دنیای React اینقدر محبوب شده اند، آیا دلیل خاصی وجود دارد یا ترکیبی از دلایل است؟ آیا این دلایل در مورد دنیای Angular نیز صدق می کند یا راه حل های جایگزینی وجود دارد؟ store ها چه مشکلاتی را حل می کنند؟

آیا توجه کرده اید که اطلاعات زیادی در مورد راه حل های storage وجود دارد، اما اطلاعات محدودی در مورد اینکه چه زمانی و چرا باید از آنها استفاده کنیم؟ بیایید به این سوالات بپردازیم.

در این پست به موضوعات زیر می پردازیم:

  • چه زمانی از Redux یا به طور کلی storage ها استفاده کنیم؟
  • آیا معمولاً به storage نیاز داریم؟
  • چرا Redux در دنیای React بسیار محبوب است؟
  • آیا مشکلات حل شده توسط Redux در دنیای Angular نیز وجود دارد؟
  • یک storage چه مشکلاتی را حل می کند؟
  • چه نوع برنامه هایی از راه حل storage سود می برند؟
  • چه نوع ابزاری به راه حل storage مرتبط است؟
  • جریان داده یک طرفه در React و Angular
  • storage ها و تست پذیری
  • storage ها و عملکرد
  • storage ها و ابزار
  • Redux در مقابل Mobx
  • مقایسه ابزار با Mobx و CycleJs

 

چه زمانی از Redux یا به طور کلی store ها استفاده کنیم؟

store ها از دنیای Redux سرچشمه گرفته‌اند، بنابراین یکی از بهترین مکان‌ها برای اولین بار دیدن و سپس برداشتن آن از آنجا خواهد بود.

بیایید راهنمای react-howto را به اکوسیستم React ببریم، چه توصیه هایی وجود دارد؟ در اینجا یک نقل قول مهم است:

احتمالاً نام Flux را شنیده اید. تعداد زیادی اطلاعات نادرست در مورد Flux وجود دارد. بسیاری از مردم می نشینند تا یک برنامه بسازند و می خواهند مدل داده خود را تعریف کنند و فکر می کنند برای انجام آن باید از Flux استفاده کنند. این روش اشتباهی برای اتخاذ Flux است.

همچنین این پست شناخته شده توسط خالق Redux وجود دارد – شما ممکن است نیازی به Redux نداشته باشید، که می تواند به طور کلی برای هر راه حل store اعمال شود.

و سپس این عبارت دیگر در React How-To وجود دارد که به نظر می رسد به طور یکسان برای Flux اصلی، Redux، Ngrx Store یا هر راه حل store به طور کلی اعمال می شود:

شما می دانید که چه زمانی به Flux نیاز دارید. اگر مطمئن نیستید که به آن نیاز دارید یا خیر، به آن نیاز ندارید.

بر این اساس، به نظر می رسد storage ها برای استفاده سیستماتیک توسط برخی از سازندگان اصلی آنها توصیه نمی شود. در این پست‌ها، ما این تصور را داریم که به نظر می‌رسد سازندگان از این می‌ترسند که store  ها به عنوان یک راه‌حل یک‌اندازه دیده شوند.

اما پس از آن با پست هایی مانند این مواجه می شویم – به نظر می رسد همیشه به Redux نیاز دارم

حتی اگر storage توسط سازندگان خود با احتیاط توصیه می‌شوند، هنوز در مقیاس جهانی React مورد استفاده قرار می‌گیرند.

چرا ممکن است؟ بیایید سعی کنیم به آن پاسخ دهیم.

 

چه زمانی استفاده از Flux یا Redux توصیه می شود؟

اگر در اسناد React How-To عمیق‌تر بگردیم، به چند نشانه می‌رسیم که چه زمانی از Flux سود می‌بریم:

کامپوننت های React در یک سلسله مراتب مرتب شده اند. اغلب اوقات، مدل داده شما نیز از یک سلسله مراتب پیروی می کند. در این مواقع Flux چیز زیادی برای شما نمی‌خرد. با این حال، گاهی اوقات، مدل داده شما سلسله مراتبی نیست. وقتی کامپوننت‌های React شما شروع به دریافت پروپوزال‌هایی می‌کنند که به نظر غیر ضروری می‌آیند، یا تعداد کمی از کامپوننت‌ها شروع به پیچیده شدن می‌کنند، ممکن است بخواهید Flux را بررسی کنید.

همچنین اگر مسائل را بررسی کنیم، این توصیه ها را نیز دریافت می کنیم. معماری store مانند توصیه می شود اگر:

شما یک قطعه داده دارید که باید در چندین مکان در برنامه خود استفاده شود، و انتقال آن از طریق props باعث می‌شود که اجزای شما اصل تک‌مسئولیت را زیر پا بگذارند (یعنی رابط کاربری آن‌ها منطقی‌تر شود)

اما این سناریو نیز وجود دارد:

چندین بازیگر مستقل (به طور کلی، سرور و کاربر نهایی) وجود دارند که ممکن است آن داده ها را تغییر دهند

بنابراین چند موقعیت وجود دارد که پیشنهاد می شود از یک راه حل store همراه با React استفاده کنید. بنابراین بیایید ببینیم که چگونه این مورد در Angular قرار می گیرد.

 

store ها و برنامه های کاربردی با به روز رسانی همزمان

اگر فقط بر اساس قسمت آخر قرار بگیریم، فقط تعداد کمی از برنامه ها، یعنی برنامه هایی که نیازمندی های فشار سرور هستند، از Flux بهره مند می شوند. زیرا معمولاً زمانی اتفاق می‌افتد که چندین بازیگر داریم که داده‌های مشابهی را به‌روزرسانی می‌کنند، و این مورد مربوط به مشکل اصلی شمارنده فیس‌بوک است که منشا Flux بود.

برای جزئیات بیشتر در مورد موضوع پیشخوان اصلی، به سخنرانی اصلی Flux نگاهی بیندازید:

 

 

توجه داشته باشید که برای قرار گرفتن در این وضعیت نیازی به فشار سرور نداریم، نظرسنجی طولانی با setInterval یا اصلاح داده‌های داخل setTimeout ما را به سناریوی مشابهی سوق می‌دهد: چندین بازیگر همزمان داده‌های مشابه را ویرایش می‌کنند.

به جرات می توان گفت که بسیاری از برنامه ها این مشکل را ندارند، درست است؟ این یک مشکل مهم است که در صورت وجود باید نسبت به آن طراحی کنیم، اما آیا اکثر برنامه ها آن را دارند؟ احتمالاً نه، فقط یک کلاس خاص از برنامه ها.

اما پس چرا Redux به طور جهانی در دنیای React پذیرفته شده است؟ که دلیل دیگر ارائه شده باقی می ماند… (متن اصلی را بخوانید)

 

store ها و کارایی

گاهی اوقات از store ها به عنوان راهی برای کارایی بیشتر یک برنامه نام برده می شود، زیرا می توانیم با استفاده از چیزی مانند ImmutableJs یا Deep Freeze حالت را غیرقابل تغییر کنیم و سپس می توانیم از تشخیص تغییر OnPush در همه جا استفاده کنیم.

مکانیسم تشخیص تغییر Angular به سرعت در حال کار است و بسیار شهودی عمل می کند. به طور پیش فرض فقط آنچه در template به عنوان عبارات استفاده می کنیم برای تشخیص تغییرات استفاده می شود، بقیه موارد نادیده گرفته می شوند.

OnPush واقعاً یک بهینه‌سازی است که احتمالاً فقط تعداد کمی از برنامه‌ها از آن سود می‌برند، مانند برنامه‌هایی که داده‌های زیادی را بارگیری می‌کنند (و چه مقدار داده می‌توانیم بارگذاری کنیم که همچنان برای کاربر مفید باشد)، یا برنامه‌هایی که در دستگاه‌های بسیار محدود اجرا می‌شوند.

به جرات می توان گفت که اکثر برنامه ها در این دسته بندی ها قرار نمی گیرند (با توجه به گوشی های هوشمند فعلی). اما اگر هنوز به OnPush نیاز داریم، می‌توانیم به سادگی از آن بدون ذخیره سازی استفاده کنیم، به خصوص اگر داده‌های ما عمدتاً فقط خواندنی باشند.

اگر برنامه یک داشبورد بی‌درنگ مانند داشبورد نمودار است، احتمالاً بهتر است داده‌ها را کنترل کنید یا راه‌حل دیگری. ما حتی می‌توانیم شاخه‌ای از UI را از تشخیص تغییر جدا کنیم و رندر آن را کاهش دهیم.

نکته اصلی در اینجا این است که افزودن Storing به این معنی است که ما یک برنامه کاربردی را کارآمدتر می کنیم یا بهینه سازی آن را آسان تر می کنیم، زیرا می توانیم سیستم تشخیص تغییر را به روشی کاملاً مستقل از ذخیره سازی بهینه سازی کنیم – این دو مورد را می توان با هم استفاده کرد اما ذاتا مرتبط نیست.

 

نصب یک store solution

 

npm install --save @ngrx/store @ngrx/core 

این به ما این امکان را می دهد که یک پایگاه داده در حافظه سمت کلاینت برای برنامه خود تعریف کنیم. اولین چیزی که می خواهیم تعریف کنیم این است که storage چه نوع داده ای را نگه می دارد.

 

تعریف شکل Application State

پس از نصب store ، ایده خوبی است که یک نوع سفارشی به نام ApplicationState تعریف کنید:

 

export interface ApplicationState {
    uiState: UiState,   
    storeData: StoreData   
}

 

همانطور که می بینیم، محتویات پایگاه داده درون حافظه ما را می توان به دو بخش تقسیم کرد: برخی از حالت ها صرفاً به UI مربوط می شود و ما آن را به عنوان یک نوع سفارشی به نام UiState تعریف می کنیم:

 

export interface UiState {
    userId:number;
    currentThreadId: number;
    currentError?: string;
}

 

این حالت شامل کاربری است که در حال حاضر در برنامه چت ما وارد شده است، thread انتخاب شده فعلی در لیست رشته و پیام خطای فعلی نمایش داده شده در صورت وجود.

همانطور که می بینیم این حالت کاملاً متفاوت از داده های thread ها است که در نوع سفارشی StoreData تعریف شده است:

 

export interface StoreData {
    participants: {[key:number]: Participant};
    threads: {[key:number]: Thread};
    messages: {[key:number]:Message};
}

 

چگونه پایگاه داده درون حافظه را پر کنیم؟

در این مرحله کاری که می‌خواهیم انجام دهیم این است که داده‌ها را از طریق یک درخواست HTTP ساده از backend دریافت کنیم و آن را در داخل storage ذخیره کنیم.

نحوه تعامل ما با store از طریق ارسال دستوری برای تغییر داده های داخلی آن به روشی از پیش تعریف شده است. آن object فرمان Action نامیده می شود. این عمل باعث تغییر وضعیت store به صورت همزمان و فوری می شود.

در مورد اقدامات ناهمزمان چطور؟ این نیز در قسمت 2 پوشش داده خواهد شد. در حال حاضر ما داده ها را آماده بارگذاری در stرروore داریم، پس چگونه آن را بارگذاری کنیم؟

ما با تعریف یک Action شروع می کنیم:

 

import {Action} from "@ngrx/store";

export const USER_THREADS_LOADED_ACTION = 'USER_THREADS_LOADED_ACTION';

export class UserThreadsLoadedAction implements Action {

    readonly type = USER_THREADS_LOADED_ACTION;

    constructor(public payload?:AllUserData) {

    }
}

 

اکشن شامل یک type و یک payload است. payload یک ساختار داده انتقال آبجکت است که با داده‌هایی که در یک درخواست http از backend واکشی شده است مطابقت دارد:

 

export interface AllUserData {
    participants: Participant[];
    threads: Thread[];
    messages: Message[];
}

 

this.backendService.loadInitialUserData()
  . subscribe(initialUserData => this.store.dispatch(
       new UserThreadsLoadedAction(initialUserData)) );

 

در مورد reducer ها اینجا بخوانید.

منبع: blog.angular

پست های مرتبط