Як війни браузерів змінили Інтернет

Google Chrome впевнено домінує над рештою браузерів, займаючи понад 60% світового ринку. Але так було далеко не завжди. Більше 10 років Internet Explorer, встановлений майже кожному комп’ютері, зберігав статус найголовнішого браузера. А потім настала жорстка конкурентна боротьба, яку іноді називають «браузерними війнами».

В наші дні Internet Explorer живе у нашій пам’яті переважно завдяки мемам – він програв у боротьбі з молодшими суперниками. На початку березня 2021 року Microsoft припинила підтримку класичної версії браузера Edge, наступника Internet Explorer. Компанія зосередилася на розробці оновленого Edge на основі двигуна Chromium, що вкотре підтверджує тотальне панування Google.

війни браузерів

Як усе починалося

Тім Бернерс-Лі

1989 року Тім Бернерс-Лі, молодший науковий співробітник Європейської організації ядерних досліджень (CERN), придумав, як спростити збір інформації всередині організації. Його ідея полягала в тому, щоб зберігати всі документи в єдиному веб-сервісі та об’єднувати їх за допомогою гіперпосилань.

Йому на допомогу прийшов колега Роберт Кайо. У 1990 році руками цих ентузіастів було придумано мову HTML, протокол HTTP та ідентифікатор URI. 12 листопада 1990 Бернерс-Лі оголосив назву браузера – WorldWideWeb (пізніше його перейменували в Nexus), а під Різдво того ж року браузер побачив світло. 20 грудня в інтернеті з’явився перший сайт – на ньому роз’яснювалася концепція WorldWideWeb.

Тіму Бернерсу-Лі та Роберту Кайо на всю роботу вистачило якихось двох місяців. WorldWideWeb виводив на екран лише літери та цифри – жодних зображень чи графіки. Набір функцій зводився до перегляду, оновлення сторінки та переходу за гіперпосиланнями. У 1991 році до участі у проекті було запрошено сторонніх розробників.

WorldWideWeb

Тім Бернерс-Лі розумів, що справжній потенціал його дітища буде по-справжньому розкритий, якщо кожен зможе самостійно вивчити його структуру. Він хотів викласти вихідний код у вільний доступ, для чого потрібно умовити керівників CERN.

У квітні 1993 року CERN оголосив WorldWideWeb вільною платформою. За свій винахід Тім Бернерс-Лі не отримав жодної копійки. Того дня перед усіма веб-розробниками відкрилися нові можливості. У 2003 році автори нових браузерів надихнулися прикладом Бернерса-Лі та працювали без авторських відрахувань.

Тіма Бернерса-Лі запитали: «Чи відчував він змішані почуття з приводу «заробляння грошей» на вебі?».

І його відповідь виявилася цілком логічною: «Не те щоб. Просто якби технологія стала пропрієтарною і перебувала б під моїм повним контролем, вона, напевно, не злетіла б. Зробити Web відкритою системою було необхідно для того, щоб вона стала універсальною. Не можна одночасно запропонувати чомусь стати спільним місцем та одночасно утримувати контроль над ним».

У 1994 році він перебрався до Массачусетського технологічного інституту та заснував Консорціум Всесвітньої павутини (скорочено W3C), де досі залишається директором. Ця міжнародна спільнота займається впровадженням стандартів у програмне забезпечення.

Тіма Бернерса-Лі вшановували на церемонії відкриття Олімпійських ігор 2012 року в Лондоні — шоу, присвячене видатним британським людям, винаходам та продуктам, які вплинули на світ. Він особисто брав участь у церемонії.

Маленький Netscape (Mosaic) та його фатальна помилка

Netscape (Mosaic)

Після публікації вихідного коду багато розробників надихнулися випуск своїх проектів. У 1992-1993 роках з’явилося кілька браузерів: MidasWWWW, MacWWWW та tkWWWWWW. Один із перших браузерів, текстовий Lynx, застосовується й досі. Крім трьох «W» в кінці назви, новачки знову і знову відтворювали одну й ту саму проблему — вони повністю копіювали WorldWideWeb і не робили нічого революційного. Лише у ViolaWWW була спроба інтегрувати графічний інтерфейс.

Марк Андреессен, студент-програміст з Університету Іллінойсу в Національному центрі суперкомп’ютерних додатків (NCSA), зумів зрушити справу з мертвої точки. У 1993 він написав браузер Mosaic і безкоштовно поширив його серед двох мільйонів користувачів. Новий браузер відрізнявся простим і зрозумілим інтерфейсом та стабільною роботою.

Mosaic також мав одну важливу особливість, якою не міг похвалитися жоден інший браузер: він показував картинки на одній сторінці з текстом, тоді як інші відкривали їх у окремому вікні. Користувачі Mosaic могли налаштовувати фон документа, шрифт для заголовків і вид тексту.

Головною перевагою Mosaic стала його кросплатформність: він працював на всіх операційних системах того часу. Тому не дивно, що до середини 90-х років частка користувачів цього браузера становила 80%.

Разом із Джеймсом Кларком, засновником Silicon Graphics, Марк Андреессен заснував софтверну компанію Mosaic Communications. Щоб уникнути проблем з ліцензуванням з боку NCSA, компанія була перейменована на Netscape Corporation, а браузер — на Netscape Navigator. Він був заснований на вихідному коді Mosaic.

Марк Андреессен

У віці 24 років Марк Андреессен сколотив статки 56 мільйонів доларів і потрапив на обкладинку журналу Time.

На той час Netscape вже умів відображати сторінки в міру їхнього завантаження і справлявся з електронною поштою. Удосконалення торкнулися та взаємодії з майбутніми користувачами. Коли функціональність браузера значно розширилася, Джеймс Кларк вигадав модель розповсюдження shareware. Це свого роду безкоштовна пробна передплата, коли користувачів знайомлять з усіма можливостями, а потім пропонують купити. Але для використання з некомерційною метою браузер можна було завантажити безкоштовно.

Бізнес процвітав, і прибутки компанії також зростали: з 85 мільйонів доларів у 1995 році до 346 мільйонів доларів у 1996 році. Стало ясно, що неможливо самотужки стати лідером без стабільного фінансування. За два роки Netscape Communications зросла до 1300 співробітників, і якби не гроші інвестора Джеймса Кларка, все це могло взагалі не статися.

wikipedia

Однак дещо засновники Netscape не врахували. Вони не викупили вихідний код Mosaic у NCSA. Права придбала Spyglass, не використавши жодного рядка коду для розвитку свого проекту Spyglass Mosaic.

Перша війна браузерів: Netscape vs. Internet Explorer

Netscape vs. Internet Explorer

Білл Гейтс скептично ставився до майбутнього інтернет-технологій, доки не побачив оглушливого успіху Netscape. Він купив вихідний код Mosaic у Spyglass за вісім мільйонів доларів. Для Netscape Communications це означало, що їм доведеться конкурувати не з ентузіастами-одинаками, а з компанією, яка багато разів перевершує їх за розмірами.

Microsoft ховала цей козир до 1995 року, до виходу ринку Windows 95 Plus. Роком раніше команда із шести програмістів під керівництвом Білла Гейтса розробила браузер під назвою Internet Explorer (IE). Його перша версія була встановлена ​​в пакеті оновлень Windows 95.

Через три місяці Microsoft представила другу версію браузера IE, яка мало чим відрізнялася від оригінального Mosaic. Але IE 2 був безкоштовним і доступним всім користувачів, включаючи комерційних. Microsoft додала в браузер підтримку кодувань, за що користувачі по всьому світу були їй дуже вдячні. Тепер вони отримали можливість переглядати сторінки рідною мовою.

З виходом Internet Explorer 3 ситуація стала складатися на користь Microsoft. Швидкість та зручність, якими славився Netscape, перетворилися на переваги IE 3 – він підтримував JavaScript та CSS. Netscape не був такий готовий: при появі найпростіших CSS-елементів браузер підвисав і оновлював сторінку. Для користувачів з обмеженим обсягом трафіку це було неприйнятно.

У жовтні 1997 року IE 3 був замінений на Internet Explorer 4, практично повністю інтегрований у Windows 98. На основі браузера був побудований знайомий усім Windows Explorer, а інтерфейс ОС наповнився чимось на кшталт гіперпосилань. Вплив IE на ринку зростав і, що важливіше, він почав обганяти конкурента Netscape.

Саме в цей момент 4-та версія браузера привернула увагу влади США. Їм не сподобалося, що браузер вбудований в Windows, але його не можна видалити або встановити будь-яким іншим способом. Уряд США вважав, що це порушує антимонопольне законодавство, і висунув звинувачення проти компанії. Конфлікт було вирішено у 2001 році: Microsoft погодилася постачати IE як стороннє програмне забезпечення.

Internet Explorer 5, представлений у березні 1999 року, більше не перезавантажував сторінку під час її оновлення. Нова технологія отримала назву Ajax. І щоб добити Netscape, IE 5 за замовчуванням вбудовувався майже у всі версії Windows, а також пакет додатків Office 2000.

Тим часом Netscape Navigator переходив із рук у руки, втрачаючи минулу славу. У 2003 році команду розробників браузера, викупленого наприкінці 90-х інтернет-провайдером AOL, було розпущено. Але перш ніж залишити поле бою, вони відкрили вихідний код, як колись вчинив Тім Бернерс-Лі. Ними рухала надія, що працю засновників компанії буде продовжено.

Повсталий з попелу – Firefox

Firefox

Код ліг основою браузера від некомерційної організації Mozilla Foundation. Перша урізана версія браузера протягом короткого часу носила звучне ім’я Phoenix, але через проблеми з реєстрацією торгової марки була перейменована на Firebird і, нарешті, на Firefox.

По суті Firefox став наступником Netscape, як Османська імперія колись стала наступницею Візантії.

Перш ніж у листопаді 2004 року Mozilla відкрила Firefox для завантаження, його розробкою та тестуванням займалася невелика команда програмістів. Широка публіка відразу оцінила можливості першої версії, а ненависники гегемонії IE і шанувальники Netscape з ентузіазмом сприйняли появу гідного суперника Microsoft. Вони навіть купили цілу сторінку у The New York Times для його реклами.

Firefox 1.0

За рік до виходу Mozilla Firefox компанія Білла Гейтса презентувала шосту версію Internet Explorer. Вона відкрила розробникам можливість створювати повнофункціональні веб-програми, але цього разу політика тотального впровадження в ОС зіграла проти IE. Нові функції не були універсальними і працювали лише в Explorer та Windows.

Mozilla Foundation і Opera Software вчасно помітили проблему несумісності технологій, тому створили Робочу групу з Інтернету, гіпертексту, додатків та технологій (WHATWG). Вона займалася створенням нових відкритих стандартів для веб-розробки, що відправлялися на затвердження до консорціуму W3C.

Opera та Safari

Ще 1994 року норвезька телекомпанія Telenor розробила першу версію браузера Opera. Спочатку він був запущений для корпоративного використання, а потім став доступним для всіх бажаючих. Opera першою впровадила прототип вкладок (hotlist) і могла б стати третьою стороною у масштабній війні між IE та Netscape, але в результаті платний браузер виявився обділеним увагою.

Opera

З виходом третьої та четвертої версій shareware-модель була заміщена банерною рекламою (відключається за плату), а в 2005 році від платної моделі відмовилися зовсім. Opera першою додала управління жестами у версії 5.10 і навіть підключила ICQ-клієнт, але всі ці надмірності не знайшли своїх користувачів і незабаром канули в лету.

Інновації, ексклюзивні функції та бадьора робота при слабкому з’єднанні були трьома стовпами Opera, завдяки яким браузер зайняв невелику, але помітну частину ринку до 2000 року.

З версії 7 (2003 рік) Opera почала працювати на новому движку Presto і розширила свій арсенал за рахунок CSS. Вона стала справжнім піонером у розвитку інтернет-технологій: від створення голосового управління та підтримки Unicode до технології fit-to-width, що дозволяє сторінці підлаштовуватись під розмір екрану.

Safari

Останньою в гонці була Apple та її браузер Safari, створений у 2003 році. До 1997 року фінансове становище компанії різко погіршилося, і тимчасового генерального директора Стіва Джобса терміново потрібно було вирішувати цю проблему. Він оперативно спрацював, закривши Newton, Cyberdog та інші напрямки, але наступний його крок на перший погляд здавався немислимим — п’ятирічна угода з головним конкурентом Microsoft.

Білл Гейтс погодився інвестувати 150 мільйонів доларів у Apple, і компанії залагодили всі юридичні суперечки. Але було ще щось. До укладення угоди комп’ютери Macintosh продавалися лише з Netscape Navigator. Для угоди Microsoft розробила Internet Explorer для Mac’ів, а Apple вбудувала його в систему за умовчанням. Браузер Netscape не виявився «поза законом» і пропонувався як альтернатива.

Сафарі – це подорож дикою природою Африки, іншими словами, навігація дикою місцевістю. Apple хотіла, щоб назва браузера відображала відкриття, що здійснюються користувачами. Однак Explorer та Navigator вже були зайняті Microsoft та Netscape.

Особливістю Safari став сучасний двигун WebKit, код якого був частково розкритий всього два роки по тому. При цьому розробники Safari не відкладали баги в довгу скриньку і намагалися виправити їх якнайшвидше. Друга версія пройшла тест Acid2 від WaSP, який перевіряє браузери на відповідність веб-стандартам. А ще Safari виявився досить швидким.

Друга війна браузерів Internet Explorer vs. Opera vs. Safari vs. Firefox

Internet Explorer vs. Opera vs. Safari vs. Firefox

Відправною точкою другої війни браузерів є 2004: Internet Explorer користується шаленою популярністю, а дрібніші браузери шукають способи збити гіганта з п’єдесталу і поділити його аудиторію. Згідно зі статистикою, до 2004 року IE займав 80% ринку, Mozilla – 12%, Netscape – 2,2%, а Opera – 1,6%.

До 2004 року Microsoft розслабилася і стала припускатися помилок, за які вчепилися молоді конкуренти. Internet Explorer 7 з’явився після п’яти років затишшя, незважаючи на те, що попередня версія вже давно не влаштовувала веб-розробників. Тому вони поспішили до конкурентів.

Випущений лише у листопаді 2006 року Internet Explorer 7 працював лише на ліцензійній Windows. Протягом року після релізу частка ринку IE дещо зросла, тож нову версію було дозволено встановлювати на піратські операційні системи. Але це не допомогло. У січні 2007 року IE вперше почав масово втрачати користувачів.

ie

За словами Хаді Партові, розробника IE 5, причиною невдачі останніх версій є невірний підхід до розробки. На самому початку Microsoft зібрала команду, яка дихала новим браузером і бачила перед собою чітку мету вдертися на ринок і зайняти перше місце. Коли мети було досягнуто, компанія повірила у своє непорушне лідерство і перевела життєво важливих членів команди на MSN Explorer. Ті, хто залишився, займалися адаптацією попередніх версій під конкретну операційну систему.

З технічного погляду Internet Explorer перетворився з новатора на наздоганяючого. Браузер остаточно відокремився від системи, в ньому з’явилися вкладки та окреме поле для запитів, групування відкритих сторінок і перегляд мініатюр. У користувачів Windows з’явилася власна стрічка новин у браузері (RSS Reader), яку можна було налаштовувати за допомогою CSS.

Але майже всі перераховані нововведення вже були в першій версії Firefox.

За місяць до IE 7 у жовтні 2006 року вийшов Firefox 2.0. У ньому з’явилися функції, які тільки-но ввів Explorer 7. Наприклад, окрема для кожної вкладки кнопка закриття, автокорекція запитів, автоматичне відновлення сесії після падіння. І низку інших доповнень, без яких неможливо уявити сучасні браузери. А ще Firefox 2.0 запустив антифішингову програму і працював вже на новому двигуні Gecko 1.8.1.

Firefox 2.0

У 2005 році Firefox 1.5 встановив рекорд за кількістю завантажень. До середини жовтня лічильник показував 100 мільйонів завантажень. Через день після виходу Firefox 2.0 його завантажили понад два мільйони разів. У цей час Google промацувала ринок перед своїм дебютом. 2006 року Mozilla Foundation отримала роялті у розмірі 61,5 мільйона доларів за вбудовування Google Search у пошукові підказки.

Internet Explorer вів відкриту війну проти Opera. Вона почалася в 2001 році, коли користувачі Opera раптово втратили доступ до сайту MSN.com, але обмеження було швидко знято після втручання антимонопольної служби. Проте, проблеми з переглядом вмісту сторінки не зникли, хоча сайт не пропонував чогось непідйомного для Opera.

У 2003 році відбулася чергова спроба Microsoft досадити Opera. Користувачі Opera раптом виявили, що сторінка MSN спотворена – всі елементи були зміщені на 30 пікселів вліво. Microsoft заявила, що це був глюк у програмному забезпеченні Opera, але норвежці провели розслідування та з’ясували, що компанія Гейтса спеціально це підлаштувала. Microsoft так і не визнала своєї провини.

Рік по тому Opera Software відправила Microsoft електронний та паперовий листи зі скаргою на те, що власники Opera отримують у Hotmail неповний файл JavaScript. Через це не можна було очистити папки зі спамом. Microsoft не відповіла та не виправила помилку.

Opera

