KnockoutJS’ning rivojlanishi

Keling, ushbu yondashuvni o’sha paytda keng tarqalgan boshqa bir mashhur yechim — KnockoutJS bilan solishtiramiz. KnockoutJS 2010-yillar boshlarida yaratilgan va u “observables”(kuzatiladigan narsalar) va “bindings”(bog’lashlar) yaratish uchun imkoniyat bergan kutubxona edi. Bu usul har safar state o’zgarganda bog’liqlikni kuzatib borishni ta’minlardi.

KnockoutJS eng dastlabkilardan yoki birinchi bo’lgan reaktiv JavaScript kutubxonalaridan biri edi. Bu yerda reaktivlik deganda, qiymatlar observables usulida state o’zgarishlariga javob berib yangilanishi tushuniladi. Zamonaviy reaktivlik usullari ba’zan “signallar” deb ataladi va ular Vue.js, SolidJS, Svelte, Qwik, hamda zamonaviy Angular kabi kutubxonalarda keng qo’llaniladi.

Observables tushunchasi bu — kuzatilishi mumkin bo’lgan ma’lumot manbai, bindings esa — ushbu ma’lumotni iste’mol qiladigan va uni foydalanuvchi interfeysida ko’rsatadigan elementlar edi: observables modelga o’xshar edi, bindings esa view vazifasini bajarardi.

Ammo, biz oldinroq muhokama qilgan MVC (Model-View-Controller) pattern’ining bir oz rivojlangan shakli sifatida, KnockoutJS Model-View-ViewModel yoki MVVM pattern’ga yaqinroq ishlardi (rasmga qarang). Keling, bu pattern’ni batafsilroq tushunishga harakat qilamiz.

MVVM pattern image

MVVM pattern

MVVM (Model-View-ViewModel) patterni — bu o’ziga xos arxitektura dizaynidir va ayniqsa WPF va Xamarin platformalarida yaratilgan murakkab foydalanuvchi interfeyslari uchun mashhurdir. Bu, an’anaviy MVC patternining rivojlangan shakli bo’lib, zamonaviy UI dasturlash platformalariga moslashtirilgan. Quyida MVVM tarkibiy qismlarini qisqacha ko’rib chiqamiz:

Model

  • Dasturning ma’lumotlari va biznes logikalarini ifodalaydi.
  • Ma’lumotlarni olish, saqlash va qayta ishlash uchun javobgardir.
  • Odatda, u ma’lumotlar bazasi, servislar yoki boshqa ma’lumot manbalari bilan ishlaydi.
  • View va ViewModel bilan bog’liq emas, ulardan bexabar.

View

  • Dasturning foydalanuvchi interfeysi(UI)ni ifodalaydi.
  • Foydalanuvchiga ma’lumotni ko’rsatadi va foydalanuvchi kiritgan ma’lumotlarni qabul qiladi.
  • MVVM’da View passiv hisoblanadi va hech qanday dastur logikasiga ega emas. Buning o’rniga, u ViewModel bilan deklarativ tarzda bog’lanadi va ma’lumotlarni bog’lash mexanizmlari orqali avtomatik ravishda o’zgarishlarni aks ettiradi.

ViewModel

  • Model va View o’rtasidagi ko’prik vazifasini bajaradi.
  • View uchun ma’lumotlar va buyruqlarni ko’rsatadi. Bu ma’lumotlar ko’pincha tayyor ko’rinishga ega formatda bo’ladi.
  • Foydalanuvchi kiritgan ma’lumotlarni boshqaradi, ko’pincha buyruq pattern’i orqali.
  • Prezentatsiya logikasini o’z ichiga olib, ma’lumotlarni Model’dan olib, View’da osongina ko’rinadigan formatga aylantiradi.
  • Muhimi, ViewModel ishlatilayotgan maxsus View’dan bexabar, bu esa mustaqil arxitekturani yaratishga imkon beradi.

MVVM pattern’ining afzalliklari

MVVM pattern’ining asosiy afzalligi MVC’ga o’xshash ma’suliyatlarni ajratish prinsipi hisoblanadi, bu quyidagi yutuqlarga olib keladi:

  • Testlash qulayligi: ViewModel va View o’rtasidagi mustaqillik UI’ni ishtirok ettirmasdan unit testlar yozishni osonlashtiradi.
  • Qayta foydalanish qulayligi: ViewModel turli ko’rinishlar yoki platformalar o’rtasida qayta foydalanilishi mumkin.
  • Davom ettirish qulayligi (Maintainability): MVVM pattern’ida aniq ajratilgan ma’suliyatlar tufayli, kodni boshqarish, kengaytirish va refaktor qilish osonlashadi.
  • Ma’lumot bog’lash qulayligi (Data binding): MVVM pattern’i ma’lumot bog’lashni qo’llab-quvvatlovchi platformalarda juda yaxshi ishlaydi. Bu interfeys yangilanishi uchun yozilishi kerak bo’lgan ortiqcha boilerplate kodni kamaytiradi.

MVC va MVVMning asosiy farqlari

Biz MVC va MVVM pattern’larini muhokama qilganimiz sababli, ularning orasidagi farqni tushunishimiz uchun ularni tezda ko’rib chiqamiz:

