Малко хора не харесват рамки, но дори и да сте един от тях, трябва да вземете под внимание и да приемете функциите, които малко улесняват живота.
Бях против използването на рамки в миналото . Напоследък обаче имам опит да работя с Реагирайте и Ъглова в някои от моите проекти. Първите няколко пъти, когато отворих редактора на кода си и започнах да пиша код в Angular, се почувствах странно и неестествено; особено след повече от десет години кодиране, без да се използват никакви рамки. След известно време реших да се ангажирам с изучаването на тези технологии. Много бързо стана очевидна една голяма разлика: беше толкова лесно да се манипулира DOM, толкова лесно да се регулира редът на възлите, когато е необходимо, и не бяха необходими страници и страници с код за изграждане на потребителски интерфейс.
Въпреки че все още предпочитам свободата да не съм прикачен към рамка или архитектура, не бих могъл да пренебрегна факта, че създаването на DOM елементи в един е много по-удобно. Затова започнах да търся начини да подражавам на преживяването във ванилия JS. Целта ми е да извлека някои от тези идеи от React и да покажа как същите принципи могат да бъдат приложени в обикновен JavaScript (често наричан ванилия JS), за да улеснят малко живота на разработчика. За да постигнем това, нека създадем просто приложение за разглеждане на проекти на GitHub.
По който и начин да изграждаме преден край, използвайки JavaScript, ще имаме достъп и ще манипулираме DOM. За нашето приложение ще трябва да изградим представяне на всяко хранилище (миниатюра, име и описание) и да го добавим към DOM като елемент от списъка. Ние ще използваме API за търсене на GitHub за да вземем нашите резултати. И тъй като говорим за JavaScript, нека да търсим в хранилищата на JavaScript. Когато запитваме ПОЖАР , получаваме следния JSON отговор:
{ 'total_count': 398819, 'incomplete_results': false, 'items': [ { 'id': 28457823, 'name': 'freeCodeCamp', 'full_name': 'freeCodeCamp/freeCodeCamp', 'owner': { 'login': 'freeCodeCamp', 'id': 9892522, 'avatar_url': 'https://avatars0.githubusercontent.com/u/9892522?v=4', 'gravatar_id': '', 'url': 'https://api.github.com/users/freeCodeCamp', 'site_admin': false }, 'private': false, 'html_url': 'https://github.com/freeCodeCamp/freeCodeCamp', 'description': 'The https://freeCodeCamp.org open source codebase '+ 'and curriculum. Learn to code and help nonprofits.', // more omitted information }, //... ] }
React улеснява писането на HTML елементи в страницата и е една от функциите, които винаги съм искал да имам, докато пиша компоненти в чист JavaScript. React използва JSX , което е много подобно на обикновения HTML.
Браузърът обаче не чете това.
Под капака React преобразува JSX в куп повиквания към React.createElement
функция. Нека да разгледаме пример за JSX, използващ един елемент от API на GitHub, и да видим на какво се превежда.
{item.name} {item.description}
;
; React.createElement( 'div', { className: 'repository' }, React.createElement( 'div', null, item.name ), React.createElement( 'p', null, item.description ), React.createElement( 'img', { src: item.owner.avatar_url } ) );
JSX е много проста. Пишете обикновен HTML код и инжектирате данни от обекта, като добавяте къдрави скоби. JavaScript кодът в скобите ще бъде изпълнен и стойността ще бъде вмъкната в получения DOM. Едно от предимствата на JSX е, че React създава виртуален DOM (виртуално представяне на страницата) за проследяване на промени и актуализации. Вместо да пренаписва целия HTML, React променя DOM на страницата, когато информацията се актуализира. Това е един от основните проблеми, които React е създаден за решаване.
Разработчиците използваха много jQuery. Бих искал да го спомена тук, защото все още е популярен, а също и защото е доста близо до решението в чист JavaScript. jQuery получава препратка към DOM възел (или колекция от DOM възли) чрез заявка към DOM. Той също така обхваща тази препратка с различни функционалности за промяна на нейното съдържание.
Докато jQuery има свои собствени инструменти за конструиране на DOM, нещото, което най-често виждам в дивото е просто HTML конкатенация. Например, можем да вмъкнем HTML код в избраните възли, като извикаме html()
функция. Според Документация за jQuery , ако искаме да променим съдържанието на div
възел с класа demo-container
можем да го направим така:
$( 'div.demo-container' ).html( 'All new content. You bet!
' );
Този подход улеснява създаването на DOM елементи; обаче, когато трябва да актуализираме възлите, трябва да потърсим нужните ни възли или (по-често) да се върнем към пресъздаване на целия фрагмент, когато се изисква актуализация.
JavaScript в браузърите има вграден DOM API което ни дава директен достъп до създаване, модифициране и изтриване на възлите в дадена страница. Това е отразено в подхода на React и чрез използването на DOM API ние се приближаваме една стъпка по-близо до предимствата на този подход. Ние променяме само елементите на страницата, които действително изискват да бъдат променени. React обаче следи и отделен виртуален DOM. Чрез сравняване на разликите между виртуалния и действителния DOM, React след това може да идентифицира кои части се нуждаят от модификация.
Тези допълнителни стъпки понякога са полезни, но не винаги и манипулирането на DOM директно може да бъде по-ефективно. Можем да създаваме нови DOM възли, използвайки _document.createElement_
функция, която ще върне препратка към създадения възел. Проследяването на тези препратки ни дава лесен начин да модифицираме само възлите, които съдържат частта, която трябва да бъде актуализирана.
Използвайки същата структура и източник на данни като в примера за JSX, можем да конструираме DOM по следния начин:
var item = document.createElement('div'); item.className = 'repository'; var nameNode = document.createElement('div'); nameNode.innerHTML = item.name item.appendChild(nameNode); var description = document.createElement('p'); description.innerHTML = item.description; item.appendChild(description ); var image = new Image(); Image.src = item.owner.avatar_url; item.appendChild(image); document.body.appendChild(item);
Ако единственото нещо, което ви е на ум, е ефективността на изпълнението на кода, тогава този подход е много добър. Ефективността обаче не се измерва само в скоростта на изпълнение, но и в лекотата на поддръжка, мащабируемост и пластичност. Проблемът с този подход е, че той е много подробен и понякога объркан. Трябва да напишем куп извиквания на функции, дори ако просто изграждаме основна структура. Вторият голям недостатък е огромният брой променливи, създадени и проследени. Да приемем, че компонентът, който работите, съдържа собствените си 30 DOM елемента, ще трябва да създадете и използвате 30 различни DOM елемента и променливи. Можете да използвате повторно някои от тях и да направите малко жонглиране за сметка на поддръжката и пластичността, но може да стане наистина объркано, много бързо.
Друг съществен недостатък се дължи на броя редове код, които трябва да напишете. С времето става все по-трудно да се преместят елементи от един родител на друг. Това е едно нещо, което наистина оценявам от React. Мога да преглеждам синтаксиса на JSX и да получа след няколко секунди кой възел се съдържа, къде и да променя, ако е необходимо. И макар да изглежда, че в началото не е голяма работа, повечето проекти имат постоянни промени, които ще ви накарат да търсите по-добър начин.
Работата директно с DOM работи и свършва работата, но също така прави конструкцията на страницата много подробна, особено когато трябва да добавим HTML атрибути и гнездови възли. Идеята би била да уловим някои от предимствата на работата с технологии като JSX и да улесним живота си. Предимствата, които се опитваме да повторим, са следните:
Ето една проста функция, която би постигнала това с помощта на HTML фрагмент.
Browser.DOM = function (html, scope) { // Creates empty node and injects html string using .innerHTML // in case the variable isn't a string we assume is already a node var node; if (html.constructor === String) { var node = document.createElement('div'); node.innerHTML = html; } else { node = html; } // Creates of uses and object to which we will create variables // that will point to the created nodes var _scope = scope || {}; // Recursive function that will read every node and when a node // contains the var attribute add a reference in the scope object function toScope(node, scope) { var children = node.children; for (var iChild = 0; iChild 0) { var _property = names.shift(); if (names.length == 0) { obj[_property] = children[iChild]; } else { if (!obj.hasOwnProperty(_property)){ obj[_property] = {}; } obj = obj[_property]; } } } toScope(children[iChild], scope); } } toScope(node, _scope); if (html.constructor != String) { return html; } // If the node in the highest hierarchy is one return it if (node.childNodes.length == 1) { // if a scope to add node variables is not set // attach the object we created into the highest hierarchy node // by adding the nodes property. if (!scope) { node.childNodes[0].nodes = _scope; } return node.childNodes[0]; } // if the node in highest hierarchy is more than one return a fragment var fragment = document.createDocumentFragment(); var children = node.childNodes; // add notes into DocumentFragment while (children.length > 0) { if (fragment.append){ fragment.append(children[0]); }else{ fragment.appendChild(children[0]); } } fragment.nodes = _scope; return fragment; }
Идеята е проста, но мощна; изпращаме функцията HTML, който искаме да създадем като низ, в низа HTML добавяме атрибут var към възлите, които искаме да имаме създадени референции за нас. Вторият параметър е обект, в който ще се съхраняват тези препратки. Ако не е посочено, ще създадем свойство “възли” на върнатия възел или фрагмент на документ (в случай, че възелът с най-висока йерархия е повече от един). Всичко се постига с по-малко от 60 реда код.
Функцията работи в три стъпки:
И така, как изглежда нашият код за изобразяване на примера сега?
var UI = {}; var template = ''; template += ' ' template += ' '; template += ' ' template += '
' template += ' '; var item = Browser.DOM(template, UI); UI.name.innerHTML = data.name; UI.text.innerHTML = data.description; UI.image.src = data.owner.avatar_url;
Първо, дефинираме обекта (UI), където ще съхраняваме препратките към създадените възли. След това съставяме HTML шаблона, който ще използваме, като низ, маркирайки целевите възли с атрибут “var”. След това извикваме функцията Browser.DOM с шаблона и празния обект, който ще съхранява препратките. И накрая, използваме съхранените препратки, за да поставим данните във възлите.
Този подход също така разделя изграждането на структурата на DOM и вмъкването на данните в отделни стъпки, което помага за поддържането на кода организиран и добре структуриран. Това ни позволява да създаваме отделно структурата на DOM и да попълваме (или актуализираме) данните, когато те станат достъпни.
Въпреки че някои от нас не харесват идеята за преминаване към рамки и предаване на контрол, важно е да признаем ползите, които тези рамки носят. Има причина да са толкова популярни.
И докато рамката може да не отговаря винаги на вашия стил или нужди, някои функционалности и техники могат да бъдат възприети, емулирани или понякога дори отделени от рамка. Някои неща винаги ще бъдат загубени при превода, но много могат да бъдат спечелени и използвани при малка част от разходите, които една рамка носи.
JSX е React компонент, който опростява синтаксиса и процеса на създаване на HTML шаблони и DOM елементи. JSX е написан като HTML вграден в изходния код, но се транслитерира в инструкции за JavaScript за DOM конструиране, като по този начин получава най-доброто от двата свята.
Виртуалният DOM е представяне на React на DOM. Сравнявайки го със DOM на страницата, е възможно да се проследяват промените и да се модифицират само частите на страницата, които действително трябва да бъдат модифицирани, вместо да се поставят в опашка големи части от страницата за синтактичен анализ и повторно изобразяване.