Незважаючи на всі перепони, Opera удосконалювалася. У 2005 році була представлена ​​Opera 8 зі спрощеним інтерфейсом – домашня сторінка перетворилася на пошукову систему. Наступне оновлення зробило браузер повністю вільним від реклами. Версія 9 вже пройшла тест Acid2 і однією з перших стала підтримувати завантаження торентів та віджети.

Версія 10, що вийшла 2009 року, була глобальною ревізією. Багато в чому вона зробила Opera одним з найзручніших браузерів. Крім того, норвежці першими відкрили світ браузерів для мобільного інтернету.

Chrome вступає в гру

Chrome

Internet Explorer зменшив оберти, а Firefox активно завойовував ринок, досягнувши позначки 20% до кінця 2007 року. Йому пророкували велике майбутнє, яке цілком могло наступити, якби не Google Chrome, урочисто представлений у вересні 2008 року (хоча чутки про його розробку ходили задовго до випуску браузера).

Однак реліз Chrome міг і не відбутися — лише на те, щоб переконати генерального директора Еріка Шмідта створити свій браузер, пішло шість років! Chrome працював на відкритому проекті Chromium і зарекомендував себе движку WebKit від Safari, що зробило його найшвидшим браузером. Він впевнено вийшов на ринок завдяки рекламі та активно набирав популярності.

Жодна інша програма не могла похвалитися такою кількістю локалізацій, як Chrome — 43 мови.

Браузер працював на всіх версіях Windows, починаючи з XP. У 2009 році компанія хотіла зробити доступними версії для Mac та Linux. У той же час Google не припиняла платити Mozilla Foundation за те, що їхня пошукова система використовується в Firefox. Контракт закінчився лише у 2014 році.

Інтерфейс Chrome відрізнявся від своїх попередників мінімалізмом. Спочатку це було прийнято без особливої ​​радості, але надалі переваги стали очевидними – з’явилося більше місця для сайту. Браузер об’єднав функції введення посилань та пошукового рядка, автоматично визначаючи, що є що – ця технологія отримала назву Omnibox. Крім того, Google додала підтримку плагінів.

При цьому розробники застосували новий підхід до архітектури браузера: сайт або плагін запускається як окремий процес у Windows і якщо одна з вкладок видасть помилку, решта продовжить свою роботу. Незабаром Google Chrome вийшов на мобільний ринок, потіснивши Opera та забезпечивши синхронізацію мобільних та настільних облікових записів.

Google Chrome

А потім з’явилася запаморочлива різноманітність інноваційних функцій та внутрішніх покращень. Google оновлювала та покращувала Chrome майже кожні два місяці. За десять років браузер оновився 69 разів, тоді як Microsoft та Firefox знадобилося більше року, щоб випустити свої оновлені версії.

Google удосконалила плагіни до рівня Firefox та звернула увагу на безпеку. Так світ заговорив про протокол HTTPS. Усі сайти, які його не використовували, Chrome став називати ненадійними.

Internet Explorer продовжував втрачати популярність. У 2009 році браузер на вимогу Європейського суду почав показувати екран із пропозицією спробувати інші браузери, що ще більше підірвало монополію Microsoft серед європейських користувачів. Крім того, на Windows з’явився Safari, який обганяє IE та Firefox за швидкістю роботи.

Із запуском високопродуктивного Firefox 3.0 аудиторія Microsoft вперше склала менше половини ринку — лише 46%. З виходом поновлення Firefox 3.5 Mozilla збільшила свою частку до 32%.

У листопаді 2011 року Chrome вперше випередив Firefox і набрав 27% користувачів. А через півроку Internet Explorer був змушений поступитися місцем Chrome.

У липні 2012 року у Chrome було вже 33% ринку. IE утримував 32%, а до кінця року різниця між велетнями збільшилася до 6%. Google не шкодувала грошей на рекламні кампанії та вміла підлаштовуватися під бажання користувачів. У цей час Microsoft знову вдалася до не найчесніших методів конкурентної боротьби. Передбачаючи поразку, Microsoft Security Essentials додав Chrome у свою вірусну базу як програмне забезпечення, що краде дані кредитних карток.

Аудиторія норвезької Opera не дотягувала навіть 2,5%. Незважаючи на всі переваги, мало хто готовий був платити за зручність. Люди йшли на інші платформи, втомившись зламувати кожне оновлення. Сьогодні браузер займає трохи більше 2% ринку.

Firefox

2020 року Mozilla звільнила десятки співробітників, щоб заощадити гроші для інвестицій в інновації. Компанія вирішила жити за коштами та не витрачати більше, ніж може заробити у найближчому майбутньому.

Firefox гідно відстояв честь прабатька Netscape. У першій війні браузерів Mozilla стримувала настання Microsoft на ринок браузерів. У 2010 році компанія знаходилася на відстані витягнутої руки від IE – 32% проти 46%. Chrome робив свої перші кроки.

Сьогодні, за даними StatCounter, Firefox посідає на світовому ринку четверте місце. Safari розташувався на третьому. Корпорація Стіва Джобса не змогла перетворити браузер на гіганта індустрії, і їй довелося припинити підтримку браузера на Windows і зосередитись на власних продуктах.

Google Chrome – це новий Internet Explorer. Сьогодні його використовують понад 65% користувачів по всьому світу, на другому місці за популярністю – Microsoft Edge – 10%. Браузер Apple (Safari) використовують переважно покупці продукції Apple.

Висновок. Нове поле битви для браузерів

Колумніст Дітер Бон пише, що смартфони стануть новим полем битви для третьої війни браузерів. Втім, кожному користувачеві доведеться самостійно відповісти на те саме питання: «Якому мобільному браузеру довірити свої персональні дані?».

Фактично, війна браузерів на мобільних пристроях триває з моменту виходу Opera Mobile у 2000 році та його спрощеної версії, Opera Mini, у 2006 році. У міру появи нових версій до них додавалися ті ж функції, що і в десктопному браузері. Через кілька років, 2008 року, Apple представила мобільну версію Safari і почала оновлювати її тричі на рік.

Opera

За даними StatCounter, у 2009 році браузер Apple захопив 34% ринку. Opera мала 25%, а Nokia і BlackBerry займали третю і четверту позиції. Всі вони, окрім Opera, були прив’язані до операційної системи. Норвежці по-справжньому виборювали своїх мобільних користувачів.

У жовтні 2015 року UC Browser несподівано став другим за популярністю мобільним браузером у світі. Мобільний браузер, що належить UCWeb китайській Alibaba Group, випередив Safari, рейтинг якого протягом року неухильно падав, і подолав позначку 17%. Більшість цього зростання була обумовлена ​​новими користувачами з Індії, Індонезії та Китаю.

У грудні 2012 року UC Browser відкрив офіс у США та вирішив, що іконка з мультяшною білкою не викличе довіри в американців, і її потрібно міняти. В результаті аніме-гризун став абстрактною фігурою.

Головною перевагою UC Browser стала хмарна технологія, яка стискає трафік до 60%, щоб скоротити час завантаження сторінки та локалізований контент для кожної країни, де компанія надає свої послуги. Співпраця з місцевими постачальниками контенту покращила зручність використання браузера та збільшила кількість завантажень.

Популярність Safari на мобільних пристроях легко пояснити: цей стандартний браузер стоїть на iPhone і Mac. Однак відносно недавно Firefox став на них також доступним. У Chrome інша доля: код Chromium, який використовується майже всіма сучасними браузерами, найкраще працює на Android.

Chrome для мобільних телефонів вийшов улітку 2012 року, коли версія для ПК вже стала найпопулярнішим браузером у світі. Люди просто ставили звичний браузер, знайомий їм з досвіду спілкування з комп’ютером.

Зрештою я хочу подякувати Тіму Бернерсу-Лі — винахіднику WorldWideWeb. Без його винаходу невідомо, як швидко людство почало б занурюватися в цифрові технології та як розвивалися б браузери. Тисячі веб-проектів могли б просто не з’явитися на світ.

Переклад статті “How the ‘Browser Wars’ Changed the Landscape of the Internet

Як працює алгоритм консенсусу Proof-of-Stake (PoS) і чому він такий популярний?

Proof-of-Stake (PoS)

  • Proof-of-Stake (PoS) — найпопулярніший алгоритм консенсусу в блокчейні, на основі якого побудовано багато криптовалют і блокчейн-платформ, наприклад Ethereum, Cardano, Solana, Tezos і Algorand.
  • Популярність PoS обумовлена ​​відсутністю необхідності купувати дороге обладнання для майнінгу та можливістю легкого пасивного заробітку через стейкінг криптовалют .
  • Перевагою Proof-of-Stake перед іншим популярним алгоритмом Proof-of-Work (PoW) є мале споживання енергії для генерації блоків та забезпечення безпеки блокчейну;

Чому і як виник Proof-of-Stake?

При розробці архітектури блокчейну, який є децентралізованим протоколом для перекладів з базою даних, що постійно оновлюється, виникає два ключові питання:

  • кому та за яким принципом надати право генерувати нові блоки;
  • як схвалюватимуться транзакції для забезпечення захисту від подвійних витрат та інших зловживань.

Вирішення цих питань призвело до появи кількох механізмів консенсусу, тобто наборів правил, за якими учасники децентралізованої мережі домовляються про те, як саме транзакції можуть бути затверджені та включені до нових блоків.

Автор біткоїна Сатоші Накамото в жовтні 2008 року в white paper першої криптовалюти запропонував механізм Proof-of-Work («доказ роботи»).

Згідно з PoW, оператори нод децентралізованої мережі (майнери) у режимі вільної конкуренції вирішують ресурсомісткі математичні завдання – пошук хеша блоку методом підбору. У разі успіху майнер або пул, що переміг, отримує можливість додати знайдений блок, а натомість отримує нагороду — нові біткоїни.

Вже через кілька років після запуску біткоїну стало зрозуміло, що принцип роботи Proof-of-Work призводить до постійного зростання майнінгової потужності, а отже, і витрат на електроенергію. Крім того, через необхідність використання потужного обладнання знижувалась доступність майнінгу.

11 липня 2011 року на популярному тоді форумі про криптовалюти Bitcointalk було запропоновано ідею альтернативного механізму консенсусу для біткоїну, який отримав назву Proof-of-Stake, або «доказ частки володіння».

Пропонувалося, що право голосу в децентралізованій мережі мають отримати всі її учасники відповідно до того, якою часткою загальної кількості монет вони мають.

Вже у серпні 2012 року цей новий механізм консенсусу отримав перше практичне втілення у криптовалюті PPCoin. Нові монети розподіляли через майнінг, а транзакції могла обробляти будь-яка нода, що зберігала криптовалюту PPC. Та ж гібридна схема консенсусу використовувалася і в інших ранніх PoS-проектах, наприклад Gridcoin і Blackcoin. Першою «чистою» PoS-криптовалютою без майнінгу став блокчейн Nxt, запущений 24 листопада 2013 року.

Механізм консенсусу Proof-of-Stakе виявився настільки вдалим і гнучким, що в наступні роки його впровадили в сотнях криптовалют у різних варіантах та модифікаціях.

Принцип роботи Proof-of-Stake

Відповідно до початкової концепції Proof-of-Stakе, право на управління блокчейном надається всім його учасникам відповідно до частки монет, якими вони володіють.

Наприклад, у криптовалюті Nxt з її «канонічним» механізмом PoS шанс сформувати черговий блок є у всіх користувачів, які мають в офіційному гаманці NXT Client щонайменше 1002 NXT протягом останніх 1440 блоків. При цьому кожен гаманець фактично є повним вузлом (нодою) та зберігає власну копію блокчейну. Такий гаманець може бути запущений на високопродуктивному сервері, так і на ноутбуці, мікрокомп’ютері Raspberry Pi і навіть у хмарному сервісі.

Чим більше монет у гаманці NXT, тим більша ймовірність, що він отримає право сформувати новий блок, і тоді користувачеві дістануться всі комісії за транзакції, що потрапили до блоку. В ідеальному випадку гаманець, який має 1% монет, буде формувати 1% усіх нових блоків.

Процес створення блоків у Nxt та інших ранніх PoS-криптовалютах отримав назву «форджинг» (тобто «ковка»), проте на цей час цей термін застосовується рідко.

Процес утримання криптовалюти в гаманці для отримання винагороди за участь у забезпеченні безпеки мережі називається «стейкінг». Сьогодні у багатьох PoS-криптовалютах відправка монет у стейкінг передбачає їхнє блокування у спеціальному смарт-контракті з неможливістю переміщення протягом певного часу, від кількох годин до кількох тижнів.

Як делегування монет вплинуло на продуктивність PoS

Використання механізму Proof-of-Stakе, коли виробником блоків може практично будь-який власник криптовалюти, дозволяє досягти високого рівня децентралізації та безпеки блокчейну. Однак, згідно з трилемою блокчейна, при цьому доводиться жертвувати продуктивністю. У згаданій мережі криптовалюти Nxt пропускна спроможність становить лише 4 транзакції в секунду, що помітно нижче, ніж у багатьох криптовалют, які використовують консенсус PoW. Наприклад, Dogecoin обробляє 33 транзакції за секунду.

Щоб знайти компроміс між децентралізацією та продуктивністю, запропонували концепцію делегування, коли з безлічі гаманців монети разом із правом голосу можуть передаватися небагатьом обчислювальним нодам.

У 2013 році Деніел Ларімер , американський програміст і криптопідприємець, використав цю концепцію для створення механізму Delegated Proof-of-Stake (DPoS). Вперше він був реалізований у блокчейн-платформі BitShares, а потім у різних варіантах втілений у найвідоміших криптопроектах EOS, Cardano, Tezos тощо. Сьогодні функція делегування стала галузевим стандартом та використовується практично у всіх імплементаціях PoS.

У DPoS власники криптовалюти можуть самі не брати участь у роботі мережі, але передавати для цього свої монети валідаторам — професійним учасникам, які керують нодами блокчейну. Натомість ті зобов’язуються нараховувати власникам монет нагороди, часто — за вирахуванням невеликої комісії.

У різних блокчейнах, залежно від їхньої архітектури, кількість валідаторів, що беруть участь у виробництві блоків, значно відрізняється.

  • Polkadot – до 16;
  • BNB Chain та EOS – 21;
  • Near – 100;
  • Cardano – близько 3200;
  • Avalanche – близько 1200;
  • Solana – понад 3400.
  • Ethereum – понад 400 тисяч.

Як правило, для запуску валідатора потрібне спеціальне обладнання з постійним доступом до Інтернету, а також значна сума нативних монет мережі. Наприклад, валідатор у мережі Ethereum повинен мати щонайменше 32 ETH, а валідатор Tezos — щонайменше 8000 XTZ.

Proof-of-Stake та стейкінг

Для компенсації витрат обчислювальних вузлів з перевірки транзакцій та генерації нових блоків у більшості PoS-блокчейнів передбачено винагороду, яку виплачують у нативних монетах цієї мережі. Як правило, його розмір за кожний блок фіксовано, але може змінюватись в залежності від поточних параметрів мережі.

Наприклад, у блокчейн-платформі Tron суперпредставник (так в даному випадку називається валідатор), який згенерував черговий блок і обробив транзакції, отримує 32 ТRX. Частиною цієї суми він ділиться з користувачами, які поклали свої TRX у стейкінг та таким чином проголосували за нього.

Прибутковість стейкінгу для валідаторів та власників монет визначається двома факторами:

  • швидкістю емісії, що визначається фіксованим значенням монет, що видаються на кожен новий блок;
  • часткою монет у обігу, які заблоковані в стейкінгу (Staking Ratio);

Наприклад, якщо в рік через стейкінг випускають 1 млн. монет при загальній пропозиції 100 млн. монет, то прибутковість стейкінгу при 50% заблокованих монет складе 2% річних. Якщо ж у стейкінгу заблоковано 25% пропозиції, то доходність збільшується вдвічі, до 4% річних.

Які різновиди Proof-of-Stake існують

На принципах PoS та делегування розроблено безліч механізмів консенсусу, які відрізняються низкою нюансів, наприклад, розподілом ролей між учасниками децентралізованої мережі.

Наведемо деякі з них:

  • Leased Proof-of-Stake (LPoS , «орендований доказ частки»)  використовується в блокчейні Waves, де користувачі за винагороду здають свої монети в оренду валідатору;
  • Nominated Proof-of-Stake (NPoS , «номінований доказ частки»)  використовується в блокчейн-платформі Polkadot і передбачає наявність так званих номінаторів, які вносять застави за валідаторів та відповідають за їхню сумлінність;
  • Pure Proof-of-Stake (PPoS, “чистий доказ частки”) – застосовується в мережі Algorand, де валідатори наступного блоку таємно і випадково вибираються серед усіх гаманців з балансом більше 1 ALGO;
  • Effective Proof-of-Stake (EPoS, “ефективний доказ частки”)  застосовується в блокчейн-платформі Harmony. Має особливий механізм розподілу винагород, що заохочує запуск безлічі дрібних валідаторів замість малої кількості великих, що стимулює децентралізацію;
  • Proof-of-Authority (PoA)  гібридний алгоритм, який поєднує доказ частки та репутацію валідаторів, кожен із яких має бути схвалений розробниками. В PoA валідатор повинен проходити процедуру верифікації особи, схожу на KYC. Цей алгоритм використовує BNB Chain.

Чи можливий перехід на Proof-of-Stake біткоїну та інших криптовалютів?

Висока енерговитратність майнінгу криптовалют, які працюють на алгоритмі PoW, уже багато років є предметом критики. Згідно з недавніми дослідженнями Кембриджського центру альтернативних фінансів, майнінг біткоїну відповідальний за викид 0,1% всього антропогенного вуглекислого газу.