KriteriyaMVCMVVM
Asosiy maqsadVeb-dasturlar uchun foydalanuvchi interfeysi va logikani ajratadi.Murakkab UI ilovalari uchun mo’ljallangan, ayniqsa desktop yoki SPA kabi ikki tomonlama ma’lumot bog’lashni ta’minlaydi.
KomponentlarModel: ma’lumot va biznes logikasi. View: foydalanuvchi interfeysi. Controller: foydalanuvchi kiritgan ma’lumotlarni boshqaradi, View’ni yangilaydi.Model: ma’lumot va biznes logikasi. View: foydalanuvchi interfeysi elementlari. ViewModel: Model va View orasida ko’prik.
Ma’lumot oqimiController foydalanuvchi kiritgan ma’lumotlarni boshqaradi, u Model va View’ni yangilaydi.View to’g’ridan-to’g’ri ViewModel bilan bog’lanadi. View’dagi o’zgarishlar avtomatik ravishda ViewModel’da aks ettiriladi va aksincha.
Ajratish(Decoupling)View odatda Controller bilan mustahkam bog’langan.ViewModel alohida View’dan mustaqil bo’lgani uchun, yuqori darajada ajratish imkoniyatini beradi
Foydalanuvchi interaktivligiController tomonidan boshqariladiViewModel’dagi ma’lumot bog’lashlar va buyruqlar orqali boshqariladi.
Platformaga moslikVeb platformalariga mos (Ruby on Rails, Django, ASP.NET MVC kabilar).Ma’lumot bog’lashni qo’llab-quvvatlovchi platformalar uchun mos (WPF, Xamarin kabilar).

Ushbu qisqa taqqoslashdan ko’rinib turibdiki, MVC va MVVM pattern’larining haqiqiy farqi birikish(coupling) va ma’lumotni bog’lash(bind) uslubida: Model va View o’rtasida Controller mavjud emasligi sababli, ma’lumotlarga egalik qilish foydalanuvchiga aniqroq va yaqinroq bo’ladi. React bu borada MVVM pattern’ini yanada yaxshilab, bir tomonlama ma’lumot oqimini ta’minlaydi, bu esa ma’lumotga egalik qilishni yanada aniqroq qiladi, shunday qilib, state faqat unga ehtiyoj sezadigan komponentlarga tegishli bo’ladi. Hozircha KnockoutJS’ga qaytaylik va uning React bilan qanday bog’liqligini ko’rib chiqaylik.

Knockout’da “Like” button misoli

KnockoutJS observable’lar va binding’lar bilan ishlash uchun API’lar eksport qilgan. Endi KnockoutJS’da “Like” tugmani qanday amalga oshirganimizni ko’rib chiqaylik. Bu bizga “Nega React kerak?” degan savolga yaxshiroq tushunishimizga yordam beradi. Bu yerda KnockoutJSd’agi button kodini ko’rib chiqamiz:

function createViewModel({ liked }) {
    const isPending = ko.observable(false);
    const hasFailed = ko.observable(false);
    const onClick = () => {
        isPending(true);
        fetch("/like", {
            method: "POST",
            body: JSON.stringify({ liked: !liked() })
        })
        .then(() => {
            liked(!liked());
        })
        .catch(() => {
            hasFailed(true);
        })
        .finally(() => {
            isPending(false);
        });
    };
    return { isPending, hasFailed, onClick, liked };
}
 
ko.applyBindings(createViewModel({ liked: ko.observable(false) }));

KnockoutJSda “view model” JavaScript obyekti bo’lib, u o’zida bizning sahifamizdagi turli elementlarga bog’langan kalitlar va qiymatlarni saqlaydi, bunda data-bind atributidan foydalaniladi. KnockoutJS’da hech qanday “komponentlar” yoki “shablonlar” yo’q, faqat view model va uni brauzerda elementlarga bog’lash usuli mavjud.

createViewModel funksiyasi tahlili

Bizning createViewModel funksiyamiz KnockoutJS yordamida qanday qilib view model yaratishni ko’rsatadi. Shundan so’ng biz ko.applyBindings funksiyasidan foydalanib, view modelni host muhit(brauzer) bilan bog’laymiz. ko.applyBindings funksiyasi view modelni oladi va brauzerda data-bind atributiga ega bo’lgan barcha elementlarni topadi, shundan keyin Knockout ularni view model bilan bog’laydi.

Brauzerimizdagi tugma quyidagi tarzda view model xususiyatlariga bog’lanadi:

<button
    data-bind="click: onClick, text: liked ? 'Liked' : isPending ? [...]
></button>

Shuni ta’kidlash kerakki, bu kod soddalashtirish maqsadida qisqartirilgan.

Biz HTML elementini createViewModel funksiyamiz bilan yaratgan “view model”imiz bilan bog’laymiz va sayt interaktiv holga keladi. Tasavvur qilganingizdek, kuzatilishi mumkin bo’lgan o’zgarishlariga aniq obuna bo’lish va keyin foydalanuvchi interfeysini bu o’zgarishlarga javoban yangilash katta mehnat talab qiladi. KnockoutJS o’z davrining ajoyib kutubxonasi bo’lgan, lekin vazifani bajarish uchun ko’p qo’shimcha kod(boilerplate) talab qilingan.

Bundan tashqari, view modellari juda katta va murakkab bo’lib ketar edi, bu esa kodni refaktor qilish va optimizatsiya qilishda muammolarga olib kelgan. Oxir-oqibat, bu narsa testlash va tushunish qiyin bo’lgan juda uzun va monolit view modellariga olib keldi. Shunga qaramay, KnockoutJS o’z davrida juda ommabop va katta yutuqqa erishgan kutubxona edi. U izolyatsiya qilingan holatda testlash uchun ham nisbatan oson bo’lgan, bu katta ustunlik hisoblanardi.

Qo’shimcha, KnockoutJS’da bu tugmani qanday test qilganimizni ko’rib chiqamiz:

test("LikeButton", () => {
    const viewModel = createViewModel({ liked: ko.observable(false) });
    expect(viewModel.liked()).toBe(false);
    viewModel.onClick();
    expect(viewModel.liked()).toBe(true);
});