پشت صحنه - نگاهی به Reactivity در VUE,ANGULAR,REACT

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


این پست در مورد Reactivity یا Change Detectionهست و منظور از reactive در این نوشته reactive programming و مباحثش نیست بلکه در این مورده که ما توی اپلیکیشن هامون یه سری تغییرات داده یا state داریم (که اگر نداشتیم که دیگه اپلیکیشن نبود) خب این فریم ورک یا لایبرری ما از کجا میفهمه که داده عوض شده بزارین یه مثال بزنم.

ما دو متغیر نام و نام خانوادگی داریم و میخوایم متغیر نام کامل ترکیب این ۲ باشه پس کدمون یه همچین شکلی پیدا میکنه.

let name = "Steve";
let family = "Jobs";
let fullName =  `${name} ${family}`;

الان مقدار fullName شده Steve Jobs حالا اگر ما یه دکمه ای رو فشار دادیم و name شد Bill ما انتظار داریم و دوس داریم که fullName ما هم مقدارش عوض بشه و تو این مثال بشه Bill Jobs ، که در عمل این رخ نمیده و مقدار fullName هنوز همون مقدار قبلیه. Reactivity که ما منظورمونه همینه که چجور میشه این کارو انجام داد و اگر بتونیم این کارو بکنیم و حالا این رو در محیط وب در نظر بگیریم و بتونیم بعد از این که اتوماتیکی مقدار رو عوض کردیم بیایم html element و مقدار داخلشو عوض کنیم تونستیم یه نانو ری اکت یا ویو بنویسیم دیگه😂.

حالا که با مفهومی که میخواستم برسونم اشنا شدیم بریم ببینیم هرکدوم از این فریم ورکا از کجا میفهمن که ما مقداری رو عوض کردیم که دوباره render میکنن و ما در لحظه تغییر رو میبینم.

Angular

روشی که Angular برای این کار انتخاب کرده بسیار پیچیده است و اسم روشش هم Dirty Checking هست که خودش چندین مقاله رو میطلبه اینه ولی خلاصش اینه که با کمک لایبرری به نام Zone قبل و بعد هر
Async Task ئی مثل Event ها( click یا input یا ...) یا HTML Request ها یا تایمر ها(setTimeout و ...) یا موراد مشابه با کمک هوکی که در اختیارتون میزاره میتونین مدیریتی روی کاری که میخواین داشته باشیم مثلا در حالت عادی نمیشه کد زیر زمان اجراش رو مشخص کرد چون تایمری که برای پیدا کردن زمان میزاریم منتظر پایان Async Task مون نمیمونه پس محاسبش براحتی امکان پذیر نیست.

function a(){
    console.log('a');
}
function b(){
     console.log('b'); 
}
function c(){
    setTimeout(d,100*Math.random());
}
function d(){
    console.log('d');
}
a();
c();
b();

اما با کمک هوک های zone.js میتونیم پس از پایان یافتن Task مون میتونیم زمان رو محاسبه کنیم و وقتی همچین کاری میتونیم بکنیم پس توی انگولار هم با توجه به این نکته که بدون Task ئی داده های ما تغییر نمیکنن ، اگر بتونیم پس از پایان هر Task بیایم و state کامپوننت رو چک کنیم میتونیم بفهمیم که داده تعییر کرده یا نه و اگر تغییر کرده در صورت لزوم دوباره رندر کنیم.

React

بر خلاف انگولار ، React روش ساده تری رو در نظر گرفته برای این مورد . روش React بدین صورت هست که شما یه state دارین و برای اپدیت state نیاز دارین که حتما setState رو کال کنین و این متد تعریفش یک چیزی تو این مایه هاست.

setState(newState){
    Object.assign(this.state, newState);
    this.goForLifecycleHooks()
}

بعد ازون هم با کمک متدی مثل goForLifecycleHooks میره سراغ lifecycle hook هاش تا مشخص بشه باید رندر بشه دوباره یا نه.

Vue

ویو هم همانند React و Angular از روش جالبی برای Reacivity استفاده میکنه. و از یه همچین کدی استفاده میکنه :