Саме цей чинник став одним із головних аргументів при спробах заборони майнінгу у різних країнах. Так, до кінця 2021 майнінг криптовалют заборонили в Китаї. У березні 2022 року Європарламент виніс на голосування питання щодо заборони криптовалюту. Хоча законопроект не підтримали, він наголосив на тенденції до видавлювання PoW з легального поля.

Після успішного переходу мережі Ethereum на консенсус Proof-of-Stake 15 вересня 2022 енергоспоживання мережі знизилося майже в 2000 разів або на 99,95%. У зв’язку з цим із новою силою розгорнулося обговорення переходу популярних PoW-криптовалютів на PoS.

Ще в грудні 2021 року розробники мем-криптовалюти Dogecoin оголосили про швидкий її перехід на алгоритм Proof-of-StakeДопомагати їм у цьому процесі вирішив Віталік Бутерін, співзасновник Ethereum.

Компанія Electric Coin Company, розробник анонімної криптовалюти Zcash, також обговорює із співтовариством перспективи переходу на PoS. За словами засновника компанії Зуко Вілкокса, це не лише підвищить безпеку та енергоефективність блокчейну, а й допоможе залучити власників ZEС до управління протоколом.

Найбільші сумніви викликає можливість переходу на PoS у разі біткоїну.

По-перше, у першої криптовалюти немає єдиного розробника. Декілька незалежних груп розробників ведуть обговорення всіх передбачуваних нововведень, отже навіть запровадження навіть найнезначніших їх викликає запеклі суперечки і займає роки.

По-друге, перехід на PoS не підтримають майнінгові пули, яким цей крок загрожує втратою доходів. Примітно, що ще в 2020 році група розробників запустила форк BitcoinPoS , який криптоспільство просто проігнорувало.

У свою чергу прихильники PoW вказують на більший рівень безпеки цього алгоритму: при поточному, вкрай високому рівні децентралізації мережі біткоїну вона практично невразлива до зовнішніх атак.

Джерело

П’ять шаблонів завантаження даних для підвищення швидкодії сайтів

підвищення швидкодії сайтів

Фреймворки вам потрібні не для всього, але у разі їх використання є низка способів, що дозволяють досягти максимальної швидкодії. У цій статті йдеться про п’ять шаблонів завантаження даних, які при доречному застосуванні дозволять значно підвищити продуктивність додатків або сайтів.

Коли справа доходить до швидкодії, обмежувати себе не варто. У мережі існують мільйони сайтів, і ви перебуваєте у тісному суперництві з усіма учасниками пошукової видачі Google. Дослідження показало, що користувачі схильні залишати сайти, що завантажуються довше трьох секунд. Три секунди – це небагато. І хоча сьогодні багато ресурсів завантажуються менше, ніж за секунду, жодне рішення не універсальне, і перший запит користувача може стати для вашої програми визначальним.

Сучасні фронтенд-додатки стають дедалі більше. Не дивно, що промисловість серйозно спантеличилася питанням оптимізації. При використанні фреймворків ми отримуємо неадекватні розміри програми, які можуть піти йому не на користь, а на шкоду. Будь-який необов’язковий фрагмент JavaScript, який ви збираєте та відправляєте у продакшен, у результаті лише збільшить кількість коду, який клієнту потрібно завантажити та обробити. Тут чергове правило каже — що менше, то краще.

Шаблони завантаження даних є найважливішою частиною вашої програми, оскільки визначають, які його частини є безпосередньо доступними для відвідувачів. Не створюйте сайт, який гальмуватиме у клієнтів через необхідність завантажити зображення розміром 5МБ на головній сторінці. Намагайтеся краще зрозуміти принципи. Вам потрібно добре розібратися у водоспаді завантаження ресурсів.

Перебір зі спіннерами завантаження та водоспад даних

Водоспад завантаження даних – це каскад файлів, які завантажуються з мережного сервера клієнтом для завантаження вашого сайту від початку до кінця. Він по суті описує життєвий цикл кожного файлу, який ви завантажуєте з мережі для завантаження вашої сторінки.

Наочно подивитися це можна, відкривши браузер і зазирнувши у вкладку Network інструментів розробника на прикладі Medium.

Домашня сторінка Medium

Тут у нас два важливі компоненти:

  1. Графік показує тимчасову лінію кожного запрошеного і завантаженого файла. Тут ви бачите, які файли йдуть першими, і можете простежити кожен послідовний запит до моменту, коли завантаження файлу займає тривалий час. Далі цей файл можна проінспектувати та з’ясувати, чи є можливість його оптимізувати.
  2. Внизу сторінки можна перевірити, скільки КБ ресурсів споживає ваш клієнт. Важливо, скільки даних клієнту необхідно завантажувати. При першій спробі цю інформацію можна використовувати як бенчмарк для подальших оптимізації.

Ніхто не любить спостерігати порожній білий екран, особливо ваші користувачі. При затримці завантаження ресурсів необхідний тимчасовий плейсхолдер, який бачитиме користувач до моменту, коли ви зможете почати створювати макет на стороні клієнта. Як правило, в якості його можна використовувати спіннер або скелетний завантажувач. У процесі поступового завантаження даних сторінка демонструватиме завантажувач, доки всі компоненти не будуть готові для відображення.

І хоча додавання таких плейсхолдерів є покращенням, зайва тривалість їхнього показу також може зашкодити. Справа в тому, що якщо ваша програма надмірно затримується на стадії завантаження, то нехай це і краще порожньої HTML-сторінки, така ситуація все одно дратує, схиляючи відвідувачів залишити сайт.

Але хіба очікування даних не є зрозумілим?

Так, але їхнє завантаження можна прискорити.

Loading

Припустимо, що ви хочете завантажити макет соціальної мережі. У цьому випадку ви можете додати спіннер або скелетний завантажувач, щоб унеможливити відображення непромальованого сайту. Скелетний завантажувач зазвичай чекатиме наступного:

  • даних від API бекенда;
  • створення макета згідно з цими даними.

Спочатку ви здійснюєте асинхронний виклик до API, потім отримуєте URL-адресу ресурсу в CDN і тільки після цього можете почати створювати макет на клієнтській стороні. Це чимало роботи, коли потрібно показати ваше обличчя, ім’я, статус та пости Instagram відразу.

П’ять важливих шаблонів завантаження даних

Розробляти програмне забезпечення стає все простіше, оскільки фреймворки на кшталт React, Vue або Angular дозволяють з легкістю створювати найпростіші програми. Однак використання цих громіздких інструментів, заповнених купою всіляких функцій, які ви навіть не використовуєте, не варто розглядати як оптимальне рішення.

Ви тут щоб оптимізувати. Пам’ятайте, що менше, тим краще.

Але якщо можливості зменшити обсяг даних у вас немає? Як забезпечити високу швидкість виконання коду? Що ж, тут вам допоможуть п’ять корисних шаблонів завантаження даних, які дозволять прискорити роботу сайту.

Рендеринг на стороні сервера та Jamstack

У сучасних JS-фреймворках відображення сторінок найчастіше реалізується на клієнтській стороні (CSR). Браузер отримує у вигляді корисного навантаження JS-бандл та статичний HTML, після чого малює DOM, а також для реактивності додає слухачів та активатори подій. Коли така програма відображається в DOM, сторінка блокується до завершення цього процесу. Рендеринг робить програму реактивною. Для його виконання необхідно зробити ще один виклик API до сервера та вийняти всі необхідні дані.

У свою чергу, малювання на стороні сервера (SSR) передбачає передачу програмою простого HTML клієнту. SSR можна розділити на два типи: з гідратацією та без гідратації. SSR – це стара техніка, яка використовується такими фреймворками, як WordPress, Ruby on Rails та ASP.NET. Основне її завдання – надати користувачеві статичний HTML із необхідними даними. На відміну від CSR, SSR не вимагає додаткового виклику API до сервера, оскільки саме сервер генерує шаблон HTML і завантажує дані.

У більш сучасних рішеннях на зразок Next.js використовується гідратація, коли статичний HTML гідратується на клієнтській стороні за допомогою JS. Можна порівняти це із заварюванням швидкорозчинної кави: кавовий порошок – це HTML, а вода – це JS. При змішуванні порошку з водою ви, очевидно, отримаєте каву.

Але що таке Jamstack? Jamstack аналогічний SSR, оскільки клієнт отримує простий HTML. Але під час SSR клієнт отримує HTML з сервера, а у випадку з Jamstack програми передають заздалегідь згенерований HTML прямо з CDN. У зв’язку з цим вони зазвичай завантажуються швидше, але тут розробникам складніше створювати динамічний контент. Такі програми рахунок попередньої генерації HTML хороші клієнта. Однак при використанні великого обсягу JS-коду на стороні клієнта стає все складніше виправдати використання Jamstack замість СSR.

І SSR, і Jamstack мають особливості. Спільне з-поміж них те, що вони не навантажують клієнта рендерингом всієї сторінки, використовуючи JS.

Jamstack у порівнянні з SSR та CSR

Коли ви оптимізуєте SEO свого сайту, рекомендується використовувати SSR або Jamstack, оскільки порівняно з CSR ці техніки повертають HTML-файли, зручні для перегляду пошуковими роботами. Пошуковики, звичайно, також можуть переглядати та компілювати JS-файли для CSR. Хоча відображення кожного JS-файлу в додатку з CSR може бути витратним за часом і знижувати ефективність SEO вашого сайту.

SSR і Jamstack дуже популярні, і все більше проектів переходять на кадри для SSR на кшталт Next.js і Nuxt.js, відмовляючись від їх ванільних CSR-аналогів – React і Vue. Головна причина в тому, що SSR-фреймворки забезпечують більшу гнучкість щодо SEO. У Next.js є цілий розділ, орієнтований на SEO оптимізації.

Додаток з SSR зазвичай містить шаблонизатори, які впроваджують змінні HTML під час його передачі клієнту. Наприклад, у Next.js можна завантажити список студентів, написавши:

export default function Home({ studentList }) {
  return (
    <Layout home>
        <ul>
          {studentList.map(({ id, name, age }) => (
            <li key={id}>
              {name}
              <br />
              {age}
            </li>
          ))}
        </ul>
    </Layout>
  );
}

Jamstack популярний для сайтів документації, які зазвичай компілюють код HTML-файли і розміщують їх в CDN. Файли Jamstack зазвичай до компіляції в HTML використовують розмітку Markdown. Ось приклад:

---
author: Agustinus Theodorus
title: 'Title'
description: Description
---
Hello World

Активне кешування пам’яті

Коли вам потрібна можливість швидко отримувати дані, з якими ви вже працювали, зверніться до кешування. Ця техніка передбачає збереження нещодавно використаної інформації для швидкого завантаження. Реалізувати кешування можна двома шляхами – за допомогою супершвидкого сховища ключ-значення на зразок Redis для збереження ключів даних та значень або за допомогою простого кеша браузера, в якому дані зберігатимуться локально.

При кешуванні дані зберігаються лише певний час, так що як постійне сховище кеш не підходить. Ця техніка дуже придатна в продакшен-середовищі і стає актуальною в міру дорослішання програми.

Але як робити правильний вибір між кешем Redis (серверним) та кешем браузера (локальним)? Обидва ці варіанти можна використовувати одночасно, але кожен із них буде слугувати своїй меті.

Схеми кешування

Серверний кеш допомагає скоротити затримку між фронтендом та бекендом. Оскільки бази даних у форматі ключ-значення працюють швидше за традиційні реляційні БД SQL, це значно зменшує час відповіді API. При цьому локальний кеш дозволяє програмі зберігати стан після оновлення сторінки, що прискорює відображення при подальших поверненнях до неї.

Загалом, якщо ви хочете підвищити швидкодію програми, то можете використовувати для прискорення API серверний кеш. Якщо ж ви хочете зберігати стан програми, варто вдаватися до локального. І хоча локальний кеш може здатися марним, він допомагає скоротити кількість викликів API до бекенду, зберігаючи стан, який рідко змінюється. Однак локальний кеш працює ефективніше при поєднанні з «живими» даними.

Генерація подій даних

Встановити з’єднання в режимі реального часу між фронтендом та бекендом можна через WebSockets. WebSockets – це механізм двосторонньої комунікації, що спирається на події.

Типова архітектура Websocket

У стандартній архітектурі WebSocket фронтенд-додаток підключається до WebSocket API, шини подій або бази даних. У більшості архітектур WebSocket це використовується як альтернатива REST, особливо в додатках на зразок чатів, коли опитування бекенда кожні кілька секунд стає неефективним. Ця техніка дозволяє отримувати по двосторонньому каналу оновлення з іншого боку без створення нового запиту.

У порівнянні з HTTP-запитами WebSocket створює дуже компактне підключення, що перевіряється на активність. Поєднання WebSocket з локальним кешем браузера дозволяє створити програму, що працює в реальному часі. Стан цієї програми можна оновлювати на основі подій, що одержуються за WebSocket. Однак тут все ж таки є ряд каверз, пов’язаних з швидкодією, масштабованістю і потенційними конфліктами даних.

Архітектура генерації подій

У чистій реалізації WebSocket, як і раніше, є безліч недоліків. Використання цієї технології замість стандартних HTTP-дзвінків повністю змінить поведінку програми. Навіть одна невелика проблема з підключенням зможе вплинути на весь досвід користувача. Наприклад, WebSocket неспроможна забезпечити швидкодію реального часу – коли потрібно зв’язатися з базою даних, використовується запит GET. Щоб зробити WebSocket раціональним та доцільним вибором у бекенді необхідно усунути ряд слабких місць, що перешкоджають ефективній роботі в режимі реального часу.

Для реалізації таких можливостей потрібний основний архітектурний шаблон, наприклад, «генерація подій» який можна використовувати для створення надійних real-time додатків. І хоча він не гарантує загальне підвищення швидкодії програми, цей шаблон явно покращить досвід користувача, надавши клієнтам можливість використовувати UI в режимі реального часу.

У сучасному JS доступні для використання провайдери WebSocket. Клас WebSocket відкриває з’єднання з віддаленим сервером і дозволяє прослуховувати, коли WebSocket створює з’єднання, закриває його, повертає помилку або повертає подію.

const ws = new WebSocket('ws://localhost');ws.addEventListener('message', (event) => {
    console.log('Message from server ', event.data);
});

Бажаєте реагувати на події сервера? Додайте функцію addEventListener та вставте зворотний дзвінок, який вона використовуватиме.

ws.send('Hello World');

Бажаєте надсилати повідомлення? І тут WebSocket має рішення. Надсилання повідомлень із сервера можна реалізувати за допомогою функції send. Причому це не складніше реалізації Hello World. Приклади взято з документації MDN .

Попереднє та відкладене завантаження даних

Попереднє і відкладене завантаження вже стали звичайними прийомами серед розробників фронтенду. Ефективне використання клієнтських ресурсів та пропускної спроможності може значно покращити швидкодію програми.

Попереднє завантаження

Ця техніка дає розробникам детальніший контроль над вільною пропускною смугою клієнта, завантаженням ресурсів та сторінками, які клієнту можуть знадобитися наступними. Коли сайт має посилання для попереднього завантаження, браузер мовчки завантажує її вміст, зберігаючи його в кеші. Коли користувач згодом переходить за такими посиланнями, вони завантажуються значно швидше.

<link rel="prefetch" href="https://example.com/example.html">

URL для попереднього завантаження встановлюються в атрибуті relHTML-елемента link. Однак дана техніка має як плюси, так і мінус.

Плюси:

  1. Операція попереднього завантаження чекає, поки мережа браузера не виявиться вільною і зупиняється, як тільки ви запустите якийсь мережевий процес, натиснувши посилання або активувавши функцію відкладеного завантаження.
  2. Попереднє завантаження кешує дані у браузері, прискорюючи переходи при перенаправленні за посиланням.

Мінус:

  1. Цей прийом можна використовувати для завантаження трекерів, що ставить безпеку користувача під загрозу.

Відкладене завантаження

Відкладене завантаження – це стандартний шаблон завантаження даних, що дозволяє клієнту завантажувати результати на вимогу, а не всі разом. При використанні цього методу клієнт отримує частини сайту поступово у процесі прокручування його подання.

Відкладене завантаження

Відкладене завантаження прискорює роботу сайту за рахунок того, що браузер може сконцентруватися на важливіших для показу ресурсах. Вам не потрібно завантажувати всі зображення/текст сайту, якщо ви їх не бачите. Однак відкладене завантаження допомагає лише відкласти скачування ресурсів, не роблячи їх менше і не скорочуючи витрати на цю процедуру.

Якщо вас цікавить аналогічне рішення, але з ухилом у бік зниження обчислювальних витрат, рекомендую звернути увагу на принцип Resumability.

Resumability

Багато розробників про принцип Resumability ніколи не чули. При використанні цієї техніки JS-код частково малюється на сервері, після чого кінцевий стан малювання серіалізується і відправляється клієнту з відповідним корисним HTML-навантаженням. Потім клієнт завершує розпочату відмальовку, витрачаючи на це вже суттєво менше часу та ресурсів. По суті, таким чином ми використовуємо сервер для виконання всієї основної роботи, після чого за допомогою серіалізації передаємо клієнту лише малу частину JS-коду для виконання.

Головна ідея Resumability полягає у передачі стану програми від сервера клієнту у вигляді серіалізації. Замість завантаження всього коду (HTML, JS) з подальшим його гідратуванням у фронтенді при використанні Resumability ми розбиваємо парсингу JS-коду на стадії та відправляємо їх клієнту у вигляді HTML.

Порівняння Resumability та гідатації. Зображення взято з Qwik

Завантаження сторінок буде блискавичним, оскільки клієнт замість перезавантаження чогось десеріалізуватиме стан, впроваджений в HTML. Resumability є досить чужорідною концепцією і багатьох проектів здасться зовсім незвичним прийомом. Придумав цей шаблон творець Qwik, Міско Хевері.

Qwik– це JS-фреймворк, що внутрішньо працює за принципом Resumability, який у нього закладався спочатку. Якщо ж говорити про такі фреймворки, як React і Vue,  то вони ніколи не зможуть задіяти техніку Resumability, не пожертвувавши зворотною сумісністю. Причина в тому, що компонент відкладеного завантаження Qwik працює асинхронно на відміну від більшості синхронно влаштованих фреймворків JavaScript.

Завдання Qwik – завантажувати якнайменше JS-коду. Справа в тому, що робити це відкладено досить важко, а в деяких випадках навіть неможливо. Чим менше вам потрібно, тим краще. Resumability дозволяє розробникам детально налаштувати відкладене завантаження та скоротити споживання пам’яті мобільними програмами, оптимізуючи під них сайт.

Використання Qwik у певному сенсі нагадує React – зокрема вони схожий синтаксис. Ось фрагмент коду Qwik. Основа програми буде представлена ​​у формі HTML:

import { App } from './app';export const Root = () => {
  return (
    <html>
      <head>
        <title>Hello Qwik</title>
      </head>
      <body>
        <App />
      </body>
    </html>
  );
};

Тут є залежність від App компонента Qwik для відкладеного завантаження:

import { component$ } from '@builder.io/qwik';
export const App = component$(() => {
  return <p>Hello Qwik</p>;
});

Qwik і React також мають схожість на рівні компонентів, а ось у серверній частині вони вже відрізняються.

import { renderToString, RenderOptions } from '@builder.io/qwik/server';
import { Root } from './root';export default function (opts: RenderOptions) {
  return renderToString(<Root />, opts);
}

Фрагмент вище показує, як серверна частина Qwik серіалізує основний компонент, використовуючи метод renderToString. Після цього клієнту потрібно лише спарсити чистий HTML і десеріалізувати стан JS без необхідності їх перезавантажувати.

Узагальнення

Швидкодія програми для клієнта дуже важлива. Чим більше ресурсів вам доводиться завантажувати, тим більше часу знадобиться програмі під час запуску. При цьому до процесу очікування завершення завантаження користувачі стають менш терпимими. Так що чим менше часу вантажиться ваш сайт, тим краще.

Однак, якщо ви працюєте над великими корпоративними програмами, то тут спосіб оптимізації вже знайти складніше. Шаблони завантаження даних є одним з можливих варіантів підвищення швидкодії ваших додатків. У цій статті ми розібрали їх п’ять:

  1. Відображення на стороні сервера (SSR) та Jamstack.
  2. Активне кешування пам’яті.
  3. Генерація подій даних.
  4. Попереднє та відкладене завантаження.
  5. Resumability.

Кожен із них ефективний на своєму місці.

SSR та Jamstack зазвичай добре підходять для додатків, що вимагають меншого керування станом на стороні клієнта. З появою сучасних фреймворків на зразок React все більше людей познайомилися з патерном відмальовки на клієнтській стороні (CSR), але в результаті спільнота все ж таки повертається до SSR. Техніка SSR використовується у старих MVC фреймворках, де за допомогою шаблонізаторів на основі даних бекенда генерується HTML. Jamstack – це ще більш стара ілюстрація початкового веб-середовища, в якому використовувався лише HTML.

Активне кешування пам’яті дозволяє швидше завантажувати дані з API. Ця техніка вирішує важливу проблему завантаження даних шляхом їх кешування або на віддаленому сервері (Redis) або локально в браузері. Крім цього, вона використовується в шаблоні попереднього завантаження даних.

Що стосується генерації подій, цей архітектурний шаблон доповнює засновані на подіях WebSocket API, що працюють в реальному часі. Простих старих WebSocket недостатньо для повноцінної ефективності, оскільки хоч сам WebSocket і працює в реальному часі, регулярні виклики API до бази даних створюють вузьке місце. Шаблон генерації подій усуває цю проблему, створюючи для завантаження даних окрему базу даних.

Попереднє та відкладене завантаження представляють найпростіші в реалізації рішення. Мета першого – тихо завантажувати дані, коли мережа перебуває у вільному стані. При цьому клієнти зберігають завантажені посилання в кеші браузерів, використовуючи їх у готовому вигляді при безпосередньому зверненні.

Відкладене завантаження скорочує обсяг ресурсів, необхідних для завантаження при першому натисканні. Вам потрібні лише ті, які ви бачите безпосередньо після завантаження сторінки. І цю техніку додатково розширює шаблон Resumability. Він має на увазі відкладене завантаження JS-компонентів шляхом їх часткового відтворення на сервері з подальшою серіалізацією стану для завершення розпочатого відтворення вже на стороні клієнта за допомогою HTML.

Подальші кроки

Вироблення навичок оптимізації додатків є безперервним процесом. Вам потрібно продумувати все наперед у проектах, які реалізуєте день у день. Шаблони завантаження даних – це лише один із способів для підвищення швидкодії.

Однак, перш ніж втручатися в процес структуризації та споживання даних додатком, краще спершу ознайомитися з усіма можливими підводними каменями.

Переклад статті “Five Data-Loading Patterns To Improve Web Performance

Що таке криптовалюта простими словами?

криптовалюта

  • Криптовалюти – це віртуальні активи, функціонування яких забезпечується мережею децентралізованих комп’ютерних нод (вузлів). Більшість із них побудовано на основі блокчейну — бази даних про транзакції в ланцюжку зв’язаних блоків.
  • Криптовалюти переважно використовуються для розрахунків між користувачами мережі, оплати комісій за перекази, а також для зберігання капіталу. Однак вони можуть мати безліч інших функцій та обмежень. У деяких блокчейнах можна випускати необмежену кількість криптовалют.
  • Залежно від функцій та сфери застосування розрізняють різні типи криптовалют, у тому числі стейблкоіни, NFT , токени управління та обернені активи.
  • Блокчейн дозволяє безпосередньо та без обмежень взаємодіяти з криптовалютами. Децентралізація та відсутність контролю будь-якої країни чи організації усуває можливість блокування коштів та інші ризики.

Коли з’явилися криптовалюти

Пропозиції щодо створення криптовалюти висловлювали ще до появи інтернету. Перші алгоритми, створені задля передачу цінності через інтернет, запропонував американський криптограф Девід Чаум. Наприкінці 80-х років він заснував відповідний проект під назвою DigiCash. Однак той закінчився невдачею.

Одним із проектів, близьким до сучасних криптовалютів, є b-money . Проект започаткував програміст Вей Дай. У b-money він уперше запропонував механізм консенсусу Proof-of-Work (PoW) для фіналізації транзакцій та розподілену базу даних для зберігання інформації про транзакції.

Біткоїн, створений анонімним користувачем під ніком Сатоші Накамото, став першою успішною криптовалютою, яка набула широкого поширення. Алгоритм роботи однойменного блокчейна багато в чому базується на ідеях Вей Дая і, у ширшому значенні, руху шифропанків.

Наступним кроком у розвитку віртуальних активів стала платформа смарт-контрактів Ethereum, яку заснував Віталік Бутерін. Запуск основної мережі відбувся у 2015 році. Завдяки смарт-контрактам блокчейн Ethereum надав можливість випускати необмежену кількість криптоактивів та програмувати їх функції.

Криптовалюти та блокчейн: відмінності від традиційних фінансів та грошей

Надійність блокчейну та закладені в ньому механізми захисту даних зробили криптовалюти гідною альтернативою традиційній грошовій системі. Перекази цифрових активів можна здійснювати безпосередньо між користувачами, а за їх перевірку та підтвердження відповідає протокол мережі, наданий безліччю незалежних вузлів. Після надсилання та верифікації транзакції її неможливо скасувати.

Інформація про переклади зберігається у відкритій базі даних. Ключовою технологією, що дозволяє взаємодіяти з віртуальними активами, є блокчейн. Криптографічні методи, що використовуються в ньому, виключають можливість зміни даних про виконані транзакції.

Завдяки децентралізації у блокчейні вдалося вирішити проблему єдиної точки відмови у базах даних та платіжних системах, забезпечивши універсальний захист від цензури. Віртуальні активи в публічних блокчейнах не схильні до впливу контролюючих органів та зовнішніх маніпуляцій. У третьої сторони немає можливості заблокувати кошти або скасувати транзакцію.

Криптовалюти можуть не бути під контролем будь-якої однієї організації, що дозволяє використовувати їх у будь-якій юрисдикції. Біткоїн – найбільш відомий приклад такого активу. Нові віртуальні активи випускають у процесі роботи блокчейну. В алгоритмі роботи конкретного цифрового активу закладено такі параметри як загальна емісія та швидкість випуску нових монет.

Користувач може створити необмежену кількість адрес. Останні зазвичай не дозволяють отримати інформацію про нього. Тільки спеціалізовані інструменти блокчейн-аналізу дають змогу відстежити рух коштів та визначити джерела їх надходження.

Як працює криптовалюта : ключові особливості

Блокчейн

Більшість криптовалют використовують технологію під назвою блокчейн. Останній є послідовністю ієрархічно пов’язаних блоків. Він містить базу даних про всі проведені транзакції. Нові транзакції записуються в блоки, які створюють оператори нод. Кожен із комп’ютерних вузлів зберігає свою копію блокчейна і під час досягнення консенсусу підтверджує нові блоки, передаючи результат іншим нодам. За підсумками цієї інформації змінюються баланси користувачів.

Хешування

У криптовалютах широко застосовуються хеш-функції. Остання дозволяє “згорнути” довільний масив даних в один рядок – хеш. Блокчейн є єдиним цілим, оскільки кожен наступний блок включає хеш попереднього. Використання інших даних призводить до суттєвої зміни цього рядка. Алгоритм унеможливлює скасування транзакції без зміни всіх наступних блоків.

Алгоритм консенсусу

За підтвердження транзакцій та перевірку всіх даних блокчейна відповідає алгоритм консенсусу, в якому беруть участь члени мережі – ноди. Найбільш популярними алгоритмами консенсусу є вже згаданий Proof-of-Work (використовується в біткоїні) та Proof-of-Stake .

Щоб підтвердити транзакції, вузли витрачають обчислювальну потужність або блокують власні кошти, які є гарантією достовірності даних. Після додавання нового блоку неможливо повторно витратити кошти, витрачені на поточному етапі. У процесі досягнення алгоритму його учасники отримують нові криптовалюти (як у PoW , так і в PoS ). Таким чином, ці механізми є способом емісії нової криптовалюти.

Адреси

Блокчейн-адреси, де зберігаються криптовалюти, побудовані з урахуванням двох ключів — громадського і приватного. Перший використовується для «відкритої» частини адреси, другий — для підпису транзакцій та доступу до адреси. Закритий ключ призначено лише для власника адреси. Існуючі комп’ютерні потужності не дозволяють зламати блокчейн-адресу, зокрема «відгадати» приватний ключ методом підбору. Створювати блокчейн-адреси та керувати ними можна у спеціальному додатку – гаманць .

Транзакції

Щоб перекласти криптовалюту, користувач надсилає до мережі «доручення», що містить інформацію про суму переказу та адресата. Дані перекладу автоматично верифікуються електронним підписом за допомогою приватного ключа. Після потрапляння повідомлення до мережі воно включається до одного з наступних блоків, який «добувають» ноди блокчейна. Будь-яка нода може перевірити підпис кожного перекладу, використовуючи публічний ключ. Такий алгоритм гарантує збереження коштів та неможливість здійснення транзакцій третіми особами.

Незмінність даних

Після додавання блоку в ланцюжок усі його транзакції вважаються підтвердженими та незворотними. Верифікація всіх транзакцій, зокрема минулих, відбувається кожен цикл досягнення консенсусу мережі. Недостовірні дані можуть потрапити в блокчейн, тільки якщо зловмисник зможе розшифровувати хеш блоків або контролюватиме суттєву частку всіх нод мережі. Наприклад, у разі біткоїну цей поріг становить 51%. За даними на 13 жовтня 2022 року, у всьому світі працює понад 15 000 нод , що робить атаку на біткоїн практично неможливою.

Класифікація криптовалют: монети та токени

Для створення та здійснення операцій з криптовалютою необхідний блокчейн, який потребує наявності інфраструктури та економічних стимулів.

Головний віртуальний криптоактив, який забезпечує функціонування ланцюжка блоків, називається нативним. Останній застосовується для оплати трансакційних комісій та виплат винагород вузлам. На платформах, які дозволяють запускати смарт-контракти, криптовалюта нативна використовується для оплати їх виконання. У біткоїну та Ethereum нативна монета носить ту саму назву, що і проект, і торгується під тикерами BTC та ETH, відповідно.

Різні криптоактиви можуть з’явитися в результаті хардфорку. До них відносяться, наприклад, Bitcoin Cash та Ethereum Classic.

Сьогодні криптовалюте вже не обов’язково потрібен окремий блокчейн: багато криптоактивів створюють для використання в різних додатках, що працюють в одній мережі. Такі криптовалюти називають токенами. Портал CryptoSlate відслідковує котирування близько 1300 токенів, випущених у мережі Ethereum.

Випуск токенів став можливим завдяки смарт-контрактам, які стали популярними після запуску Ethereum. Створити власну криптовалюту може будь-який користувач, який має навички програмування. Найбільш відомим стандартом токенів є ERC-20.

Які бувають токени

Віртуальні активи, які використовуються для розрахунків та інших цілей у проектах та децентралізованих додатках називаються ютіліті-токенами.

Найчастіше токени застосовуються для управління проектом. Спільнота власників активу може приймати рішення щодо подальших напрямів розвитку проекту. Власники мають можливість виставляти пропозиції на голосування та брати участь у опитуваннях. Вага голосу при цьому пропорційна числу монет.

Монети можуть надавати певні права чи привілеї. Токени використовують для залучення коштів при первинній пропозиції монет (ICO). У деяких проектах віртуальні активи надають право на частину прибутку проекту. Токени, що мають набір функцій у рамках програми або мережі, також називають ютіліті-токен (utility tokens).

Криптовалюти, офіційно визнані цінними паперами, називають сек’юріті-токенами. На жовтень 2022 року є принаймні кілька десятків таких криптоактивів.

На базі блокчейнів реалізовані стейблкоіни. Вартість цих токенів прив’язана до ціни певного активу, наприклад, долара США. Найчастіше вони застосовуються для розрахунків та накопичення заощаджень. Їхні емітенти зберігають у резервах відповідну кількість валюти, що гарантує стабільність ціни та збереження коштів.

Платформи смарт-контрактів дозволяють випускати обгорнуті токени. Останні прив’язані за вартістю криптовалюти з іншого блокчейна. Наприклад, для того щоб використовувати біткоїн у мережі Ethereum випущений токен WBTC. Прив’язка реалізується завдяки тому, що випуск токена вимагає блокування відповідного обсягу криптовалюти, що виступає в якості забезпечення. Для розблокування віртуальних активів обгорнуті токени спалюються.

У сучасних мережах випускаються невзаємозамінні токени (NFT). Це унікальні монети, які представляють певні цифрові об’єкти (зображення, аудіо та відеофайли). NFT стали об’єктами колекціонування. Перші незамінні токени є раритетами і мають високу вартість (наприклад, CryptoPunks). Також NFT можуть виконувати інші функції, наприклад використовуватися як аватари або квитки на певні заходи.

Існують криптовалюти, які є похідними фінансовими інструментами. На основі таких токенів, зокрема, збудовано ліквідний стейкінг. На відміну від стейблкоїнів, їх ціна регулюється ринковими механізмами. Деякі торгові платформи випускають токени з кредитним плечем.

Як формується цінність криптовалюти

При створенні блокчейну розробники ухвалюють рішення про те, якою буде токеноміка проекту. Остання визначає алгоритм випуску та розподіл монет між користувачами. Емісія – це кількість монет, що перебуває в обігу. У деяких проектах початковий випуск дорівнює нулю, наприклад, у біткоїні. У таких випадках криптовалюта може випускатися постійно або періодично. Ряд проектів мають постійну емісію.

Розподіл початкового випуску монет між користувачами визначається розробником. Зазвичай частина активів залишається командою проекту. Кошти резервуються на рекламу розробку та розвиток проекту. Частина монет використовується для залучення інвестицій. Їх можуть купити великі інвестори в рамках одного або кількох раундів. Багато проектів використовують публічний токенсейл для продажу віртуальних активів усім охочим. В інших випадках практикують розподіл монет між активними користувачами – аірдроп.

Алгоритми роботи криптовалют можуть вимагати блокування частини монет (наприклад, при стейкінгу). Оскільки частина активів внесена до смарт-контрактів, певний обсяг коштів недоступний. Як правило, випуск криптовалюти обмежений максимальною емісією. Для проектів, які постійно генерують монети, загальна їхня кількість ніколи не перевищить цю величину. Наприклад, максимальна пропозиція BTC становить 21 млн.