function changeDetection(obj){
    Object.keys(obj).forEach(key=> {
        let value = obj[key];
        Object.defineProperty(obj,key,{
            get (){
                console.log(`You are getting "${key}" : ${value}`);
                return value;
             },
             set (newValue){
                 console.log(`old value "${key}"  : ${value}`);  
                 console.log(`new value "${key}" : ${newValue}`); 
                 value = newValue;
             }
        });
    });
}

خب خط 2 Object.keys رو داریم اگر با این متد اشنا نیستین کارش این هست که لیست key های یک Object رو به شما برمیگردونه و خط 3 مقدار اون key رو ذخیره میکنیم. حالا میریم سر بخش جالب ماجرا که Object.defineProperty هست این متد استاتیک یک property جدید میسازه یا تغییرش میده. و سینتکسش بدین صورت هست:

Object.defineProperty(obj, prop, descriptor)

obj : آبجکتی که میخوایم این بلارو سرش بیاریم.
prop : مشخص میکنیم کدوم پراپرتی
descriptor : ابجکت توصیفگرمون هست که میخوایم بگیم پراپرتی چه خصوصیاتی داشته باشه یا چجوری رفتار کنه.

توصیفگر همونطور که گفتیم یه ابجکته که یه سری خصوصیات رو میشه مشخص کرد .

configurable

اگر false باشه شما دوباره نمیتونین خصوصیات property تون رو عوض کنین همچنین شما نمیتونین این property رو حذف کنین. (مقدار پیش فرضش false هست).

enumerable

در بالا در مورد Object.keys صحبت کردیم اگر enumerable مقدارش true باشه در لیست ظاهر میشه. همچنین شما میتونین با فانکشن propertyIsEnumerable مقدار enumerable پراپرتی مورد نظرتون رو بگیرین. (مقدار پیش فرضش false هست).

این 2 خصوصیات اصلی بودن ولی شما علاوه بر این 2 خصوصیات اختیاری دیگه رو میتونین مشخص کنین.

value

مقدار که یک property رو میتونیم از این طریق مشخص کنیم . value میتونه هر مقدار معتبری باشه (عدد/رشته/فانکشن/ابجکت/...) و مقدار پیش فرض هم undefined هست.

writable

اگر نمیخواین مقدار property قابل تغییر باشه writable رو برابر false بزارین ، مقدار پیش فرض هم همین false هست.

علاوه بر این 4 مشخصه شما میتونین 2 فانکشن getter/setter رو نیز مشخص کنین.

get

فانکشن get مقدار property رو برمیگردونه و اگر property در خود آبجکت یا آبجکت هایی که ازش ارث بری کرده وجود نداشته باشه undefined برمیگردونه. شما میتونین همانند بالا قبل از return هر کاری که بخواین انجام بدین یا حتی روی داده برای نمایشش تغییراتی رو اعمال کنین مثلا رشته رو upper case کنین.

set

این فانکشن یک مقدار جدید به عنوان مقدار جدید دریافت میکنه و میتونه مقدار property رو عوض کنه.
*: شما همانند بالا برای set کردن یک property به یه متغیر واسطه نیاز دارین.
**:اگر descriptor شامل مشخصه های value یا writable و get یا set باشه شاهد error خواهیم بود.

همونطور که دیدین ما میتونیم با کمک descriptor مشخصه هایی رو برای property مون تعریف کنیم. با کمک descriptor و get / set ما میتونیم برروی رفتار property نظارت داشته باشیم.

بر میگردیم به بحثمون پس وقتی ما میتونیم همچین نظارتی رو داشته باشیم میتونیم وقتی داده ای رو در vue تغییر دادیم میتونیم تغییرات رو ببینیم و عوض console.log کردن کارای لازمه بعد از change detection مثل وارد شدن به lifecycle هارو داشته باشیم.



همونطور که دیدیم هر فریم ورک (لایبرری) روش خودش رو برای Change Detecting داره بنابراین وقتی جایی نیاز شد بدون استفاده از فریم ورک Reactivity داشته باشیم میتونیم از یکی ازین روش ها استفاده کنیم.امیدوارم وقتی بشه و در مورد هر روش به صورت جدا و کامل پستی رو بنویسم.

موفق باشین.