На вартість криптовалюти впливає халвінг. Механізм використовується у криптовалютах, які виплачують фіксовану винагороду за блок. Халвінг відбувається періодично та призводить до зниження цієї суми. Подія зменшує інфляцію, а за наявності попиту актив стимулює зростання його вартості.

На ціну криптовалюти впливає спалювання монет. Останнє передбачає виведення з обігу певного обсягу активів. Щоб знищити монети, власники надсилають їх на спеціальні адреси чи смарт-контракти. При цьому можливе стрибкоподібне зростання вартості криптовалюти, а періодичні спалювання підвищують її дефіцитність.

Криптовалюти, на відміну від фіатних грошей, не контролюються певною організацією або державою. Тому для віртуальних активів характерна висока волатильність. Їхня вартість визначається переважно ринковими механізмами. Цінність криптовалюти залежить від співвідношення між попитом та пропозицією. Якщо на ринку продається значний обсяг монет, а охочих купити мало, відбувається падіння вартості. Коли попит перевищує пропозицію, ціна зростає.

Основним показником затребуваності віртуального активу є ринкова капіталізація. Остання дорівнює добутку обсягу монет, що у зверненні вартість одиниці.

Джерело

Що таке ChatGPT чи хто такий ChatGPT?

ChatGPT

30 листопада лабораторія штучного інтелекту OpenAI представила свій чат-бот ChatGPT, що базується на нейромережевій мовній моделі GPT-3.5. Про розробку майже відразу почали писати як про саму «людську», «навчену» та «достовірну». Оптимісти передбачають, що ChatGPT замінить людину в написанні текстів та коду, песимісти жахаються, що вона замінить людину в написанні текстів та коду. І багато хто бачать у розвитку моделі ChatGPT пряму загрозу Google.

Хто такий ChatGPT?

Чат-бот, який не навчився (і не навчиться?) поганому. ChatGPT, це високорозвинений та вихований ІІ: він здатний давати вичерпні відповіді, сперечатися та відхиляти недоречні питання.

Розробники створювали ChatGPT на суперкомп’ютері Azure AI на основі мовної моделі GPT-3,5 компанії OpenAI. Чат-бот прагнули зробити простим у використанні, коректним та «людяним». Його навчали за допомогою великої кількості текстів з Інтернету та системи Reinforcement Learning from Human Feedback.

Bloomberg розвиває тему можливостей: на запит ChatGPT може видавати базовий програмний код, елементарний фінансовий аналіз, забавні вірші та пісні, імітації художнього тексту, реферати практично на будь-яку тему, резюме технічних статей або наукових концепцій, прогнози, персональні поради та відповіді практично на будь-яке питання .

Варто відразу обмовитися, що при всьому прагненні бути коректним, він не застрахований від неправильних відповідей. Оглядач Bloomberg у відповідь на запит біографії Муссоліні отримав історію його захоплення скейтбордами. Журналісти Wired були збентежені порадами «як подрібнений порцеляна, додана в грудне молоко, може підтримувати травну систему немовлят» і зазначили, що чат-бот часто мислить стереотипами, яким схильне все суспільство. Stack Overflow взагалі заборонив публікувати відповіді, згенеровані ІІ, саме через те, що вони часто створювали лише ілюзію правильних рішень.

Коротку історію OpenAI та ChatGPT описав Insider

Ілон Маск та колишній президент Y Combinator Сем Альтман заснували OpenAI у 2015 році. У 2016 році компанія запустила Gym, набір інструментів для створення систем самонавчання ІІ. У тому ж році з’явилася платформа Universe.

У 2018 році, через три роки після заснування OpenAI, Маск залишив раду директорів компанії. Згідно з офіційною версією, гендиректор Tesla подав у відставку, щоб «усунути потенційний майбутній конфлікт», який міг виникнути через те, що автовиробник також зосередив свою увагу на ІІ. Пізніше Маск сказав, що пішов із компанії через незгоду з поглядами команди OpenAI на майбутній розвиток: він вважав, що некомерційний характер компанії має зробити її більш відкритою.

2019 року компанія створила чат-бот, який міг писати достовірні фейкові новини. Спочатку в OpenAI заявили, що бот настільки досяг успіху в цьому занятті, що вони вирішили не випускати його в люди. Але пізніше компанія таки анонсувала версію інструменту ІІ під назвою GPT-2. 2020 року з’явився GPT-3.

Про своє партнерство з Microsoft OpenAI оголосила наприкінці 2019 року. Незадовго до того компанія відмовилася від некомерційного статусу. Інвестиції Microsoft становили $1 млрд, і вона разом із OpenAI ставала володаркою ексклюзивної ліцензії на всі розробки.

Торік з’явилося ще одне дітище – Dall-E – ІІ для створення зображень.

Після виходу ChatGPT Ілон Маск назвав його «жахливо добрим», впевнений, що «ми недалеко від небезпечно сильного ІІ».

Через п’ять днів після запуску у чат-бота було більше мільйона користувачів, і їхня кількість збільшується.

Що OpenAI збирається робити далі

У планах OpenAI зробити ChatGPT доступним у вигляді інтерфейсу прикладного програмування, щоб сторонні розробники інтегрували його у свої сайти або додатки без необхідності розбиратися в базовій технології.

Це означає, що якщо все піде, як заплановано, невдовзі компанії зможуть використовувати ChatGPT для створення віртуальних помічників, ботів для обслуговування клієнтів або маркетингових інструментів. Писати романи, п’єси, публіцистичні статті – поки що немає. Але ChatGPT міг би взяти на себе перегляд документів та інші важкі завдання. Він непоганий помічник, коли треба вигадувати нові незвичайні ідеї чи спрощувати процес ухвалення рішень. І, до речі, він сам міг би генерувати ідеї свого використання, бо фантазії експертів поки що виглядають скромними.

Він нас погубить: погляд скептиків

Є побоювання, що ChatGPT поставить під удар систему освіти. Якщо ІІ непогано виглядає як помічник у процесах навчання, то на етапі контролю та оцінки результатів він може нашкодити: ніколи ще студентські реферати не виглядали такими унікальними та «рукотворними». Techcrunch цитує Пола Кедроскі з Массачусетського технологічного: “Ганьба OpenAI за запуск цієї кишенькової ядерної бомби без обмежень у непідготовлене суспільство”.

Він упевнений, що це той випадок, коли технології можуть проковтнути творців. «Хтось може сказати: «Те саме відбувалося, коли на автомобільні заводи прийшла автоматизація і робітники залишилися без роботи?». Але це зовсім інше. Ці специфічні технології навчання самокаталізують; вони навчаються на запитах. Таким чином, роботи на виробничому підприємстві, хоч і руйнували і створювали неймовірні економічні наслідки для людей, які там працюють, не розгорнулися і не почали поглинати все, що відбувається всередині заводу, переміщаючись по секторах. Хоча це те, що ви повинні очікувати (ІІ).»

The Verge бачить причини бути обережними з ChatGPT у «надлишку можливостей»: моделі ІІ – це «чорні ящики», вони настільки величезні та складні, що ми не до кінця розуміємо, як вони працюють, не робимо всіх висновків і не прораховуємо всіх варіантів розвитку та всіх майбутніх наслідків.

ChatGPT – це ” стохастичний папуга “, позбавлений людиноподібного розуміння світу як складної та абстрактної системи і лише повторює вже існуючі словесні формули.

Загроза Google

Те, в якому вигляді ChatGPT видає відповіді на питання, має напружити Корпорацію добра. Оглядач Techcrunch упевнений, що розробка OpenAI підібралася найближче до того, як має працювати пошукач.

Цю ж ідею розвиває Bloomberg . Робота Google побудована так, що він переглядає мільярди веб-сторінок, індексує цей контент, а потім ранжує його як найбільш релевантні відповіді. На виході ми отримуємо список посилань. ChatGPT пропонує дещо більш привабливе для змучених свободою вибору інтернет-користувачів: єдина відповідь, заснована на власному пошуку та синтезі цієї інформації.

Але Google навряд чи піде шляхом генерації власних унікальних відповідей на питання, тому що це зламає його бізнес-модель: згідно з даними, зібраними тим самим Bloomberg, близько 81% виручки Alphabet Inc. у розмірі $257,6 млрд у 2021 році припадало на рекламу, більша частина якої була з оплатою за клік.

З одного боку, нехай розквітають усі квіти, але сам автор публікації визнається, що в 13 із 18 пошукових запитів ChatGPT виявився суб’єктивно кращим і кориснішим, ніж Google. При цьому Microsoft має свій пошуковик, Bing, і вона точно не пропустить привабливої ​​можливості інтегрувати в нього ІІ.

Трохи прикладної культурології

Усі дискусії, що розгортаються навколо ChatGPT, за великим рахунком виявляють культурний код, реалізований неодноразово в багатьох літературних творах — від Пігмаліона до Франкенштейна: неминучий суперечка творця та його «творіння», що загрожує перетворитися на бій.

Yahoo розглянув схожий сюжет в історії з ChatGPT, коли технології будуть здатні замінити людину, і він може не знайти собі місця у майбутньому.

Джерело

Що таке автоматичний маркет-мейкер?

маркетмейкер

Автоматичний маркет-мейкер (Automated Market Maker, AMM) – програмний алгоритм контролю ліквідності та ціноутворення криптоактивів на децентралізованих біржах.

AMM-системи широко застосовуються у сфері DeFi, зокрема на децентралізованих біржах (DEX), таких як включаючи Uniswap, Balancer, Bancor та Curve.

Для створення децентралізованих ринків AMM використовує ліквідність у криптовалютних загальнодоступних пулах із кількох токенів, заблокованих у спеціальних смарт-контрактах.

Як з’явився автоматичний маркет-мейкер (АММ)?

Автоматичний маркет-мейкер (AMM) – автономний торговельний механізм, на якому працює більшість торгових DeFi-протоколів . Він забезпечує рух капіталу та виконання обмінних операцій користувачів на доступних ринках криптоактивів.

Першим відомим розробником, хто заговорив про реалізацію AMM, був член команди проекту Gnosis Алан Лю. Його ідеї були викладені засновником Ethereum Віталіком Бутеріним на Reddit у 2016 році та в особистому блозі у липні 2017 року.

Ця концепція стала основою протоколу платформи Uniswap, яка отримала перший грант у розмірі $100 000 від Ethereum Foundation. Окрім того, Віталік Бутерін консультував розробників Uniswap.

Згодом АММ став широко відомим саме завдяки Uniswap. Водночас однією з перших успішних реалізацій AMM можна назвати платформу Bancor Network, яка зібрала $140 млн через ICO у червні 2017 року.

Як пули ліквідності пов’язані з АММ?

Ключовим елементом, необхідним роботи АММ, є пул ліквідності — своєрідне сховище криптоактивів як смарт-контракта . Пул ліквідності зазвичай складається з двох криптоактивів і формує ринок — аналог торгової пари на централізованій біржі.

Одні учасники пулу блокують свої кошти на отримання доходу з допомогою комісій за обмін. Таких користувачів називають постачальниками ліквідності.

Інша категорія — безпосередні користувачі децентралізованої біржі, які обмінюють криптовалюти в протоколі (такі операції називаються «свопи»), використовуючи якийсь із пулів.

Щоб краще зрозуміти пули ліквідності, розглянемо, що призвело до їхнього створення.

Перші DEX-біржі працювали на Ethereum та використовували для торгівлі стандартну книгу ордерів, що застосовуються на централізованих біржах. Щоб такий торговельний механізм був ефективним, він повинен мати дуже високу швидкість обробки транзакцій. Враховуючи, що операції на децентралізованій біржі підтверджуються через блокчейн, реальна швидкість та можливості таких DEX були вкрай обмежені. АММ дозволила знайти вирішення цієї проблеми.

Таким чином, AMM — принципово інший спосіб створення ринку активів, будучи формулою або правилами, за якими протокол працює із заявками на купівлю або продаж, а також із резервами користувачів.

Як працюють пули ліквідності?

У пулах можна використовувати два і більше активів. Наприклад, на біржі Uniswap можна створювати пули для парних токенів. Платформа Balancer дозволяє створювати пули для трьох та більше токенів. А протокол Curve призначений для пулів на основі активів з аналогічною вартістю, таких як ETH та обернутий токен WETH або USDC та DAI . Роботу цих пулів регулює АММ.

Кожна з таких AMM-DEX може використовувати власні формули та правила для взаємодії з пулами ліквідності. Наприклад, протокол Uniswap використовує таку формулу: x * y = k.

У рівнянні x та y представляють кількість токенів, доступних у пулі ліквідності; k – Постійна величина, звана інваріантом. У випадку з Curve використовуються формули x * y = k та x + y = k.

За формулою x * y = k також працюють проекти SushiSwap та PancakeSwap – це найпоширеніший вид AMM-DEX.

Як відбувається ціноутворення активу у пулі ліквідності?

При блокуванні ліквідності в пулі її постачальник отримує спеціальні LP-токени, що підтверджують їхню частку в пулі. Їх можна як боргову розписку, володіння якої дає право отримувати комісії від обмінних операцій на біржі і повернути свою частку з пулу.

LP-токени – це переданий криптоактив, який можна продати або обміняти на відкритому ринку, а також інвестувати в сторонні програми DeFi.

Процес обміну одного активу в інший у вигляді пулу ліквідності називається свопом (swap). Суть процесу полягає в додаванні в пул тільки одного активу замість двох, як у випадку з LP. За проведення свопу пул стягує невелику комісію, яку можна порівняти з комісією за угоду на централізованій біржі, тобто 0,1-0,3%. Комісії розподіляються між постачальниками ліквідності пропорційно їх частці в пулі.

Приклад

Створимо умовний пул ліквідності для пари ETH/USDC. За ціною 1 ETH, яка дорівнює 2000 USDC, потрібно буде одночасно заблокувати в смарт-контракті будь-яку кількість цих двох монет у співвідношенні 1:2000. За такої ціни в пулі може бути 100 ETH та 200 000 USDC.

Баланс активів у пулі визначається їхньою ціною. Коли користувач вирішить обміняти 10 ETH, використовуючи вищеописаний пул, він внесе свої монети до смарт-контракту звичайною транзакцією. В обмін на свої 10 ETH він отримає 20000 USDC (без урахування комісії за обмін).

Після цього обміну баланс пулу складе вже 110 ETH та 180 000 USDC. Отже, вартість ETH у цьому пулі складе близько 1636 USDC замість 2000 USDC інших ринках. Така ситуація залучає арбітражних трейдерів, які отримують вигоду з дисбалансу шляхом додавання USDC до пулу, поки ціна не досягне ринкових 2 000 USDC за 1 ETH.

У чому недоліки АММ?

Хоча АММ став проривом для трейдингу та DeFi, він має ряд явних недоліків. По-перше, при здійсненні свопів за допомогою АММ існує високий ризик прослизання ціни. У свою чергу, це призводить до ризиків непостійних збитків для LP і майнером цінності (MEV) для звичайних користувачів.

Для захисту від подібних ризиків створюються й інші види AMM, такі як проект CowSwap , що поєднує розробки AMM-Balancer і протоколу Gnosis.

По-друге, на відміну централізованих бірж під час торгівлі через АММ можна розміщувати лише одне вид заявок. Торгувати лімітними заявками чи іншими видами, наприклад, Stop Loss, не можна.

Що таке непостійні збитки (Impermanent Loss)?

Непостійні збитки (Impermanent Loss, IL) – це тимчасові або нереалізовані збитки при утриманні активів у пулі ліквідності під час використання AMM-DEX. IL відносяться до постачальників ліквідності (LP), означаючи різницю в ціні на момент блокування токенів у пулі та фактичною ціною на момент утримання. Збитки називаються нереалізованими тому, що не фіксуються доти, доки ліквідність не виведена з пулу.

Як приклад візьмемо пул ліквідності на біржі Uniswap, що працює за класичною формулою x * y = k:

  1. Постачальник ліквідності заблокував 1 ETH та 2000 DAI. Його частка у пулі становила 10%.
  2. Всього в пулі знаходиться 10 ETH та 20 000 DAI – еквівалент 40 000 DAI.
  3. Баланс пулу не змінювався, оскільки не було нових постачальників ліквідності.
  4. Допустимо, що ринкова ціна ETH змінилася до 4000 DAI.
  5. Тоді арбітражні трейдери скористалися ситуацією та змінили співвідношення в пулі на 5 ETH з одного боку та на 20 000 DAI з іншого. При цьому загальний розмір пулу залишився початковим – 40 000 DAI.
  6. У цей момент постачальник ліквідності вирішив вивести свою частку з пулу – вона становить 10%.
  7. З урахуванням поточного балансу пулу він виводить 0,5 ETH та 2000 DAI, хоча спочатку додавав 1 ETH та 2000 DAI.
  8. Початкова вартість його частки була 4000 DAI (1 ETH плюс 2000 DAI) у перерахунку на стейблкоіни. Вартість активів на момент виведення склала ті ж 4000 DAI (0,5 ETH плюс 2000 DAI)
  9. Однак, якби користувач просто утримував свої 1 ETH та 2000 DAI, то вартість його активів склала б вже 6000 DAI. У цьому полягає нереалізований збиток чи прибуток під час використання AMM-DEX.
  10. Користувач також отримає 10% (пропорційно його частці в пулі) від усіх комісій пулу як нагороду за надання ліквідності. Нагорода може бути зменшена, наприклад, через податки, що відраховуються на адресу розробників або в казначейство проекту на майбутній розвиток.

Це умовний приклад різкого підвищення ціни одного активу на 50%, без урахування безлічі угод арбітражних трейдерів та часу, які необхідні для вирівнювання ціни
активу всередині пулу з ціною на централізованих біржах, де зміни відбуваються моментально.

При «спокійній» течії ринку до розрахунку непостійних збитків додають змінні рухи, як описав у своєму блозі розробник StarkNet Петериса Ерінса. У результаті прогнозних обчислень значення Impermanent Loss на дворазовому русі ціни активу становитиме близько 5,7%. Але це лише прогнозна величина — потенційні збитки передбачити дуже складно.

Як знизити ризики торгівлі за допомогою AMM?

Перш ніж використовувати пул ліквідності, слід прорахувати всі можливі комісії, які вам доведеться заплатити на момент введення та планованого виведення активів.

Необхідно враховувати можливість руху ціни активів на обидві сторони. Деякі потенційні проблеми, такі як ризик незмінних збитків (IL), можна розрахувати заздалегідь.

Для розрахунку показника ІЛ потрібно розуміти природу його походження. Також можна скористатися одним із калькуляторів непостійних збитків, доступних в Інтернеті.

React + TypeScript: необхідний мінімум

React + TypeScript

Чимало React-розробників запитують себе: чи треба мені вчити TypeScript? Ще як треба!

Переваги вивчення TS можуть бути зведені до наступного:

  • ваші шанси отримати більш високооплачувану роботу збільшаться;
  • у вашому коді буде набагато менше багів, його буде легше читати та підтримувати;
  • рефакторити код та оновлювати залежності стане набагато простіше.

Ця стаття являє собою мінімальне введення з використання TypeScript у React.

Основи TypeScript, необхідні React

Примітиви

Існує 3 основні примітиви, які є фундаментом для інших типів:

string // наприклад, "Pat"
boolean // наприклад, true
number // наприклад, 23 чи 1.99

Масиви

Тип масиву складається з примітивів чи інших типів:

number[] // наприклад, [1, 2, 3]
string[] // наприклад, ["Lisa", "Pat"]
User[] // кастомний тип, наприклад, [{ name: "Pat" }, { name: "Lisa" }]

Об’єкти

Об’єкти усюди. Приклад простого об’єкта:

const user = {
  firstName: "Pat",
  age: 23,
  isNice: false,
  role: "CTO",
  skills: ["HTML", "CSS", "jQuery"]
}

Тип, що описує цей об’єкт, виглядає так:

type User = {
  firstName: string;
  age: number;
  isNice: boolean;
  role: string;
  skills: string[];
}

Припустимо, що користувач має друзів, які також є користувачами:

type User = {
  // ...
  friends: User[];
}

Пет завжди ставив кар’єру на перше місце, тому в нього цілком може не бути друзів:

const user = {
  firstName: "Pat",
  age: 23,
  isNice: false,
  role: "CTO",
  skills: ["CSS", "HTML", "jQuery"],
  friends: undefined,
}

У TypeScript для позначення опціонального (необов’язкового) поля використовується символ ? після назви поля:

type User = {
  // ...
  friends?: User[];
}

Перерахування

Ми визначили тип поля role як string:

type User = {
  // ...
  role: string;
}

Пету це не подобається. Він вважає, що тип string недостатньо суворий. Його працівники мають вибирати з обмеженого набору ролей.

Для цього відмінно підійде перерахування (enumeration, enum):

enum UserRole {
  CEO,
  CTO,
  SUBORDINATE,
}

Так набагато краще. Але Пет знає, що значення елементів перерахування є числа. Значенням CEOє 0CTO– 1, а SUBORDINATE– 2. Пету це не до вподоби.

На щастя, як значення елементів перерахування можна використовувати рядки:

enum UserRole {
  CEO = "ceo",
  CTO = "cto",
  SUBORDINATE = "inferior-person",
}

Тепер Пет задоволений:

enum UserRole {
  CEO = "ceo",
  CTO = "cto",
  SUBORDINATE = "inferior-person",
}

type User = {
  firstName: string;
  age: number;
  isNice: boolean;
  role: UserRole;
  skills: string[];
  friends?: User[];
}

const user = {
  firstName: "Pat",
  age: 23,
  isNice: false,
  role: UserRole.CTO, // равняется "cto"
  skills: ["HTML", "CSS", "jQuery"],
}

Функції

Пет любить показувати свою владу, звільняючи підлеглих. Визначимо функцію, що дозволяє Пету робити це.

Типізація параметрів функції

Існує, як мінімум, 3 способи ідентифікувати звільнену особу. По-перше, ми можемо використовувати декілька параметрів:

function fireUser(firstName: string, age: number, isNice: boolean) {
  // ...
}

// чи так
const fireUser = (firstName: string, age: number, isNice: boolean) => {
  // ...
}

По-друге, ми можемо обернути параметри в об’єкт і визначити типи в об’єкті:

function fireUser({ firstName, age, isNice }: {
  firstName: string;
  age: number;
  isNice: boolean;
}) {
  // ...
}

Нарешті ми можемо винести параметри в окремий тип. Спойлер: така техніка часто використовується для проповнення компонентів React:

type User = {
  firstName: string;
  age: number;
  role: UserRole;
}

function fireUser({ firstName, age, role }: User) {
  // ...
}

Типізація значення, що повертається функцією

Пет вважає, що просто звільнити працівника недостатньо. Тому може бути гарною ідеєю повертати користувача з функції.

Для визначення типу значення, що повертається функцією, також є кілька способів. Ми можемо додати : Typeпісля закривання дужки списку параметрів функції:

function fireUser(firstName: string, age: number, role: UserRole): User {
  // ...
  return { firstName, age, role };
}

// чи так
const fireUser = (firstName: string, age: number, role: UserRole): User => {
  // ...
  return { firstName, age, role };
}

Якщо, наприклад, ми спробуємо повернути null отримаємо помилку:

null

Насправді, найчастіше ми не маємо необхідності визначати тип значення, що повертається функцією, явно. TypeScript відмінно справляється з припущенням (висновком) таких типів:

Code

За наявності сумнівів у коректності TypeScript типу, що виводиться, достатньо навести курсор на назву змінної або функції (спасибі сучасним редакторам коду).

Інші речі, з якими вам доведеться мати справу

Існує ще кілька корисних речей, які можуть знадобитися при використанні TypeScript у React. Мабуть, найважливішими з них є:

React і TypeScript

Коли мова заходить про використання TypeScript в React, слід пам’ятати, що компоненти і хуки React — це лише функції, а props — лише об’єкти.

Функціональні компоненти

У більшості випадків у нас немає необхідності визначати тип, який повертається компонентом:

function UserProfile() {
  return <div>If you're Pat: YOU'RE AWESOME!!</div>
}

Типом, що повертається компонентом є JSX.Element, як видно на наведеному нижче зображенні:

Code

Якщо ми спробуємо повернути з компонента не JSX, то отримаємо попередження:

Code

В даному випадку об’єкт user не є валідним JSX:

'UserProfile' cannot be used as a JSX component.
Its return type 'User' is not a valid JSX element.

Props

Як зазначалося раніше, props — це лише об’єкти:

enum UserRole {
  CEO = "ceo",
  CTO = "cto",
  SUBORDINATE = "inferior-person",
}

type UserProfileProps = {
  firstName: string;
  role: UserRole;
}

function UserProfile({ firstName, role }: UserProfileProps) {
    if (role === UserRole.CTO) {
    return <div>Hey Pat, you're AWESOME!!</div>
  }
  return <div>Hi {firstName}, you suck!</div>
}

// чи так
const UserProfile = ({ firstName, role }: UserProfileProps) => {
    if (role === UserRole.CTO) {
    return <div>Hey Pat, you're AWESOME!!</div>
  }
  return <div>Hi {firstName}, you suck!</div>
}

Зверніть увагу : при роботі над React-проектами ви, швидше за все, зустрінете багато коду, в якому використовується тип React.FC або React.FunctionComponent:

const UserProfile: React.FC<UserProfileProps>({ firstName, role }) {
  // ...
}

Використовувати ці типи більше не рекомендується .

Props-коллбеки

Як пропов компонентам часто передаються функції зворотного виклику (коллбеки):

type UserProfileProps = {
  id: string;
  firstName: string;
  role: UserRole;
  fireUser: (id: string) => void;
};

function UserProfile({ id, firstName, role, fireUser }: UserProfileProps) {
  if (role === UserRole.CTO) {
    return <div>Hey Pat, you're AWESOME!!</div>;
  }
  return (
    <>
      <div>Hi {firstName}, you suck!</div>
      <button onClick={() => fireUser(id)}>Fire this loser!</button>
    </>
  );
}

voidозначає, що функція нічого не повертає.

Дефолтні props

Як ви пам’ятаєте, ми можемо зробити поле опціональним за допомогою ?. Те саме можна робити з props:

type UserProfileProps = {
  age: number;
  role?: UserRole;
}

Опціональний props може мати значення за замовчуванням:

function UserProfile({ firstName, role = UserRole.SUBORDINATE }: UserProfileProps) {
  if (role === UserRole.CTO) {
    return <div>Hey Pat, you're AWESOME!!</div>
  }
  return <div>Hi {firstName}, you suck!</div>
}

Хуки

useState()

useState– Найпопулярніший хук React. У багатьох випадках його не слід типізувати. TypeScript здатний вивести типи значень, що повертаються useState() на основі початкового значення:

function UserProfile({ firstName, role }: UserProfileProps) {
  const [isFired, setIsFired] = useState(false);

  return (
    <>
      <div>Hi {firstName}, you suck!</div>
      <button onClick={() => setIsFired(!isFired)}>
        {isFired ? "Oops, hire them back!" : "Fire this loser!"}
      </button>
    </>
  );
}
Code

Тепер ми в безпеці. При спробі оновити стан не логічним значенням отримуємо помилку:

Code

У деяких випадках TypeSctipt не може вивести тип значень, що повертаються useState():

// TS не знає, элементи якого типу будуть у масиву
const [names, setNames] = useState([]);

// початковим значенням є `undefined`, тому TypeScript-у невідом справжній тип
const [user, setUser] = useState();

// теж саме справедливо для `null` в якості початкового значення
const user = useState(null);
Code

useState()реалізовано за допомогою загального типу (дженерика, generic type). Ми можемо використовувати це для типізації стану:

// типом `names` є `string[]`
const [names, setNames] = useState<string[]>([]);
setNames(["Pat", "Lisa"]);

// типом user є `User | undefined`
const [user, setUser] = useState<User>();
setUser({ firstName: "Pat", age: 23, role: UserRole.CTO });
setUser(undefined);

// типом `user` є `User | null`
const [user, setUser] = useState<User | null>(null);
setUser({ firstName: "Pat", age: 23, role: UserRole.CTO });
setUser(null);

Ми не будемо розглядати інші вбудовані хуки, вони типизуються схожим чином, за винятком того useEffect(), що не потребує типізації.

Хуки на замовлення

Кастомний хук – це просто функція:

function useFireUser(firstName: string) {
    const [isFired, setIsFired] = useState(false);
  const hireAndFire = () => setIsFired(!isFired);

    return {
    text: isFired ? `Oops, hire ${firstName} back!` : "Fire this loser!",
    hireAndFire,
  };
}

function UserProfile({ firstName, role }: UserProfileProps) {
  const { text, hireAndFire } = useFireUser(firstName);

  return (
    <>
      <div>Hi {firstName}, you suck!</div>
      <button onClick={hireAndFire}>
        {text}
      </button>
    </>
  );
}

Події

З вбудованими обробниками подій у React працювати легко, оскільки TypeScript-у відомі типи подій:

function FireButton() {
  return (
    <button onClick={(event) => event.preventDefault()}>
      Fire this loser!
    </button>
  );
}

Але визначення оброблювача у вигляді окремої функції докорінно змінює справу:

function FireButton() {
  const onClick = (event: /* ??? */) => {
    event.preventDefault();
  };

  return (
    <button onClick={onClick}>
      Fire this loser!
    </button>
  );
}

Який тип має event? Існує 2 підходи:

  • гуглити (не рекомендується, викликає запаморочення));
  • приступити до реалізації вбудованої функції та дозволити TS вивести типи:
Code

Щасливі копіпастінг. Нам навіть не потрібно розуміти, що відбувається (більшість обробників є дженериками, як useState()).

function FireButton() {
  const onClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
    event.preventDefault();
  };

  return (
    <button onClick={onClick}>
      Fire this loser!
    </button>
  );
}

Що щодо оброблювача зміни значення інпуту?

function Input() {
  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    console.log(event.target.value);
  };

  return <input onChange={onChange} />;
}

А команда?

function Select() {
  const onChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    console.log(event.target.value);
  };

  return <select onChange={onChange}>...</select>;
}

Дочірні компоненти

Композиція компонентів, від якої ми всі в захваті, передбачає передачу пропа children:

type LayoutProps = {
  children: React.ReactNode;
};

function Layout({ children }: LayoutProps) {
  return <div>{children}</div>;
}

Тип React.ReactNodeнадає більшу свободу вибору переданого значення. Він дозволяє передавати майже будь-що (крім об’єкта):

Code

Якщо як props повиннa передаватися тільки розмітка, тип childrenможна обмежити до React.ReactElementабо JSX.Element(що по суті те саме):

type LayoutProps = {
  children: React.ReactElement; // чи `JSX.Element`
};

Ці типи є набагато суворішими:

Code

Сторонні бібліотеки

Додавання типів

Сьогодні багато сторонніх бібліотек містять відповідні типи. У цьому випадку окремий пакет (з типами) не потрібно встановлювати.

Типи для великої кількості існуючих бібліотек містяться в репозиторії DefinitelyTyped на GitHub і публікуються під егідою організації @types(включаючи типи React). При встановленні пакета без типів та його імпорті отримуємо таку помилку:

Code

Копіюємо виділену команду та виконуємо її в терміналі:

npm i --save-dev @types/styled-components

Якщо ви все ж таки зіткнулися з відсутністю типів для сторонньої бібліотеки, доведеться визначити глобальні типи самостійно у файлі .d.ts(ми не розглядатимемо його в рамках статті).

Використання дженериків

Бібліотеки розраховані на різні випадки використання, тому вони мають бути гнучкими. Для забезпечення гнучкості типів використовуються дженерики. Ми їх уже бачили в useState():

const [names, setNames] = useState<string[]>([]);

Такий прийом дуже поширеним для сторонніх бібліотек. Приклад з Axios:

import axios from "axios"

async function fetchUser() {
  const response = await axios.get<User>("https://example.com/api/user");
  return response.data;
}
Code

Реагувати на запит:

import { useQuery } from "@tanstack/react-query";

function UserProfile() {
  // загальні типи даних і помилки
  const { data, error } = useQuery<User, Error>(["user"], () => fetchUser());

  if (error) {
    return <div>Error: {error.message}</div>;
  }
  // ...
}

Стилізовані компоненти:

import styled from "styled-components";

// загальний тип для props
const MenuItem = styled.li<{ isActive: boolean }>`
  background: ${(props) => (props.isActive ? "red" : "gray")};
`;

function Menu() {
  return (
    <ul>
      <MenuItem isActive>Menu Item 1</MenuItem>
    </ul>
  );
}

Способи усунення несправностей

Початок роботи з React & TypeScript

Створення нового React-проекту з підтримкою TypeScript – справа однієї команди. Я рекомендую використовувати шаблони Vite+React+TS або Next.js+TS:

npm create vite [project-name] --template react-ts

npx create-next-app [project-name] --ts

Виявлення правильного типу

Ми це вже розглядали, але повторимо ще раз: починаємо писати вбудовану функцію та дозволяємо TypeScript вивести правильний тип події:

Code

За наявності сумнівів щодо кількості доступних параметрів набираємо (...args) => та отримуємо відповідний масив:

Code

Вивчення типу

Найпростіший спосіб отримати список всіх доступних полів типу – використовувати автозавершення в IDE. Для цього достатньо натиснути CTRL + Пробіл (Windows) або Option + Пробіл (Mac):

Code

Щоб перейти до визначення типу, слід натиснути CTRL + Click (Windows) або CMD + Click (Mac):

Code

Читання повідомлень про помилки

Повідомлення про помилки та попередження TS є дуже інформативними, головне – навчитися їх правильно читати. Розглянемо приклад:

function Input() {
  return <input />;
}

function Form() {
  return (
    <form>
      <Input onChange={() => console.log("change")} />
    </form>
  );
}

Ось що показує TS:

Code

Що це означає? Що ще за тип IntrinsicAttributes? При роботі з бібліотеками (у тому числі з самим React) ви часто зустрічатимете дивні назви типів, на кшталт цього.

Моя порада: ігноруйте їх спочатку.

Найважливішою частиною є останній рядок:

Property 'onChange' does not exist on type...

Дивимося на визначення компонента Input:

function Input() {
  return <input />;
}

У нього немає пропа onChange. Саме це “не подобається” TypeScript.

Тепер розглянемо складніший приклад:

const MenuItem = styled.li`
  background: "red";
`;

function Menu() {
  return <MenuItem isActive>Menu Item</MenuItem>;
}
Code

Нічого собі повідомлення про помилку! Без паніки: прокручуємо в кінець повідомлення – як правило, відповідь знаходиться там:

Code

Перетини

Припустимо, що у нас є тип User, визначений в окремому файлі, наприклад types.ts:

export type User = {
  firstName: string;
  role: UserRole;
}

Він використовується для типізації пропов компонента UserProfile:

function UserProfile({ firstName, role }: User) {
  // ...
}

Який використовується в компоненті UserPage:

function UserPage() {
  const user = useFetchUser();

  return <UserProfile {...user} />;
}

Поки все добре. Але що якщо UserProfile буде приймати ще один проп – функцію fireUser?

function UserProfile({ firstName, role, fireUser }: User) {
  return (
    <>
      <div>Hi {firstName}, you suck!</div>
      <button onClick={() => fireUser({ firstName, role })}>
        Fire this loser!
      </button>
    </>
  );
}

Отримуємо помилку:

Code

Цю проблему можна вирішити за допомогою перетину (intersection type). При перетині всі поля двох типів поєднуються в один тип. Перетини створюються за допомогою символу &:

type User = {
  firstName: string;
  role: UserRole;
}

// `UserProfileProps` буде вміщувати в себе всі поля `User` та `fireUser`
type UserProfileProps = User & {
    fireUser: (user: User) => void;
}

function UserProfile({ firstName, role, fireUser }: UserProfileProps) {
  return (
    <>
      <div>Hi {firstName}, you suck!</div>
      <button onClick={() => fireUser({ firstName, role })}>
        Fire this loser!
      </button>
    </>
  );
}

Більш “чистим” способом є визначення окремих типів для компонентів, що приймаються props:

type User = {
  firstName: string;
  role: UserRole;
}

// !
type UserProfileProps = {
  user: User;
  fireUser: (user: User) => void;
}

function UserProfile({ user, onClick }: UserProfileProps) {
  return (
    <>
      <div>Hi {user.firstName}, you suck!</div>
      <button onClick={() => fireUser(user)}>
        Fire this loser!
      </button>
    </>
  );
}

Дякую за увагу та happy coding!

Переклад статті “Minimal TypeScript Crash Course For React

Як виправити проблему хука React useState, яка не встановлює початкове значення?

React useState

Хук useState дозволяє нам створювати зміни стану в наших компонентах React. Він приймає аргумент для початкового значення стану. Іноді нам може знадобитися встановити початкове значення стану з props. І ми хочемо оновити початкове значення, коли змінюється значення props.

У цій статті ми розглянемо, як виправити React хук useState з встановленням початкового стану з props.

Оновлення стану під час оновлення властивості

Щоб оновити стан, коли props оновлюється, ми маємо спостерігати за значенням props за допомогою useEffect хука. Потім у useEffect зворотному виклику ми можемо викликати функцію встановлення стану, щоб оновити значення стану значенням властивості. Наприклад, ми можемо написати:

import React, { useEffect, useState } з "react";
  const Count = ({ count }) => { 
  const [num, setNum] = useState(count);
  useEffect(() => { 
    setNum(count); 
  }, [count]);
  return <p>{num}</p>; 
};
export default function App() { 
  const [count, setCount] = useState(0);
  return ( 
    <div className="App"> 
      <button onClick={() => setCount((c) => c + 1)}>increment</button> 
      <Count count={count} /> 
    </div> 
  ); 
}

У Count компоненті ми маємо useState хук зі count значенням як аргумент. Це встановлюється початкове значення count яке дорівнює num. Потім у нас є useEffect хук, який стежить за count значенням, передаючи його в масив у другому аргументі. Потім у зворотному виклику ми викликаємо, setNum щоб оновити num значення та відобразити його в операторі return під ним.

У App ми маємо стан count, створений хуком useState. Потім ми викликаємо setCount в обробнику onClick кнопки, яка оновлює значення count стану. І ми передаємо count значення як значення count props у Count компоненті.

Тепер, коли ми натискаємо кнопку збільшення, ми бачимо оновлення значення num та його останнє значення.

Висновок

Ми можемо переконатися, що стан компонента React оновлюється, коли змінюється значення props, спостерігаючи за значенням props за допомогою useEffect хука, а потім викликаємо функцію встановлення стану у зворотному виклику useEffect зі значенням props як аргументом.

Переклад статті “How to Fix the React useState Hook Not Setting Initial Value Problem?

Потужність декораторів TypeScript на живих прикладах. Декорування методів класу.

Декоратори darkside

Декоратори – інструмент декларативного програмування. З їх допомогою можна легко та елегантно додати до класів та членів класу метадані. На основі цих метаданих можна розширювати або змінювати поведінки класів та членів класу, не змінюючи при цьому кодову базу, до якої застосовано декоратор. Саму технологію можна віднести до мета-програмування чи декларативного програмування.

У рамках цієї статті розбирається кілька прикладів із реальних проектів, де застосування декораторів спростило код для розуміння і виключило його дублювання.

Яку проблему вирішують декоратори?

За допомогою декораторів ми можемо уникнути “дублювання” коду, інкапсулюючи наскрізну функціональність в окремий модуль. Забрати зайвий “шум” у коді, що дозволить сфокусуватися автору на бізнес логіці програми.

Наскрізна функціональність – функціональність, яка розподілена по всій кодовій базі. Як правило, ця функціональність не залежить від предметної галузі вашого проекту. До неї можна віднести такі приклади:

  • Логування
  • Кешування
  • Валідація
  • Форматування
  • і т.д.

Для роботи з наскрізною функціональністю існує ціла парадигма -  Аспектно-орієнтоване програмування (AOP). Про її реалізацію в JavaScript раджу прочитати в цій чудовій статті . Так само існують чудові бібліотеки, що реалізують AOP JavaScript:

Якщо Вам цікава тема AOP, раджу поставити ці пакети та погратися з їхньою функціональністю.

У цій статті я спробував показати, як можна вирішити описані вище проблеми, вбудованої в TypeScript функціональністю — декоратори.

Для прочитання цієї статті передбачається, що ви маєте досвід використання react, mobx і typescript, т.к. я не вдаватимуся до подробиць цих технологій.

Трохи про декораторів загалом

У TypeScript декоратором є функція.

Форма застосування: @funcName . Де funcName – ім’я функції, що описує декоратор. Після прикріплення декоратора до члена класу, а потім його виклику, спочатку будуть виконуватися декоратори, а потім код класу. Однак декоратор може перервати потік виконання коду на своєму рівні, так що основний код класу в кінцевому рахунку не буде виконано. Якщо до члена класу прикріплено кілька декораторів, їх виконання відбувається зверху донизу по черзі.

Декоратори досі є експериментальною функцією TypeScript. Тому, для їх використання, вам потрібно додати до вашого tsconfig.json наступне налаштування :

{
  "compilerOptions": {
    "experimentalDecorators": true,
  },
}

Функція-декоратор викликається компілятором, і компілятор сам підставляє потрібні аргументи.

Сигнатура цієї функції для методів класу:

funcName<TCls, TMethod>(target: TCls, key: string, descriptor: TypedPropertyDescriptor): TypedPropertyDescriptor | void

Де:

  • target – об’єкт, для якого буде застосовано декоратор
  • key – ім’я методу класу, що декорується
  • descriptor – дескриптор методу класу.

За допомогою дескриптора ми можемо отримати доступ до вихідного методу об’єкта.

За допомогою шаблонної типізації можна виключити неправильне використання декоратора. Наприклад, ви можете обмежити застосування декоратора методами класу, у яких перший аргумент має строковий тип, визначивши тип дескриптора таким чином:

type TestDescriptor = TypedPropertyDescriptor<(id: string, ...args: any[]) => any>;

У наших прикладах ми використовуватимемо фабрики декораторів . Фабрика декораторів -  це функція яка повертає викликану декоратором під час виконання функцію.

function format(pattern: string) {  
  // это фабрика декораторов и она возвращает функцию-декоратора  
  return function (target) {    
    // это декоратор. Здесь будет код,    
    // который что то делает с target и pattern  
  };
}

Підготовчі роботи

При розгляді прикладів нижче ми будемо використовувати 2 моделі даних:

export type Product = {
  id: number;
  title: string;
};

export type User = {
  id: number;
  firstName: string;
  lastName: string;
  maidenName: string;
}

У всіх функціях декораторів для дескриптора ми будемо використовувати тип PropertyDescriptor , який є еквівалентом TypedPropertyDescriptor<any> .

Додамо функцію-хелпер createDecorator, яка допоможе нам скоротити синтаксичний цукор створення декораторів:

export type CreateDecoratorAction = (self: T, originalMethod: Function, ...args: any[]) => Promise | void;

export function createDecorator(action: CreateDecoratorAction) {
  return (target: T, key: string, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value; // ссылка на оригинальный метод класса
    // переопределяем метод класса
    descriptor.value = async function (...args: any[]) {
      const _this = this as T;
      await action(_this, originalMethod, ...args);
    };
  };
}

Проект побудований на React + TypeScript . Для відображення стану програми на екран використовується чудова бібліотека Mobx . Нижче на прикладах я опущу пов’язані з Mobx частини коду, щоб сфокусувати вашу увагу на проблематики та її вирішення.

 Повну робочу версію коду можна знайти в цьому репозиторії .

Відображення індикатора завантаження даних

Спершу створимо клас AppStore, який міститиме в собі весь стан нашої маленької програми. Додаток складатиметься з двох списків – список користувачів та список товарів. Дані будуть використовуватися з сервісу dummyjson .

AppStore

В результаті рендеру сторінки викликаються два запити на сервер для завантаження списків. AppStore виглядає так:

class AppStore {
  users: User[] = [];
  products: Product[] = [];
  usersLoading = false;
  productsLoading = false;

  async loadUsers() {
    if (this.usersLoading) {
      return;
    }
    try {
      this.setUsersLoading(true);
      const resp = await fetch("https://dummyjson.com/users");
      const data = await resp.json();
      const users = data.users as User[];
      this.users = users;
    } finally {
      this.setUsersLoading(false);
    }
  }

  async loadProducts() {
    if (this.productsLoading) {
      return;
    }
    try {
      this.setProductsLoading(true);
      const resp = await fetch("https://dummyjson.com/products");
      const data = await resp.json();
      const products = data.users as Product[];
      this.products = products;
    } finally {
      this.setProductsLoading(false);
    }
  }
  
  private setUsersLoading(value: boolean) {
    this.usersLoading = value;
  }
 
  private setProductsLoading(value: boolean) {
    this.usersLoading = value;
  }
}

Зміна значень прапорців usersLoading і productsLoading контролює видимість індикаторів завантаження списку. Можна побачити в наведеному коді вище, ця функціональність методу повторюється. Спробуємо скористатися декораторами, щоб усунути це дублювання. Інкапсульуємо всі прапори завантаження в один об’єкт, який лежатиме як loading нашого сховища стану. Для цього визначимо інтерфейс і базовий клас (для повторного використання коду управління станом завантаження прапорів):

type KeyBooleanValue = {
  [key: string]: boolean;
};

export interface ILoadable {
  loading: T;
  setLoading(key: keyof T, value: boolean): void;
}

export abstract class Loadable implements ILoadable {
  loading: T;
  constructor() {
    this.loading = {} as T;
  }
  
  setLoading(key: keyof T, value: boolean) {
    (this.loading as KeyBooleanValue)[key as string] = value;
  }
}

Якщо у вас немає можливості використовувати успадкування, можете використовувати інтерфейс ILoadable і реалізувати власний метод setLoading.

Тепер ізолюємо загальну функціональність контролю стану прапорів декоратор. Для цього створимо узагальнену фабрику створення декораторів loadable, використовуючи функцію хелпер createDecorator :

export const loadable = (keyLoading: keyof T) =>
  createDecorator<ILoadable>(async (self, method, ...args) => {
    try {
      if (self.loading[keyLoading]) return;
      self.setLoading(keyLoading, true);
      return await method.call(self, ...args);
    } finally {
      self.setLoading(keyLoading, false);
    }
  });

Фабрична функція є узагальненою та приймає на вхід ключі властивостей об’єкта, що лежатиме у властивості loading інтерфейсу ILoadable. Для коректного використання цього декоратора потрібно, щоб наш клас реалізував інтерфейс ILoadable. Скористаємося успадкуванням від класу Loadable, який вже реалізує цей інтерфейс і перепишемо код наступним чином:

const defaultLoading = {
  users: false,
  products: false,
};

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];
  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  async loadUsers() {
    const resp = await fetch("https://dummyjson.com/users");
    const data = await resp.json();
    const users = data.users as User[];
    this.users = users;
  }

  @loadable("products")
  async loadProducts() {
    const resp = await fetch("https://dummyjson.com/products");
    const data = await resp.json();
    const products = data.users as Product[];
    this.products = products;
  }
}

Як тип для об’єкта як loading ми передаємо динамічно обчислюваний тип typeof defaultLoading від стану за умовчанням цього об’єкта — defaultLoading. Також, привласнюємо цей стан властивості loading. За рахунок цього, рядкові ключі, які ми передаємо в декоратор loadable, контролюється типізацією typescript. Як ви бачите, методи loadUsers та loadProducts краще читаються, а функціональність показу спіннерів інкапсульована в окремий модуль. Фабрика декораторів loadable та інтерфейс ILoadable абстраговані від конкретної реалізації стора і можуть використовуватися в необмеженій кількості сторін у додатку.

Обробка помилок у методі

Якщо раптом, з якоїсь причини, у наведеному вище прикладі сервіс dummyjson перестане бути доступним, наш додаток впаде з помилкою і користувач про це не дізнається. Давайте виправимо цю ситуацію

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];

  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  async loadUsers() {
    try {
      const resp = await fetch("https://dummyjson.com/users");
      const data = await resp.json();
      const users = data.users as User[];
      this.users = users;
    } catch (error) {
      notification.error({
        message: "Error",
        description: (error as Error).message,
        placement: "bottomRight",
      });
    }
  }

  @loadable("products")
  async loadProducts() {
    try {
      const resp = await fetch("https://dummyjson.com/products");
      const data = await resp.json();
      const products = data.users as Product[];
      this.products = products;
    } catch (error) {
      notification.error({
        message: "Error",
        description: (error as Error).message,
        placement: "bottomRight",
      });
    }
  }
}

У кожному методі з’являється блок try…catch…, де обробка помилок відбувається в блоці catch. Спливає повідомлення у нижньому правому куті з текстом помилки. Скористайтеся силою декораторів та інкапсулюємо цю обробку в окремий модуль, зробивши її абстрактною:

export const errorHandle = (title?: string, desc?: string) =>
  createDecorator(async (self, method, ...args) => {
    try {
      return await method.call(self, ...args);
    } catch (error) {
      notification.error({
        message: title || "Error",
        description: desc || (error as Error).message,
        placement: "bottomRight",
      });
    }
  });

Фабрична функція приймає на вхід необов’язкові параметри — кастомний заголовок та опис помилок, які будуть виводитися в повідомленні. Якщо параметри не будуть заповнені, буде використовуватися заголовок за промовчанням і повідомлення з поля message помилки. Використовуємо функцію errorHandle у нашому коді:

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];
  
  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  @errorHandle()
  async loadUsers() {
    const resp = await fetch("https://dummyjson.com/users");
    const data = await resp.json();
    const users = data.users as User[];
    this.users = users;
  }
  @loadable("products")
  @errorHandle()
  async loadProducts() {
    const resp = await fetch("https://dummyjson.com/products");
    const data = await resp.json();
    const products = data.users as Product[];
    this.products = products;
  }
}

Так просто ми додали функціональність обробки помилок, прибрали дублювання коду і сам код методів залишився простим та читаним.

Повідомлення про успішну роботу методу

Припустимо, що нам потрібно повідомити про успішне завантаження списків користувачів та продуктів. Без декораторів код виглядав би так:

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];
  
  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  @errorHandle()
  async loadUsers() {
    const resp = await fetch("https://dummyjson.com/users");
    const data = await resp.json();
    const users = data.users as User[];
    this.users = users;
    notification.success({
      message: "Users uploaded successfully",
      placement: "bottomRight",
    });
  }

  @loadable("products")
  @errorHandle()
  async loadProducts() {
    const resp = await fetch("https://dummyjson.com/products");
    const data = await resp.json();
    const products = data.users as Product[];
    this.products = products;
    notification.success({
      message: "Products uploaded successfully",
      placement: "bottomRight",
    });
  }
}

Також, інкапсулюємо цю функціональність в окремий модуль і зробимо її абстрактною:

export const successfullyNotify = (message: string, description?: string) =>
  createDecorator(async (self, method, ...args) => {
    const result = await method.call(self, ...args);
    notification.success({
      message,
      description,
      placement: "bottomRight",
    });
    return result;
  });

Фабрична функція приймає на вхід обов’язковий параметр повідомлення у повідомленні та не обов’язковий парметр опис повідомлення. Перепишемо код із використанням цієї функції:

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];

  constructor() {
    super();
    this.loading = defaultLoading;
  }
  @loadable("users")
  @errorHandle()
  @successfullyNotify("Users uploaded successfully")
  async loadUsers() {
    const resp = await fetch("https://dummyjson.com/users");
    const data = await resp.json();
    const users = data.users as User[];
    this.users = users;
  }

  @loadable("products")
  @errorHandle()
  @successfullyNotify("Products uploaded successfully")
  async loadProducts() {
    const resp = await fetch("https://dummyjson.com/products");
    const data = await resp.json();
    const products = data.users as Product[];
    this.products = products;
  }
}

Логування методу

Якщо ви збираєте дані вашої програми і потім проводите аналіз для його оптимізації та відстеження помилок, то вам необхідно додавати логи в код. Розглянемо приклад логування – виведення в консоль. Додамо логи до нашого сервісу без використання декоратора:

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];

  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  @errorHandle()
  @successfullyNotify("Users uploaded successfully")
  async loadUsers() {
    try {
      console.log(`Before calling the method loadUsers`);
      const resp = await fetch("https://dummyjson.com/users");
      const data = await resp.json();
      const users = data.users as User[];
      this.users = users;
      console.log(`The method loadUsers worked successfully.`);
    } catch (error) {
      console.log(`An exception occurred in the method loadUsers. Exception message: `, (error as Error).message);
      throw error;
    } finally {
      console.log(`The method loadUsers completed`);
    }
  }

  @loadable("products")
  @errorHandle()
  @successfullyNotify("Products uploaded successfully")
  async loadProducts() {
    try {
      console.log(`Before calling the method loadProducts`);
      const resp = await fetch("https://dummyjson.com/products");
      const data = await resp.json();
      const products = data.users as Product[];
      this.products = products;
      console.log(`The method loadProducts worked successfully.`);
    } catch (error) {
      console.log(`An exception occurred in the method loadProducts. Exception message: `, (error as Error).message);
      throw error;
    } finally {
      console.log(`The method loadProducts completed`);
    }
  }
}

Як бачите, код у методах ускладнився для сприйняття та розуміння. І тут ще немає коду, який включає або відключає логи залежно від стейджу збірки або за будь-якими іншими критеріями, що зробить код ще складнішим. Створимо універсальний декоратор для логування.

export type LogPoint = "before" | "after" | "error" | "success";

let defaultLogPoint: LogPoint[] = ["before", "after", "error", "success"];

export function setDefaultLogPoint(logPoints: LogPoint[]) {
  defaultLogPoint = logPoints;
}

export const log = (points = defaultLogPoint) =>
  createDecorator(async (self, method, ...args) => {
    try {
      if (points.includes("before")) {
        console.log(`Before calling the method ${method.name} with args: `, args);
      }

      const result = await method.call(self, ...args);

      if (points.includes("success")) {
        console.log(`The method ${method.name} worked successfully. Return value: ${result}`);
      }

      return result;
    } catch (error) {
      if (points.includes("error")) {
        console.log(
          `An exception occurred in the method ${method.name}. Exception message: `,
          (error as Error).message
        );
      }
      throw error;
    } finally {
      if (points.includes("after")) {
        console.log(`The method ${method.name} completed`);
      }
    }
  });

У цьому декораторі ми визначили точки для логування, які можна налаштовувати, передавши їх типізований масив у перший параметр фабрики декоратора. За промовчанням логується все. Також, функції setDefaultLogPoint можна перевизначити точки логування за промовчанням. Застосуємо цю фабрику в нашому коді:

class AppStore extends Loadable {
  users: User[] = [];
  products: Product[] = [];
  
  constructor() {
    super();
    this.loading = defaultLoading;
  }

  @loadable("users")
  @errorHandle()
  @successfullyNotify("Users uploaded successfully")
  @log()
  async loadUsers() {
    const resp = await fetch("https://dummyjson.com/users");
    const data = await resp.json();
    const users = data.users as User[];
    this.users = users;
  }

  @loadable("products")
  @errorHandle()
  @successfullyNotify("Products uploaded successfully")
  @log()
  async loadProducts() {
    const resp = await fetch("https://dummyjson.com/products");
    const data = await resp.json();
    const products = data.users as Product[];
    this.products = products;
  }
}

Підсумуємо

Області застосування декораторів різноманітні та потенціал їх великий. З їхньою допомогою можна легко вирішити цілий спектр прикладних завдань, зробити код елегантним та простим для читання. Завдяки декораторам, ми можемо інкапсулювати повторюваний наскрізний функціонал у модулі, і перевикористовувати в інших частинах проекту або інших проектах. Однак у невмілих руках такий потужний інструмент може й нашкодити, наприклад, змінити пряме призначення методу. Але, на мій погляд, це не є підставою не вивчати та застосовувати цю технологію у своїх проектах. Декоратори стануть чудовим доповненням у ваших проектах як у ГО так і у функціональній парадигмі.

Переклад статті “The power of TypeScript decorators: real cases. Decorating class methods.

Що таке веб-стандарти та як працює веб-браузер?

як працює веб-браузер

Розповім я вам одну історію. Якось я розробляв черговий компонент з вибором дати для нашої системи проектування. Компонент складається з поля для текстового введення та спливаючого календаря, що відображається при натисканні мишею по цьому полю. Потім календар, що випливає, можна закрити, клацнувши поза ним або якщо вибрано дату.

Компонент для вибору дати

У більшості реалізацій такої логіки клацнути поза календарем застосовувалися конкретні слухачі подій, прикріплені до DOM. Але я хотів зробити наш компонент з вибором дати доступним так, щоб можна було відкривати календар із вкладками і таким же чином його закривати. Крім того, слухачі подій можуть вступати в конфлікт один з одним, якщо ви розмістите на сторінці кілька таких компонентів для вибору дати.

А якщо просто покластися на нативні події фокусування і розмиття, а не зв’язуватися з клацаннями поза полем? У такому разі, природно, підтримуються вкладки, події торкання та клацання, і все це вже реалізовано у браузері. Єдина проблема, яку доведеться вирішити в даному випадку, така: що робити, якщо ви клацаєте спливаючим полем, але без вибору дати. Тоді фокус переміщається на календар, в результаті з полем для введення відбувається розмиття, і зрештою спливаюче поле ховається.

Тут я задумався, а чи є спосіб зробити клацання, але не зрушувати фокус. Нашвидкуруч погуглив, я знайшов, як це зробити: придушити подію mouseDown, за замовчуванням спрацьовує для спливаючого поля. Все так: одного рядка вистачило, щоб усі клацання працювали, але поле для введення тексту залишалося у фокусі.

Здавалося: і все, рішення знайдено. Давайте рухатись далі. Але щось мене зупиняло. Чому саме mouseDown, а не mouseUp зупиняє фокус, але пропускає клацання? Це частина якогось чинного стандарту? Чи працюватиме це в кросбраузерному режимі? Бібліотека React Testing, за допомогою якої у нас робилися інтеграційні тести, також не підтримувала цієї можливості, тому мені довелося б змінювати функцію симуляції.

Що таке веб-стандарт?

Добре, оскільки відповіді зі Stack Overflow мені було недостатньо, то де ще шукати інформацію про поведінку веб-браузерів, як не в стандартних стандартах?
Ймовірно, ви чули про W3C , він же – Консорціум Всесвітньої Павутини. Це міжнародне співтовариство, яке розробляє відкриті стандарти для Інтернету. W3C прагне гарантувати, що всі керуються одними і тими самими нормами, і нам не доведеться підтримувати десятки різних оточень. Якщо зайдете на їхній сайт, то знайдете список усіх стандартів , над якими вони працюють.

Давайте заглянемо в один документ, де можуть бути відповіді на наші запитання –  UI Events Standard(Стандарт подій інтерфейсу користувача). У цьому документі вказано потік подій DOM, визначено список подій та порядок їх виконання. Якщо вам здавалося, що веб-стандарти – це нудні, каламутні простирадла тексту, через які доводиться продиратися – одразу переходьте до розділу DOM Event Architecture (Архітектура подій DOM). Тут пояснено сплив подій, розказано про захоплення подій, а сам текст забезпечений веселими картинками. Проте це дуже конкретний документ, саме такий, яким і має бути стандарт. Вас здивує його якість, він насправді дуже якісно написаний, рясніє прикладами та рекомендаціями.

Також в ньому визначено і нашу подію mouseDown, зокрема, як вона діє за умовчанням:

“У багатьох реалізаціях подія мусила вживатися, щоб ініціювати ряд контекстно-залежних дій, що виконуються за умовчанням. Такі дії можна запобігти, якщо ця подія скасована. До таких дій, що виконуються за замовчуванням, можуть входити: початок перетягування, де об’єкт, що перетягується, – це зображення або посилання; початок виділення тексту, т. д. Крім того, в деяких реалізаціях передбачається можливість панорамування за допомогою миші, що активується, коли середня кнопка миші втоплена в момент диспетчеризації події, що минув.

Добре, для нашої події передбачені деякі дії за умовчанням, але в самій події фокусу немає нічого специфічного, оскільки фокус дійсно залежить від браузера. Давайте познайомимося з ними.

Знайомство з браузерними двигунами

Сучасний браузер – це дуже складний зразок софту з базою коду приблизно десятки мільйонів рядків . Тому браузер зазвичай ділиться на кілька частин.
Щоб визначити, де саме визначаються події фокусу, потрібно зробити огляд, який би дозволив зрозуміти, за що відповідає кожна частина. Почнемо з Chromium і документації щодо його проектування Getting Around The Chrome Source Code . Як бачите, тут безліч модулів і логіка, за яку відповідають модулі, у всіх модулів різна.

Узагальнений огляд Chromium

Давайте коротко розберемо їх усе, щоб зрозуміти, як взаємодіють усі ці компоненти.

  • chrome: це базова програма з логікою запуску, інтерфейсом користувача і всіма вікнами. Він містить проекти для chrome.exe та chrome.dll. Тут ви також знайдете ресурси, наприклад, іконки чи курсори.
  • content: це серверна частина програми, що обробляє комунікацію з дочірніми процесами.
  • net: це мережна бібліотека, яка допомагає виконувати запити на веб-сайти.
  • base: це місце для загального коду, що поділяється між усіма субпроектами. Сюди можуть бути включені такі речі, як операції над рядками, узагальнені утиліти, тощо.
  • blink: це двигун рендерингу, що відповідає за весь конвеєр відображення, у тому числі, за дерева DOM, стилі, події, інтеграцію з V8.
  • v8: остання більшість браузерного движка на JavaScript. Завдання цього компонента – компілювати JavaScript у нативний машинний код.

Як бачите, браузер складається з кількох незалежних частин, які спілкуються один з одним через API. З погляду розробника зазвичай найцікавіші Blink та V8. Дії за замовчуванням, що визначаються браузером, не входять до складу V8, але у Blink всі вони мають бути визначені та реалізовані. Але перш ніж переходити до бази коду Blink, давайте розберемося, як веб-браузери працюють з точки зору користувача.

Конвеєр візуалізації

Уявіть, що ви вводите в браузер адресу домену, а потім браузер вибирає та завантажує набір ресурсів: HTML, CSS, файли JS, зображення, ярлики. Але що має статися далі?

Браузерний конвеєр рендерингу

На першому кроці HTML-файли проходитимуть синтаксичний розбір і перетворюватимуться на дерево DOM . DOM – це не тільки внутрішнє уявлення сторінки, але й API, що відкривається JavaScript для виконання запитів і модифікації рендерингу за допомогою системи так званих «прив’язок».

Наступний крок після побудови дерева DOM – це обробка стилів CSS. Для цієї мети в браузерах є інструмент аналізу CSS, що збирає модель стильових правил. Побудувавши модель стильових правил, можна об’єднати їх із набором стилів, що задаються за замовчуванням (що надаються браузером) і обчислити остаточне значення кожної стильової властивості для кожного елемента DOM. Цей процес називається роздільною здатністю (або перерахунком) стилів .

У наступній частині описує макет , потрібно визначити візуальну геометрію для всіх елементів. На даному етапі кожен елемент отримує координати (x та y), ширину та висоту. Двигун компонування обчислює всі зони виходу за межі та веде їх облік – яка частина елемента буде видимою, а яка ні.

Коли у нас будуть всі координати для всіх елементів, настане час для відображення . Для цієї операції ми скористаємося координатами з попереднього кроку та кольорами із стильових правил, після чого скомбінуємо на їх основі список інструкцій з малювання. Важливо малювати елементи у правильному порядку, щоб, накладаючись одне одного, вони зберігали правильну «поверховість». Цей порядок можна змінити за допомогою стильового правила z-index.

Давайте виконаємо наші інструкції для відображення за списком і перетворимо їх на растр колірних значень. Цей етап називається розтеризацією . Саме зараз ми візьмемо наші зображення та декодуємо їх у карту бітів.

Пізніше розтеризована карта бітів зберігатиметься у пам’яті GPU. На даному етапі в роботу включаються бібліотеки, що абстрагують апаратне забезпечення та виклики до OpenGL і DirectX під Windows. Коли GPU отримує команди для відображення растрового малюнку, на дисплеї відображаються відповідні пікселі.

Ось у нас є всі найважливіші частини рендерингового конвеєра. Але що станеться, якщо прокрутити сторінку або застосувати на ній деяку анімацію? Насправді рендеринг не статичний. Для представлення змін використовуються анімаційні кадри. Кожен кадр повністю відображає стан контенту у конкретний час. Найскладніше при організації цього процесу – досягти потрібної продуктивності. Щоб анімація йшла гладко, необхідно генерувати щонайменше 60 кадрів на секунду. Провернути весь конвеєр 60 разів на секунду буде практично неможливо, особливо на повільних пристроях.

А що, якщо ми не будемо щоразу рендерити все заново, а надамо спосіб інвалідувати елемент на конкретному етапі. Скажімо, якщо ви динамічно змінюєте колір кнопки, то браузер помітить її вузол як інвалідний, і в наступному кадрі анімації її буде відображено заново. Якщо нічого не зміниться, ми зможемо повторно використовувати старий кадр.

Цей спосіб зручний для оптимізації невеликих динамічних змін вмісту. Давайте подумаємо, що робити, якщо потрібно змінювати великі області. Наприклад, після повного прокручування сторінки всі пікселі на новому екрані будуть іншими. Тому сторінка декомпонується на рівні, кожен із яких раструється незалежно. Рівень може бути дуже малий і представляти лише один вузол a DOM. Потім всі ці рівні комбінуватимуться в іншому потоці, який називається потік композитора . За такої оптимізації не доводиться повторно раструвати все відразу, достатньо виконувати ці операції для невеликих шарів, а потім правильно компонувати їх один з одним.

Отже, ми коротко розглянули, що робить Blink і як виглядає конвеєр рендерингу. Давайте заглибимося в код.

Навігація по базі коду Blink

Здається, ми наближаємося до фінішної прямої. Давайте відкриємо репозиторій Blink і розглянемо його.

Кореневий каталог репозиторію Blink

Швидко стає зрозуміло, що, хоча ми змогли значно конкретизувати наше вихідне питання, масив коду поки що все одно занадто великий, щоб вручну знайти в ньому конкретний рядок, що відповідає за запобігання фокусу.

Давайте спробуємо пошукати в Google на ім’я події:

mousedown site:https://chromium.googlesource.com/chromium/blink/+/master/Source

Приводить нас до файлу EventHandler , де можна знайти деталі реалізації для багатьох вхідних подій. У тому числі, той рядок, який нас найбільше цікавить.

bool swallowEvent = !dispatchMouseEvent(EventTypeNames::mousedown, mev.innerNode(), m_clickCount, mouseEvent);

Значення , що повертається dispatchMouseEventозначає «продовжувати обробляти так, як задано за замовчуванням», тому, у разі використання preventDefaultswallowEventдорівнює true.

Трохи нижче розташований виклик події фокусування, який спрацьовує лише у випадку, якщо swallowEvent == false.

swallowEvent = swallowEvent || handleMouseFocus(MouseEventWithHitTestResults(mouseEvent, hitTestResult), sourceCapabilities);

Можете дослідити не тільки подію фокусування, але і всі дії, які задаються за умовчанням для події mouse down, у тому числі виділення, перетягування і роботу з повзунком. Також тут реалізуються подія відпускання кнопки миші та подія подвійного клацання.

Gecko і WebKit

Діставшись сюди, ми вже встигли чимало покопатися у вихідному коді браузерів і дуже непогано уявляємо собі їхню структуру. Так чому б не перевірити Firefox чи взагалі Safari. Двигун браузера Firefox називається Gecko, а двигун Safari – WebKit.

У Gecko також передбачена оглядова сторінка для розробників, тому легко усвідомити основні концепції цього движка. Спираючись на досвід роботи з Chrome, ви знайдете тут акуратний файл EventStateManager на 6000 рядків коду, в якому для подій задаються дії та стандартні поведінки.

WebKit– Це браузерний двигун від Apple, що використовується в Safari та інших продуктах Apple. Blink від Chrome є форком WebKit, тому у них багато спільного, і мені не важко знайти реалізації подій в їх версії файлу EventHandler .

Тепер, переконавшись, що можна безпечно придушити подію, що мусила встановити, я можу повернутися до початку цієї посади і спокійно доопрацювати інструмент для вибору дати.

Заключення

Разом ми пройшли шлях після простої проблеми до введення в веб-стандарти і розбору деталей, що стосуються реалізації браузерів.

Нехай вас не лякає прихована складність наявних модулів, що навіть стосуються браузера або компілятора. На Вас чекає захоплююча подорож. Ймовірно, ви легко знайдете, що тут можна було б покращити, і, що набагато важливіше, на власному досвіді набудете унікальних знань про те, як саме влаштовано. Особисто я перелопатив гору документації, працюючи над цим дослідженням, і рекомендую всім діяти так само. У всіх браузерах надається чудова документація, тому я справді не впевнений, а чи потрібно для роботи ще щось, крім неї.

Переклад статті “What are Web Standards and how does Web Browser work?